Android Fragment बैक स्टैक के साथ समस्या


121

जिस तरह से एंड्रॉइड टुकड़ा बैकस्टैक काम करने लगता है उसके साथ मुझे एक बड़ी समस्या हो गई है और जो भी मदद की पेशकश की जाती है उसके लिए सबसे आभारी होंगे।

कल्पना कीजिए कि आपके पास 3 टुकड़े हैं

[1] [2] [3]

मैं चाहता हूं कि उपयोगकर्ता नेविगेट करने में सक्षम हो [1] > [2] > [3]लेकिन रास्ते में वापस (बैक बटन दबाकर) [3] > [1]

जैसा कि मैंने कल्पना की थी कि यह उस समय तक पूरा नहीं होगा addToBackStack(..)जब खंडित होने वाले लेनदेन का निर्माण किया जाएगा[2] XML में परिभाषित अंश धारक में लाने वाले ।

इस की वास्तविकता के रूप में लगता है कि अगर मैं [2]फिर से प्रकट नहीं करना चाहता हूं जब उपयोगकर्ता वापस बटन दबाता है [3], तो मुझे addToBackStackलेन-देन में कॉल नहीं करना चाहिए: यह टुकड़ा दिखाता है[3] । यह पूरी तरह से काउंटर-सहज ज्ञान युक्त है (शायद आईओएस दुनिया से आता है)।

वैसे भी अगर मैं इसे इस तरह से करता हूं, जब मैं से [1] > [2]जाता हूं और वापस दबाता हूं तो मैं वापस आ जाता हूं[1] उम्मीद के ।

अगर मैं जाता हूं [1] > [2] > [3]और फिर वापस दबाता हूं तो मैं वापस कूद जाता हूं [1](उम्मीद के मुताबिक)। अब अजीब व्यवहार तब होता है जब मैं कोशिश करता हूं और [2]फिर से कूद जाता हूं [1]। देखने में आने [3]से पहले सबसे पहले इसे संक्षेप में प्रदर्शित किया [2]जाता है। यदि मैं इस बिंदु पर वापस प्रेस करता हूं [3], और यदि मैं एक बार फिर से वापस आता हूं तो ऐप बाहर निकल जाता है।

क्या कोई मुझे समझने में मदद कर सकता है कि यहाँ क्या चल रहा है?


और यहाँ मेरी मुख्य गतिविधि के लिए लेआउट xml फ़ाइल है:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          android:layout_width="fill_parent"
          android:layout_height="fill_parent"
          android:orientation="vertical" >

<fragment
        android:id="@+id/headerFragment"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        class="com.fragment_test.FragmentControls" >
    <!-- Preview: layout=@layout/details -->
</fragment>
<FrameLayout
        android:id="@+id/detailFragment"
        android:layout_width="match_parent"
        android:layout_height="fill_parent"

        />



अपडेट यह वह कोड है जिसका उपयोग मैं नव उत्तराधिकारियों द्वारा बनाने के लिए कर रहा हूं

    Fragment frag;
    FragmentTransaction transaction;


    //Create The first fragment [1], add it to the view, BUT Dont add the transaction to the backstack
    frag = new Fragment1();

    transaction = getSupportFragmentManager().beginTransaction();
    transaction.replace(R.id.detailFragment, frag);
    transaction.commit();

    //Create the second [2] fragment, add it to the view and add the transaction that replaces the first fragment to the backstack
    frag = new Fragment2();

    transaction = getSupportFragmentManager().beginTransaction();
    transaction.replace(R.id.detailFragment, frag);
    transaction.addToBackStack(null);
    transaction.commit();


    //Create third fragment, Dont add this transaction to the backstack, because we dont want to go back to [2] 
    frag = new Fragment3();
    transaction = getSupportFragmentManager().beginTransaction();
    transaction.replace(R.id.detailFragment, frag);
    transaction.commit();


     //END OF SETUP CODE-------------------------
    //NOW:
    //Press back once and then issue the following code:
    frag = new Fragment2();
    transaction = getSupportFragmentManager().beginTransaction();
    transaction.replace(R.id.detailFragment, frag);
    transaction.addToBackStack(null);
    transaction.commit();

    //Now press back again and you end up at fragment [3] not [1]

बहुत धन्यवाद


लेकिन टुकड़ा ओवरलैप हो रहा है, जब मैं फ्रैगमेंट सी से टुकड़े ए पर बैकपैक करता हूं
प्रियंका

मेरे पास एक ही मुद्दा है, आप इसे कैसे ठीक करते हैं?
प्रियंका

मेरे ans का संदर्भ लें .. इसकी मदद से आप < stackoverflow.com/questions/14971780/… >
एमडी खली

जवाबों:


203

स्पष्टीकरण: यहाँ क्या चल रहा है?

यदि हम ध्यान में रखते हैं कि हम प्रलेखन द्वारा जानते हैं कि .replace()बराबर है .remove().add():

एक मौजूदा टुकड़े को बदलें जो एक कंटेनर में जोड़ा गया था। यह अनिवार्य रूप remove(Fragment)से सभी वर्तमान में जोड़े गए टुकड़ों के लिए कॉलिंग के समान है जो कि उसी के साथ containerViewIdऔर फिर add(int, Fragment, String)यहां दिए गए समान तर्कों के साथ जोड़े गए।

फिर जो हो रहा है, वह ऐसा है (मैं इसे और अधिक स्पष्ट करने के लिए संख्याओं को जोड़ रहा हूँ):

// transaction.replace(R.id.detailFragment, frag1);
Transaction.remove(null).add(frag1)  // frag1 on view

// transaction.replace(R.id.detailFragment, frag2).addToBackStack(null);
Transaction.remove(frag1).add(frag2).addToBackStack(null)  // frag2 on view

// transaction.replace(R.id.detailFragment, frag3);
Transaction.remove(frag2).add(frag3)  // frag3 on view

(यहाँ सभी भ्रामक चीजें होने लगती हैं)

याद रखें कि .addToBackStack()केवल लेन-देन ही बचत कर रहा है न कि खुद के रूप में टुकड़ा ! तो अब हमारे पास frag3लेआउट है:

< press back button >
// System pops the back stack and find the following saved back entry to be reversed:
// [Transaction.remove(frag1).add(frag2)]
// so the system makes that transaction backward!!!
// tries to remove frag2 (is not there, so it ignores) and re-add(frag1)
// make notice that system doesn't realise that there's a frag3 and does nothing with it
// so it still there attached to view
Transaction.remove(null).add(frag1) //frag1, frag3 on view (OVERLAPPING)

// transaction.replace(R.id.detailFragment, frag2).addToBackStack(null);
Transaction.remove(frag3).add(frag2).addToBackStack(null)  //frag2 on view

< press back button >
// system makes saved transaction backward
Transaction.remove(frag2).add(frag3) //frag3 on view

< press back button >
// no more entries in BackStack
< app exits >

संभावित समाधान

FragmentManager.BackStackChangedListenerबैक स्टैक में परिवर्तन देखने के लिए लागू करने पर विचार करें और अपने तर्क को onBackStackChanged()मेथोड में लागू करें :


अच्छी व्याख्या @arvis। हालाँकि, हम DexterMoon की तरह हैकिंग के तरीकों का सहारा लिए बिना इस व्यवहार को कैसे रोक सकते हैं, या Nemanja से popBackStack का उपयोग करके जो संक्रमण एनीमेशन खेलते समय फ्रैगमेंट दिखाता है?
मोमो

@ मैं आपको FragmentManager.BackStackChangedListenerबैक स्टैक में बदलाव के लिए देखने के लिए लागू कर सकता हूं । onBackStackChanged()मिथोड के साथ अपने सभी लेनदेन की निगरानी करें और आवश्यक कार्य करें: पूर्व के लिए। बैकस्टैक में लेनदेन की एक संख्या का पता लगाएं; नाम ( FragmentTransaction addToBackStack (String name)) आदि द्वारा विशेष लेन-देन की जांच करें
अरविस

जवाब के लिए धन्यवाद। मैंने वास्तव में कोशिश की कि आज पहले, श्रोता को पंजीकृत किया और onBackstackChange से टुकड़ा हटा दिया। जबकि पॉपिंग संक्रमण ने खेला टुकड़ा एक सफेद खाली क्षेत्र बन जाता है। मुझे लगता है कि इस विधि को निकाल दिया जाता है जब पॉपिंग एनीमेशन शुरू करता है और समाप्त होने पर नहीं ...
मोमो

4
वापस ढेर का उपयोग करने से बचें! यह वास्तव में समग्र दक्षता के साथ मदद नहीं करता है! हर बार जब आप नेविगेट करना चाहते हैं तो प्लेन रिप्लेसमेंट () या इससे भी बेहतर रिमूव / ऐड का उपयोग करें!
stack_ved

@Arvis u pls मदद कर सकता हूँ मुझे एक ही मुद्दा हो रहा है .... स्टैक गिनती 0 लेकिन फिर भी मेरा टुकड़ा दिखाई दे रहा है?
एरुम

33

सही!!! बहुत बालों को खींचने के बाद, मैंने आखिरकार यह काम करने के लिए कैसे ठीक से काम किया है।

ऐसा लगता है जैसे कि टुकड़ा [3] को उस दृश्य से हटाया नहीं जाता है जब वापस दबाया जाता है, इसलिए आपको इसे मैन्युअल रूप से करना होगा!

सबसे पहले, प्रतिस्थापन का उपयोग न करें (), लेकिन इसके बजाय निकालें और अलग से उपयोग करें। ऐसा लगता है जैसे प्रतिस्थापित () ठीक से काम नहीं करता है।

इसका अगला भाग onKeyDown मेथड को ओवरराइड कर रहा है और जब भी बैक बटन दबाया जाता है तो हर बार करंट के टुकड़े को हटाता है।

@Override
public boolean onKeyDown(int keyCode, KeyEvent event)
{
    if (keyCode == KeyEvent.KEYCODE_BACK)
    {
        if (getSupportFragmentManager().getBackStackEntryCount() == 0)
        {
            this.finish();
            return false;
        }
        else
        {
            getSupportFragmentManager().popBackStack();
            removeCurrentFragment();

            return false;
        }



    }

    return super.onKeyDown(keyCode, event);
}


public void removeCurrentFragment()
{
    FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();

    Fragment currentFrag =  getSupportFragmentManager().findFragmentById(R.id.detailFragment);


    String fragName = "NONE";

    if (currentFrag!=null)
        fragName = currentFrag.getClass().getSimpleName();


    if (currentFrag != null)
        transaction.remove(currentFrag);

    transaction.commit();

}

उम्मीद है की यह मदद करेगा!


यह काम करता है, लेकिन यह मेरे प्रोजेक्ट में उपयोग नहीं किया जा सकता है क्योंकि टुकड़ा फिर से लोड हो जाता है! कोई सुझाव?
थारकनिरामन

लेकिन, अगर मैं ऐसा कर रहा हूं। जब मैं फ्रोजनमेंट सी के लिए C से A में वापस आ रहा हूं तो मुझे खाली स्क्रीन मिल रही है
निगम पेट्रो

मुझे संदेह है कि @Arvis के जवाब को आपकी समस्या पर कुछ प्रकाश डालना चाहिए
क्रिस बर्च

16

सबसे पहले आंख खोलने के स्पष्टीकरण के लिए @Arvis का धन्यवाद।

मैं इस समस्या के लिए यहां स्वीकृत उत्तर के विभिन्न समाधानों को प्राथमिकता देता हूं। मैं किसी भी तरह से पूरी तरह से जरूरत से ज्यादा पीछे व्यवहार के साथ खिलवाड़ करना पसंद नहीं करता हूं और जब मैंने डिफ़ॉल्ट बैक स्टैक पॉपिंग के बिना अपने दम पर टुकड़ों को जोड़ने और हटाने की कोशिश की है, जब वापस बटन दबाया जाता है तो मैंने अपने आप को टुकड़ा नरक में पाया :) यदि आप। जब आप इसे हटाते हैं तो f1 पर f2 जोड़ें। onResume, onStart आदि जैसे कॉलबैक तरीकों में से कोई भी कॉल नहीं करेगा और यह बहुत दुर्भाग्यपूर्ण हो सकता है।

किसी भी तरह मैं यह कर रहा हूँ:

वर्तमान में प्रदर्शन पर केवल खंड 1 है।

f1 -> f2

Fragment2 f2 = new Fragment2();
this.getActivity().getSupportFragmentManager().beginTransaction().replace(R.id.main_content,f2).addToBackStack(null).commit();

यहाँ से बाहर कुछ भी नहीं है। खंड 2 में से यह कोड आपको खंड 3 तक ले जाता है।

f2 -> f3

Fragment3 f3 = new Fragment3();
getActivity().getSupportFragmentManager().popBackStack();
getActivity().getSupportFragmentManager().beginTransaction().replace(R.id.main_content, f3).addToBackStack(null).commit();

मुझे यकीन नहीं है कि डॉक्स पढ़ने से अगर यह काम करना चाहिए, तो यह पॉपिंग ट्रांजेक्शन विधि को एसिंक्रोनस कहा जाता है, और हो सकता है कि एक बेहतर तरीका popBackStackImmediate () हो। लेकिन जहां तक ​​मैं अपने उपकरणों पर बता सकता हूं कि यह त्रुटिपूर्ण रूप से काम कर रहा है।

कहा गया विकल्प होगा:

final FragmentActivity activity = getActivity();
activity.getSupportFragmentManager().popBackStackImmediate();
activity.getSupportFragmentManager().beginTransaction().replace(R.id.main_content, f3).addToBackStack(null).commit();

यहाँ वास्तव में f3 पर वापस जाने के लिए संक्षिप्त रूप से f1 beofre पर वापस जाना होगा, इसलिए वहां थोड़ी गड़बड़ है।

यह वास्तव में आप सभी को करना है, स्टैक व्यवहार को वापस करने की कोई आवश्यकता नहीं है ...


6
लेकिन गड़बड़, यह एक समस्या है
Zyoo

यह बैकस्टैक परिवर्तनों के लिए सुनने की तुलना में "हैक-आई" कम लगता है। धन्यवाद।
विलाप करते हुए

13

मुझे पता है कि यह एक पुरानी विचित्रता है, लेकिन मुझे वही समस्या मिली और इसे इस तरह से ठीक करना चाहिए:

सबसे पहले, Fragment1 को एक नाम के साथ BackStack में जोड़ें (उदाहरण के लिए "Frag1"):

frag = new Fragment1();

transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.detailFragment, frag);
transaction.addToBackStack("Frag1");
transaction.commit();

और फिर, जब भी आप Fragment1 पर वापस जाना चाहते हैं (इसके ऊपर 10 टुकड़े जोड़ने के बाद भी), तो बस नाम के साथ popBackStackImmediate को कॉल करें:

getSupportFragmentManager().popBackStackImmediate("Frag1", 0);

आशा है कि यह किसी की मदद करेगा :)


1
बहुत बढ़िया जवाब। मुझे getSupportFragmentManager () का उपयोग करना पड़ा। popBackStackImmediate ("Frag1", FragmentManager.POP_BACK_STACK_INCLUSIVE); हालांकि यह मेरे उपयोग के मामले के लिए काम करने के लिए
एलिसाबेगले

1
मुझे यह कोड कहाँ रखना है ???? getSupportFragmentManager ()। popBackStackImmediate ("Frag1", 0); पर MainActivty या onBackPress
पावेल

यह काम नहीं कर रहा है। :( यह मेरा कोड है, मेरे मामले में फ्रैगमेंट ओवरलैपिंग है। यह फ्रैगमेंट ए को खोलता है, लेकिन फ्रैगमेंट ए को फ्रैगमेंट बी पर ओवरलैप किया गया है।
प्रियंका

FragmentManager fragmentManager = getActivity ()। GetSupportFragmentManager (); fragmentManager.popBackStack (FragmentA.class.getName (), FragmentManager.POP_BACK_STACK_INCLUSIVE); टुकड़े-टुकड़े का टुकड़ा FragmentE टुकड़ा = नया FragmentE (); fragmentTransaction.replace (R.id.fragment_content, fragmentE, fragmentE.getClass ()। getName ()); fragmentTransaction.commit ();
प्रियंका

5

@Arvis के उत्तर के बाद मैंने और भी गहरी खुदाई करने का निर्णय लिया और मैंने इस बारे में एक तकनीकी लेख लिखा है: http://www.andreabaccega.com/blog/2015/08/16/how-to-avoid-fragments-overlapping- कारण-टू-backstack-दुःस्वप्न में एंड्रॉयड /

चारों ओर आलसी डेवलपर्स के लिए। मेरे समाधान में हमेशा बैकस्टैक में लेनदेन को जोड़ना और एक अतिरिक्त प्रदर्शन करना शामिल हैFragmentManager.popBackStackImmediate() आवश्यकता पड़ने पर (स्वचालित रूप से) शामिल है।

कोड कोड की बहुत कम पंक्तियाँ हैं और, मेरे उदाहरण में, मैं C से A तक वापस छोड़े बिना "B" में जाना चाहता था यदि उपयोगकर्ता बैकस्टैक (C नाविक से D तक) में गहराई से नहीं गया था।

अत: संलग्न कोड A -> B -> C (पीछे) -> A & A -> B -> C -> D (पीछे) -> C (पीछे) -> B (पीछे) - A के अनुसरण में काम करेगा।

कहाँ पे

fm.beginTransaction().replace(R.id.content, new CFragment()).commit()

प्रश्न में "बी" से "सी" तक जारी किए गए थे।

ठीक है, ठीक है यहाँ कोड है :)

public static void performNoBackStackTransaction(FragmentManager fragmentManager, String tag, Fragment fragment) {
  final int newBackStackLength = fragmentManager.getBackStackEntryCount() +1;

  fragmentManager.beginTransaction()
      .replace(R.id.content, fragment, tag)
      .addToBackStack(tag)
      .commit();

  fragmentManager.addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
    @Override
    public void onBackStackChanged() {
      int nowCount = fragmentManager.getBackStackEntryCount();
      if (newBackStackLength != nowCount) {
        // we don't really care if going back or forward. we already performed the logic here.
        fragmentManager.removeOnBackStackChangedListener(this);

        if ( newBackStackLength > nowCount ) { // user pressed back
          fragmentManager.popBackStackImmediate();
        }
      }
    }
  });
}

1
इस लाइन fragmentManager.popBackStackImmediate () पर दुर्घटना हो रही है; त्रुटि: java.lang.IllegalStateException: FragmentManager पहले से ही com.example.myapplication.FragmentA $ 2.onBackStackChanged (FragmentA.java:43) पर लेनदेन निष्पादित हो रहा है
प्रियंका

1

यदि आप addToBackStack () और popBackStack () के साथ स्ट्रगल कर रहे हैं तो बस उपयोग करें

FragmentTransaction ft =getSupportFragmentManager().beginTransaction();
ft.replace(R.id.content_frame, new HomeFragment(), "Home");
ft.commit();`

अपनी गतिविधि में OnBackPressed () में टैग द्वारा फ़रमान का पता लगाएं और फिर अपना सामान रखें

Fragment home = getSupportFragmentManager().findFragmentByTag("Home");

if (home instanceof HomeFragment && home.isVisible()) {
    // do you stuff
}

अधिक जानकारी के लिए https://github.com/DattaHujare/NavigationDrawer मैं खंड जोड़ने के लिए कभी भी AddToBackStack () का उपयोग नहीं करता।


0

मुझे लगता है, जब मैंने आपकी कहानी पढ़ी है कि [3] भी बैकस्टैक पर है। यह बताता है कि आप इसे चमकती क्यों देखते हैं।

समाधान स्टैक पर [3] सेट करने के लिए कभी नहीं होगा।


हाय jdekei आपके इनपुट के लिए धन्यवाद। समस्या यह है कि मैं यह नहीं देख सकता कि मैं बैकस्टैक में [3] को कहां जोड़ रहा हूं। मैंने कोड का एक और हिस्सा जोड़ा है जो बिल्कुल (नेविगेशन) दिखाता है कि मैं बटन का उपयोग करके प्रदर्शन कर रहा हूं।
क्रिस बर्च

यह मेरी भी मदद करता है, लेकिन मैं आपको कोड से केवल हटाने के लिए उपयोग करता हूं और एक अन्य टुकड़ा जांचकर्ता। प्रतिस्थापित विधि मेरे लिए ठीक काम करती है, हो सकता है, वह s old method issue but now itठीक हो। साभार
विक्टर वी।

0

मेरे पास एक ऐसा ही मुद्दा था जहां मुझे उसी [M1.F0] में 3 लगातार टुकड़े थे Activity-> [M1.F1] -> [M1.F2] जिसके बाद एक नए Activity[M2] को कॉल किया गया । यदि उपयोगकर्ता ने [एम 2] में एक बटन दबाया था, तो मैं [एम 1, एफ 2] के बजाय [एम 1, एफ 1] पर लौटना चाहता था, जो कि पहले से ही प्रेस व्यवहार था।

इसे पूरा करने के लिए मैं [M1, F2] को हटाता हूं, [M1, F1] पर कॉल शो करता हूं, लेन-देन करता हूं, और फिर [M1, F2] को छिपाकर कॉल करता हूं। इसने अतिरिक्त बैक प्रेस को हटा दिया जो अन्यथा पीछे रह जाता।

// Remove [M1.F2] to avoid having an extra entry on back press when returning from M2
final FragmentTransaction ftA = fm.beginTransaction();
ftA.remove(M1F2Fragment);
ftA.show(M1F1Fragment);
ftA.commit();
final FragmentTransaction ftB = fm.beginTransaction();
ftB.hide(M1F2Fragment);
ftB.commit();

नमस्ते इस कोड को करने के बाद: मैं वापस कुंजी दबाने पर Fragment2 का मूल्य नहीं देख पा रहा हूं। मेरा कोड:

FragmentTransaction ft = fm.beginTransaction();
ft.add(R.id.frame, f1);
ft.remove(f1);

ft.add(R.id.frame, f2);
ft.addToBackStack(null);

ft.remove(f2);
ft.add(R.id.frame, f3);

ft.commit();

@Override
    public boolean onKeyDown(int keyCode, KeyEvent event){

        if(keyCode == KeyEvent.KEYCODE_BACK){
            Fragment currentFrag =  getFragmentManager().findFragmentById(R.id.frame);
            FragmentTransaction transaction = getFragmentManager().beginTransaction();

            if(currentFrag != null){
                String name = currentFrag.getClass().getName();
            }
            if(getFragmentManager().getBackStackEntryCount() == 0){
            }
            else{
                getFragmentManager().popBackStack();
                removeCurrentFragment();
            }
       }
    return super.onKeyDown(keyCode, event);
   }

public void removeCurrentFragment()
    {
        FragmentTransaction transaction = getFragmentManager().beginTransaction();
        Fragment currentFrag =  getFragmentManager().findFragmentById(R.id.frame);

        if(currentFrag != null){
            transaction.remove(currentFrag);
        }
        transaction.commit();
    }

0

executePendingTransactions() , commitNow() नहीं काम किया (

Androidx (जेटपैक) में काम किया।

private final FragmentManager fragmentManager = getSupportFragmentManager();

public void removeFragment(FragmentTag tag) {
    Fragment fragmentRemove = fragmentManager.findFragmentByTag(tag.toString());
    if (fragmentRemove != null) {
        fragmentManager.beginTransaction()
                .remove(fragmentRemove)
                .commit();

        // fix by @Ogbe
        fragmentManager.popBackStackImmediate(tag.toString(), 
            FragmentManager.POP_BACK_STACK_INCLUSIVE);
    }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.