Android 4.2: नेस्टेड टुकड़ों के साथ वापस स्टैक व्यवहार


108

एंड्रॉइड 4.2 के साथ, समर्थन लाइब्रेरी को यहां देखे गए नेस्टेड टुकड़ों के लिए समर्थन मिला । मैंने इसके साथ खेला है और बैक स्टैक और getChildFragmentManager () के बारे में एक दिलचस्प व्यवहार / बग पाया । जब getChildFragmentManager () और addToBackStack (स्ट्रिंग नाम) का उपयोग कर रहे हैं, तो बैक बटन दबाकर सिस्टम पिछले स्टैक में बैक स्टैक को नीचे नहीं चलाता है। दूसरी ओर, जब getFragmentManager () और addToBackStack (स्ट्रिंग नाम) का उपयोग करते हैं, तो बैक बटन दबाकर सिस्टम पिछले टुकड़े में वापस आ जाता है।

मेरे लिए, यह व्यवहार अप्रत्याशित है। अपने डिवाइस पर बैक बटन दबाकर, मैं उम्मीद कर रहा हूं कि बैक स्टैक में अंतिम जोड़ा हुआ टुकड़ा पॉपअप हो जाएगा, भले ही टुकड़ा बच्चों के टुकड़े प्रबंधक में बैक स्टैक में जोड़ा गया हो।

क्या यह व्यवहार सही है? क्या यह व्यवहार बग है? क्या इस मुद्दे के लिए कोई काम है?

getChildFragmentManager के साथ नमूना कोड ():

public class FragmentceptionActivity extends FragmentActivity {

@Override
protected void onCreate(Bundle arg0) {
    super.onCreate(arg0);

    final FrameLayout wrapper1 = new FrameLayout(this);
    wrapper1.setLayoutParams(new FrameLayout.LayoutParams(
            FrameLayout.LayoutParams.MATCH_PARENT,
            FrameLayout.LayoutParams.MATCH_PARENT));
    wrapper1.setId(1);

    final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
            FrameLayout.LayoutParams.MATCH_PARENT,
            FrameLayout.LayoutParams.WRAP_CONTENT);
    params.topMargin = 0;

    final TextView text = new TextView(this);
    text.setLayoutParams(params);
    text.setText("fragment 1");
    wrapper1.addView(text);

    setContentView(wrapper1);

    getSupportFragmentManager().beginTransaction().addToBackStack(null)
            .add(1, new Fragment1()).commit();
}

public class Fragment1 extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        final FrameLayout wrapper2 = new FrameLayout(getActivity());
        wrapper2.setLayoutParams(new FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.MATCH_PARENT,
                FrameLayout.LayoutParams.MATCH_PARENT));
        wrapper2.setId(2);

        final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.MATCH_PARENT,
                FrameLayout.LayoutParams.WRAP_CONTENT);
        params.topMargin = 100;

        final TextView text = new TextView(getActivity());
        text.setLayoutParams(params);
        text.setText("fragment 2");
        wrapper2.addView(text);

        return wrapper2;
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        getFragmentManager().beginTransaction().addToBackStack(null)
                .add(2, new Fragment2()).commit();
    }
}

public class Fragment2 extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        final FrameLayout wrapper3 = new FrameLayout(getActivity());
        wrapper3.setLayoutParams(new FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.MATCH_PARENT,
                FrameLayout.LayoutParams.MATCH_PARENT));
        wrapper3.setId(3);

        final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.MATCH_PARENT,
                FrameLayout.LayoutParams.WRAP_CONTENT);
        params.topMargin = 200;

        final TextView text = new TextView(getActivity());
        text.setLayoutParams(params);
        text.setText("fragment 3");
        wrapper3.addView(text);

        return wrapper3;
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        getChildFragmentManager().beginTransaction().addToBackStack(null)
                .add(3, new Fragment3()).commit();
    }
}

public class Fragment3 extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        final FrameLayout wrapper4 = new FrameLayout(getActivity());
        wrapper4.setLayoutParams(new FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.MATCH_PARENT,
                FrameLayout.LayoutParams.MATCH_PARENT));
        wrapper4.setId(4);

        final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.MATCH_PARENT,
                FrameLayout.LayoutParams.WRAP_CONTENT);
        params.topMargin = 300;

        final TextView text = new TextView(getActivity());
        text.setLayoutParams(params);
        text.setText("fragment 4");
        wrapper4.addView(text);

        return wrapper4;
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        getChildFragmentManager().beginTransaction().addToBackStack(null)
                .add(4, new Fragment4()).commit();
    }
}

public class Fragment4 extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        final FrameLayout wrapper5 = new FrameLayout(getActivity());
        wrapper5.setLayoutParams(new FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.MATCH_PARENT,
                FrameLayout.LayoutParams.MATCH_PARENT));
        wrapper5.setId(5);

        final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.MATCH_PARENT,
                FrameLayout.LayoutParams.WRAP_CONTENT);
        params.topMargin = 400;

        final TextView text = new TextView(getActivity());
        text.setLayoutParams(params);
        text.setText("fragment 5");
        wrapper5.addView(text);

        return wrapper5;
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
    }
}

}

getFragmentManager के साथ नमूना कोड ():

public class FragmentceptionActivity extends FragmentActivity {

@Override
protected void onCreate(Bundle arg0) {
    super.onCreate(arg0);

    final FrameLayout wrapper1 = new FrameLayout(this);
    wrapper1.setLayoutParams(new FrameLayout.LayoutParams(
            FrameLayout.LayoutParams.MATCH_PARENT,
            FrameLayout.LayoutParams.MATCH_PARENT));
    wrapper1.setId(1);

    final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
            FrameLayout.LayoutParams.MATCH_PARENT,
            FrameLayout.LayoutParams.WRAP_CONTENT);
    params.topMargin = 0;

    final TextView text = new TextView(this);
    text.setLayoutParams(params);
    text.setText("fragment 1");
    wrapper1.addView(text);

    setContentView(wrapper1);

    getSupportFragmentManager().beginTransaction().addToBackStack(null)
            .add(1, new Fragment1()).commit();
}

public class Fragment1 extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        final FrameLayout wrapper2 = new FrameLayout(getActivity());
        wrapper2.setLayoutParams(new FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.MATCH_PARENT,
                FrameLayout.LayoutParams.MATCH_PARENT));
        wrapper2.setId(2);

        final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.MATCH_PARENT,
                FrameLayout.LayoutParams.WRAP_CONTENT);
        params.topMargin = 100;

        final TextView text = new TextView(getActivity());
        text.setLayoutParams(params);
        text.setText("fragment 2");
        wrapper2.addView(text);

        return wrapper2;
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        getFragmentManager().beginTransaction().addToBackStack(null)
                .add(2, new Fragment2()).commit();
    }
}

public class Fragment2 extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        final FrameLayout wrapper3 = new FrameLayout(getActivity());
        wrapper3.setLayoutParams(new FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.MATCH_PARENT,
                FrameLayout.LayoutParams.MATCH_PARENT));
        wrapper3.setId(3);

        final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.MATCH_PARENT,
                FrameLayout.LayoutParams.WRAP_CONTENT);
        params.topMargin = 200;

        final TextView text = new TextView(getActivity());
        text.setLayoutParams(params);
        text.setText("fragment 3");
        wrapper3.addView(text);

        return wrapper3;
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        getFragmentManager().beginTransaction().addToBackStack(null)
                .add(3, new Fragment3()).commit();
    }
}

public class Fragment3 extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        final FrameLayout wrapper4 = new FrameLayout(getActivity());
        wrapper4.setLayoutParams(new FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.MATCH_PARENT,
                FrameLayout.LayoutParams.MATCH_PARENT));
        wrapper4.setId(4);

        final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.MATCH_PARENT,
                FrameLayout.LayoutParams.WRAP_CONTENT);
        params.topMargin = 300;

        final TextView text = new TextView(getActivity());
        text.setLayoutParams(params);
        text.setText("fragment 4");
        wrapper4.addView(text);

        return wrapper4;
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        getFragmentManager().beginTransaction().addToBackStack(null)
                .add(4, new Fragment4()).commit();
    }
}

public class Fragment4 extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        final FrameLayout wrapper5 = new FrameLayout(getActivity());
        wrapper5.setLayoutParams(new FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.MATCH_PARENT,
                FrameLayout.LayoutParams.MATCH_PARENT));
        wrapper5.setId(5);

        final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.MATCH_PARENT,
                FrameLayout.LayoutParams.WRAP_CONTENT);
        params.topMargin = 400;

        final TextView text = new TextView(getActivity());
        text.setLayoutParams(params);
        text.setText("fragment 5");
        wrapper5.addView(text);

        return wrapper5;
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
    }
}

}

हममम बहुत दिलचस्प है। मुझे नहीं लगता कि मैं उस व्यवहार की उम्मीद करूंगा। यदि यह मामला है, तो ऐसा लगता है कि हम OnBackPressed पर ओवरराइड कर सकते हैं और उस पद्धति में बच्चे के टुकड़े के साथ टुकड़े की एक पकड़ प्राप्त करते हैं, फिर एक पॉप लेनदेन को निष्पादित करने के लिए ChildFragmentManager की पकड़ प्राप्त करें।
मार्को आरएस

@ मार्को, बिल्कुल, लेकिन फिर भी यह एक समाधान है। एपीआई ठीक से काम क्यों नहीं करेगा?
ईगोर

जवाबों:


67

यह समाधान @ उत्तर का बेहतर संस्करण हो सकता है:

@Override
public void onBackPressed() {
    // if there is a fragment and the back stack of this fragment is not empty,
    // then emulate 'onBackPressed' behaviour, because in default, it is not working
    FragmentManager fm = getSupportFragmentManager();
    for (Fragment frag : fm.getFragments()) {
        if (frag.isVisible()) {
            FragmentManager childFm = frag.getChildFragmentManager();
            if (childFm.getBackStackEntryCount() > 0) {
                childFm.popBackStack();
                return;
            }
        }
    }
    super.onBackPressed();
}

फिर, मैंने ऊपर दिए गए @ उत्तर के आधार पर यह समाधान तैयार किया।

जैसा कि @ AZ13 ने कहा, यह समाधान केवल एक स्तर के बाल टुकड़े स्थितियों में संभव है। कई स्तर के टुकड़े के मामले में, काम थोड़ा जटिल हो जाता है, इसलिए मैं सलाह देता हूं कि इस समाधान को केवल मेरे द्वारा कहे गए व्यवहार्य मामले का प्रयास करें। =)

नोट: चूंकि getFragmentsविधि अब एक निजी विधि है, इसलिए यह समाधान काम नहीं करेगा। आप एक लिंक के लिए टिप्पणियों की जांच कर सकते हैं जो इस स्थिति के बारे में समाधान सुझाता है।


2
इस के बजाय it it उत्तर का उपयोग किया जाना चाहिए - यह सही ढंग से कई नेस्टेड टुकड़ों को संभालता है
Hameno

क्या यह वास्तव में काम कर रहा कोड है? यहां तक ​​कि getFragments सार्वजनिक विधि नहीं है? stackoverflow.com/a/42572412/4729203
wonsuc

यह एक बार काम करता है लेकिन अब काम नहीं कर सकता है अगर getFragments अब सार्वजनिक नहीं है।
इमेलारिलिक

1
getFragmentsअब सार्वजनिक है।
ग्रिगोर

यह सही ढंग से काम नहीं करता है अगर आपके पास फ्रागा है -> फ्रैग बी -> पेजर के अंदर फ्रैग सी ...
G24

57

बग जैसा लगता है। यहां देखें: http://code.google.com/p/android/issues/detail?id=40323

एक वर्कअराउंड के लिए मैंने सफलतापूर्वक उपयोग किया है (जैसा कि टिप्पणियों में सुझाया गया है):

    @Override
public void onBackPressed() {

    // If the fragment exists and has some back-stack entry
    if (mActivityDirectFragment != null && mActivityDirectFragment.getChildFragmentManager().getBackStackEntryCount() > 0){
        // Get the fragment fragment manager - and pop the backstack
        mActivityDirectFragment.getChildFragmentManager().popBackStack();
    }
    // Else, nothing in the direct fragment back stack
    else{
        // Let super handle the back press
        super.onBackPressed();          
    }
}

12
दुर्भाग्य से यह एक उचित समाधान नहीं है और सरलतम मामले के लिए सिर्फ एक समाधान है। बस मान लीजिए कि आपके पास एक टुकड़ा है, जिसमें एक और टुकड़ा है, जिसमें एक और टुकड़ा और इसी तरह शामिल है। फिर आपको कॉल को अंतिम टुकड़े तक
फैलाना होगा

@MuhammadBabar नहीं, आप नहीं करेंगे। यदि यह अशक्त है, तो कथन के दूसरे भाग का मूल्यांकन नहीं किया जाएगा क्योंकि यह पहले से ही गलत है। ऑपरेंड है और नहीं | ।
शॉन

25

इस सवाल का असली जवाब Fragment Transaction के फ़ंक्शन में setPrimaryNavigationFragment है।

/**
 * Set a currently active fragment in this FragmentManager as the primary navigation fragment.
 *
 * <p>The primary navigation fragment's
 * {@link Fragment#getChildFragmentManager() child FragmentManager} will be called first
 * to process delegated navigation actions such as {@link FragmentManager#popBackStack()}
 * if no ID or transaction name is provided to pop to. Navigation operations outside of the
 * fragment system may choose to delegate those actions to the primary navigation fragment
 * as returned by {@link FragmentManager#getPrimaryNavigationFragment()}.</p>
 *
 * <p>The fragment provided must currently be added to the FragmentManager to be set as
 * a primary navigation fragment, or previously added as part of this transaction.</p>
 *
 * @param fragment the fragment to set as the primary navigation fragment
 * @return the same FragmentTransaction instance
 */
public abstract FragmentTransaction setPrimaryNavigationFragment(Fragment fragment);

आपको यह फ़ंक्शन प्रारंभिक पैरेंट फ़्रेग्मेंट पर सेट करना होगा जब गतिविधि इसे जोड़ रही हो। मेरी गतिविधि के अंदर एक प्रतिस्थापन कार्य है जो इस तरह दिखता है:

public void replaceFragment(int containerId, BaseFragment fragment, boolean addToBackstack) {
    FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
    fragmentTransaction.setPrimaryNavigationFragment(fragment);
    if (addToBackstack) {
        fragmentTransaction.addToBackStack(fragment.TAG);
    }

    fragmentTransaction.replace(containerId, fragment).commit();
}

यह आपको वैसा ही व्यवहार प्रदान करता है जैसे कि आपका नियमित फ्रैगमेंट बी बैक फ्रैगमेंट ए से वापस क्लिक करता है, सिवाय इसके कि अब यह बच्चे के टुकड़ों पर भी है!


3
शानदार खोज! यह ओपी द्वारा उल्लिखित मुद्दे को बहुत अधिक क्लीनर और कम हैकी तरीके से हल करता है।
जोना

जब तक हैवीहोफग्रैगमेंट के रूप में नेविगेशन लाइब्रेरी का हिस्सा नहीं होता, तब तक दुर्घटनाग्रस्त हो जाता है
दीदी

22

यह समाधान @ismailarilik उत्तर का बेहतर संस्करण हो सकता है:

नेस्टेड फ्रैगमेंट संस्करण

private boolean onBackPressed(FragmentManager fm) {
    if (fm != null) {
        if (fm.getBackStackEntryCount() > 0) {
            fm.popBackStack();
            return true;
        }

        List<Fragment> fragList = fm.getFragments();
        if (fragList != null && fragList.size() > 0) {
            for (Fragment frag : fragList) {
                if (frag == null) {
                    continue;
                }
                if (frag.isVisible()) {
                    if (onBackPressed(frag.getChildFragmentManager())) {
                        return true;
                    }
                }
            }
        }
    }
    return false;
}

@Override
public void onBackPressed() {
    FragmentManager fm = getSupportFragmentManager();
    if (onBackPressed(fm)) {
        return;
    }
    super.onBackPressed();
}

जब घोंसले के टुकड़े के कई स्तर होते हैं, तो यह काम नहीं करता है
स्प्लिन्टर

मेघ आप मुझे घोंसले के टुकड़े की अपनी वास्तुकला दे? मेरे ऐप में यह काम करता है। शायद मुझे कुछ याद आ जाए।
林奕 林奕

9
बैकस्टैक को पॉप करने का क्रम वैसा नहीं है जैसा मैंने इस उदाहरण में बताया है। मेरा मानना ​​है कि आपको सबसे पहले पेड़ में सबसे गहरे घोंसले वाले दिखाई देने वाले टुकड़े को ढूंढना चाहिए और उस पर बैकस्टैक को पॉप करने का प्रयास करना चाहिए, फिर उत्तरोत्तर अन्य दिखाई देने वाले टुकड़ों की तलाश में पेड़ को ऊपर ले जाना चाहिए, जहां आप बैकस्टैक को तब तक पॉप कर सकते हैं जब तक कि आपको एक नहीं मिलता। क्विक फिक्स जो सबसे सरल मामलों को हैंडल करता है, वह ऑनबैकप्रेडेड (फ्रैग्मेंटमैनेजर एफएम) फंक्शन को बदलने के लिए होता है और उस खंड को स्विच करता है, जो बैकपैक को उस टुकड़े से जोड़ता है, जो टुकड़ों को जोड़ता है। इस तरह से अधिक गहराई से घोंसले के बच्चे के टुकड़ों में बैकस्टैक को पॉपिंग करना सबसे पहले होगा
थियो

1
इसके अलावा, जब आपके पास बहुत से नेस्टेड टुकड़े होते हैं, तो प्रत्येक अपने बच्चे के टुकड़ों के साथ जो स्क्रीन के हिस्से को कवर कर सकता है, प्रत्येक बच्चे के टुकड़े प्रबंधक के लिए एक अलग बैकस्टैक होने का विचार समझ में आता है। बैकस्टैक को गतिविधि में सभी टुकड़ों में एकीकृत किया जाना चाहिए, और टुकड़े के घोंसले के स्तर की परवाह किए बिना, उस क्रम में टुकड़े के परिवर्तन को ट्रैक करना चाहिए।
थियो

क्या बिना SupportFragmentManagerमानक के इस समाधान का उपयोग करना संभव है FragmentManager?
पीटर चैप्पी

13

इस उत्तर के साथ यह पुनरावर्ती बैक चेकिंग को हैंडल करेगा और प्रत्येक टुकड़े को डिफ़ॉल्ट व्यवहार को ओवरराइड करने का मौका देगा। इसका मतलब है कि आपके पास एक टुकड़ा हो सकता है जो एक ViewPager को होस्ट करता है जो उस पृष्ठ पर स्क्रॉल करने के लिए कुछ विशेष करता है जैसे कि बैक-स्टैक, या होम पेज पर स्क्रॉल करें और फिर अगले बैक प्रेस से बाहर निकलें।

AppCompatActivity को विस्तारित करने वाली अपनी गतिविधि में इसे जोड़ें।

@Override
public void onBackPressed()
{
    if(!BaseFragment.handleBackPressed(getSupportFragmentManager())){
        super.onBackPressed();
    }
}

इसे अपने बेसग्रैगमेंट में जोड़ें या जिस कक्षा से आप अपने सभी अंशों को प्राप्त कर सकते हैं।

public static boolean handleBackPressed(FragmentManager fm)
{
    if(fm.getFragments() != null){
        for(Fragment frag : fm.getFragments()){
            if(frag != null && frag.isVisible() && frag instanceof BaseFragment){
                if(((BaseFragment)frag).onBackPressed()){
                    return true;
                }
            }
        }
    }
    return false;
}

protected boolean onBackPressed()
{
    FragmentManager fm = getChildFragmentManager();
    if(handleBackPressed(fm)){
        return true;
    }
    else if(getUserVisibleHint() && fm.getBackStackEntryCount() > 0){
        fm.popBackStack();
        return true;
    }
    return false;
}

यह बहुत अच्छा जवाब है, एपीआई स्तर 23/24 पर काम कर रहा है और समर्थन के साथ 24.1.1
रिनव

कुछ समय से इसका उपयोग कर रहे हैं, यह एक महान समाधान है - लेकिन हाल ही में पुस्तकालय समूह के बाहर के समूह से getFragments () के लिए सीधी पहुंच के बारे में चेतावनी देना। क्या आपने पहले से ही एक काम के साथ अद्यतन किया है?
साइमन हकेट

कुछ तय है या 2020 में हमारे पास अभी भी मुद्दा है और आपको अपने समाधान को लागू करना चाहिए ?? !!
जहर

3

यह कोड टुकड़ा प्रबंधकों के पेड़ को नेविगेट करेगा और पिछले एक को जोड़ा गया था जिसमें कोई भी टुकड़ा है जो इसे स्टैक से पॉप कर सकता है:

private FragmentManager getLastFragmentManagerWithBack(FragmentManager fm)
{
  FragmentManager fmLast = fm;

  List<Fragment> fragments = fm.getFragments();

  for (Fragment f : fragments)
  {
    if ((f.getChildFragmentManager() != null) && (f.getChildFragmentManager().getBackStackEntryCount() > 0))
    {
      fmLast = f.getFragmentManager();
      FragmentManager fmChild = getLastFragmentManagerWithBack(f.getChildFragmentManager());

      if (fmChild != fmLast)
        fmLast = fmChild;
    }
  }

  return fmLast;
}

विधि को बुलाओ:

@Override
public void onBackPressed()
{
  FragmentManager fm = getLastFragmentManagerWithBack(getSupportFragmentManager());

  if (fm.getBackStackEntryCount() > 0)
  {
    fm.popBackStack();
    return;
  }

  super.onBackPressed();
}

यह सभी नेस्टेड टुकड़ा के लिए काम करता है अगर इसके सही ढंग से पंजीकृत है !! +100
प्रतीक सलूजा

2

कारण यह है कि आपकी गतिविधि FragmentActivity से निकलती है, जो BACK कुंजी प्रेस ( FragmentActivity की लाइन 173 देखें) को संभालती है ।

हमारे आवेदन में, मैं एक ViewPager (टुकड़े के साथ) का उपयोग कर रहा हूं और प्रत्येक टुकड़े में नेस्टेड टुकड़े हो सकते हैं। जिस तरह से मैंने इसे संभाला है वह है:

  • एक एकल विधि के साथ एक इंटरफ़ेस OnBackKeyPressedListener को परिभाषित करना void onBackKeyPressed()
  • इस इंटरफ़ेस को "टॉप" अंशों में लागू किया गया है जो ViewPager दिखाता है
  • onKeyDown को ओवरराइड करना और BACK प्रेस का पता लगाना, और व्यू पेजर में वर्तमान में सक्रिय अंश में onBackKeyPressed को कॉल करना।

यह भी ध्यान दें, कि मैं getChildFragmentManager()टुकड़ों में ठीक से घोंसले के टुकड़े का उपयोग कर रहा हूं । आप इस एंड्रॉइड-डेवलपर्स पोस्ट में एक चर्चा और स्पष्टीकरण देख सकते हैं ।


2

यदि आप एक टुकड़े में टुकड़े का उपयोग कर रहे हैं तो उपयोग करें getChildFragmentManager()

getChildFragmentManager().beginTransaction().replace(R.id.fragment_name, fragment).addToBackStack(null).commit();

यदि आप बच्चे के टुकड़े का उपयोग कर रहे हैं और इसका उपयोग करें getParentFragmentManager()

getParentFragmentManager().beginTransaction().replace(R.id.fragment_name, fragment).addToBackStack(null).commit();

यदि दोनों आपके लिए काम नहीं कर रहे हैं तो यह कोशिश करें getActivity().getSupportFragmentManager()

getActivity().getSupportFragmentManager().beginTransaction().replace(R.id.fragment_name, fragment).addToBackStack(null).commit();

1

मैं ऑन-क्रिएट व्यू () विधि में मूल विधि को जोड़कर और मूल दृश्य को पारित करके टुकड़ा बैक स्टैक को संभालने में सक्षम था।

private void catchBackEvent(View v){
    v.setFocusableInTouchMode(true);
    v.requestFocus();
    v.setOnKeyListener( new OnKeyListener()
    {
        @Override
        public boolean onKey( View v, int keyCode, KeyEvent event )
        {
            if( keyCode == KeyEvent.KEYCODE_BACK )
            {
                if(isEnableFragmentBackStack()){
                    getChildFragmentManager().popBackStack();
                                    setEnableFragmentBackStack(false);
                    return true;
                }
                else
                    return false;   
            }
            return false;
        }
    } );
}

यह विधि इनेबेलफ्रैगमेंटबैकस्टैक () एक बूलियन ध्वज है जो यह जानने के लिए कि मैं मुख्य टुकड़े पर या अगले एक पर कब हूं।

सुनिश्चित करें कि जब आप उस टुकड़े को बनाते हैं जिसे स्टैक करने की आवश्यकता होती है, तो आपको addToBackstack मेथड जोड़ना होगा।


1

यह समाधान बेहतर हो सकता है, इसे रोकें यह नेस्टेड टुकड़ों के सभी स्तरों की जांच करता है:

 /**
 * This method will go check all the back stacks of the added fragments and their nested fragments
 * to the the {@code FragmentManager} passed as parameter.
 * If there is a fragment and the back stack of this fragment is not empty,
 * then emulate 'onBackPressed' behaviour, because in default, it is not working.
 *
 * @param fm the fragment manager to which we will try to dispatch the back pressed event.
 * @return {@code true} if the onBackPressed event was consumed by a child fragment, otherwise {@code false}.
 */
public static boolean dispatchOnBackPressedToFragments(FragmentManager fm) {

    List<Fragment> fragments = fm.getFragments();
    boolean result;
    if (fragments != null && !fragments.isEmpty()) {
        for (Fragment frag : fragments) {
            if (frag != null && frag.isAdded() && frag.getChildFragmentManager() != null) {
                // go to the next level of child fragments.
                result = dispatchOnBackPressedToFragments(frag.getChildFragmentManager());
                if (result) return true;
            }
        }
    }

    // if the back stack is not empty then we pop the last transaction.
    if (fm.getBackStackEntryCount() > 0) {
        fm.popBackStack();
        fm.executePendingTransactions();
        return true;
    }

    return false;
}

अपनी गतिविधि में onBackPressedआप इसे इस तरह कह सकते हैं:

FragmentManager fm = getSupportFragmentManager();
                // if there is a fragment and the back stack of this fragment is not empty,
                // then emulate 'onBackPressed' behaviour, because in default, it is not working
                if (!dispatchOnBackPressedToFragments(fm)) {
                    // if no child fragment consumed the onBackPressed event,
                    // we execute the default behaviour.
                    super.onBackPressed();
                }

1

उनकी मदद के लिए सभी का धन्यवाद, यह (ट्वीक किए गए संस्करण) मेरे लिए काम करता है:

@Override
public void onBackPressed() {
    if (!recursivePopBackStack(getSupportFragmentManager())) {
        super.onBackPressed();
    }
}

/**
 * Recursively look through nested fragments for a backstack entry to pop
 * @return: true if a pop was performed
 */
public static boolean recursivePopBackStack(FragmentManager fragmentManager) {
    if (fragmentManager.getFragments() != null) {
        for (Fragment fragment : fragmentManager.getFragments()) {
            if (fragment != null && fragment.isVisible()) {
                boolean popped = recursivePopBackStack(fragment.getChildFragmentManager());
                if (popped) {
                    return true;
                }
            }
        }
    }

    if (fragmentManager.getBackStackEntryCount() > 0) {
        fragmentManager.popBackStack();
        return true;
    }

    return false;
}

नोट: आप शायद इन नेस्टेड टुकड़ों के बैकग्राउंड कलर को ऐप थीम के विंडो बैकग्राउंड कलर में सेट करना चाहेंगे, क्योंकि डिफ़ॉल्ट रूप से वे पारदर्शी होते हैं। इस प्रश्न के दायरे से बाहर, लेकिन यह विशेषता android.R.attr.windowBackground को हल करके, और उस संसाधन ID पर फ़्रैगमेंट दृश्य की पृष्ठभूमि सेट करके पूरा किया गया है।


1

5 साल से अधिक और यह मुद्दा अभी भी प्रासंगिक है। यदि आप इसके प्रतिबंध के कारण fragmentManager.getFragments () का उपयोग नहीं करना चाहते हैं। नीचे की कक्षाओं का विस्तार और उपयोग करें:

NestedFragmentActivity.java

abstract public class NestedFragmentActivity extends AppCompatActivity {

    private final Stack<Integer> mActiveFragmentIdStack = new Stack<>();
    private final Stack<String> mActiveFragmentTagStack = new Stack<>();

    @Override
    public void onBackPressed() {
        if (mActiveFragmentIdStack.size() > 0 && mActiveFragmentTagStack.size() > 0) {

            // Find by id
            int lastFragmentId = mActiveFragmentIdStack.lastElement();
            NestedFragment nestedFragment = (NestedFragment) getSupportFragmentManager().findFragmentById(lastFragmentId);

            // If cannot find by id, find by tag
            if (nestedFragment == null) {
                String lastFragmentTag = mActiveFragmentTagStack.lastElement();
                nestedFragment = (NestedFragment) getSupportFragmentManager().findFragmentByTag(lastFragmentTag);
            }

            if (nestedFragment != null) {
                nestedFragment.onBackPressed();
            }

            // If cannot find by tag, then simply pop
            mActiveFragmentTagStack.pop();
            mActiveFragmentIdStack.pop();

        } else {
            super.onBackPressed();
        }
    }

    public void addToBackStack(int fragmentId, String fragmentTag) {
        mActiveFragmentIdStack.add(fragmentId);
        mActiveFragmentTagStack.add(fragmentTag);
    }
}

NestedFragment.java

abstract public class NestedFragment extends Fragment {

    private final Stack<Integer> mActiveFragmentIdStack = new Stack<>();
    private final Stack<String> mActiveFragmentTagStack = new Stack<>();

    private NestedFragmentActivity mParentActivity;
    private NestedFragment mParentFragment;

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);

        if (getParentFragment() == null) {
            try {
                mParentActivity = (NestedFragmentActivity) context;
            } catch (ClassCastException e) {
                throw new ClassCastException(context.toString()
                        + " must implement " + NestedFragmentActivity.class.getName());
            }
        } else {
            try {
                mParentFragment = (NestedFragment) getParentFragment();
            } catch (ClassCastException e) {
                throw new ClassCastException(getParentFragment().getClass().toString()
                        + " must implement " + NestedFragment.class.getName());
            }
        }
    }

    public void onBackPressed() {

        if (mActiveFragmentIdStack.size() > 0 && mActiveFragmentTagStack.size() > 0) {

            // Find by id
            int lastFragmentId = mActiveFragmentIdStack.lastElement();
            NestedFragment nestedFragment = (NestedFragment) getChildFragmentManager().findFragmentById(lastFragmentId);

            // If cannot find by id, find by tag
            if (nestedFragment == null) {
                String lastFragmentTag = mActiveFragmentTagStack.lastElement();
                nestedFragment = (NestedFragment) getChildFragmentManager().findFragmentByTag(lastFragmentTag);
            }

            if (nestedFragment != null) {
                nestedFragment.onBackPressed();
            }

            // If cannot find by tag, then simply pop
            mActiveFragmentIdStack.pop();
            mActiveFragmentTagStack.pop();

        } else {
            getChildFragmentManager().popBackStack();
        }
    }

    private void addToBackStack(int fragmentId, String fragmentTag) {
        mActiveFragmentIdStack.add(fragmentId);
        mActiveFragmentTagStack.add(fragmentTag);
    }

    public void addToParentBackStack() {
        if (mParentFragment != null) {
            mParentFragment.addToBackStack(getId(), getTag());
        } else if (mParentActivity != null) {
            mParentActivity.addToBackStack(getId(), getTag());
        }
    }
}

स्पष्टीकरण:

उपरोक्त वर्गों से विस्तारित प्रत्येक गतिविधि और टुकड़ा उनके प्रत्येक बच्चे, और बच्चों के बच्चों के लिए अपनी पीठ के ढेर का प्रबंधन करता है, और इसी तरह। बैकस्टैक केवल "सक्रिय टुकड़ा" टैग / आईडी का रिकॉर्ड है। इसलिए कैवेट हमेशा आपके टुकड़े के लिए एक टैग और / या आईडी प्रदान करना है।

चाइल्डफ्रैग्मेंटरमैन में बैकस्टैक को जोड़ते समय, आपको "addToParentBackStack ()" भी कॉल करना होगा। यह सुनिश्चित करता है कि बाद के चबूतरे के लिए टुकड़ा का टैग / आईडी माता-पिता के टुकड़े / गतिविधि में जोड़ा जाता है।

उदाहरण:

    getChildFragmentManager().beginTransaction().replace(
            R.id.fragment,
            fragment,
            fragment.getTag()
    ).addToBackStack(null).commit();
    addToParentBackStack();

1

मैंने इसे सही ढंग से लागू किया है अगर किसी को अब तक कोई जवाब नहीं मिला

बस अपने बच्चे के घोंसले के टुकड़े पर इस विधि को जोड़ें

   @Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    // This callback will only be called when MyFragment is at least Started.
    OnBackPressedCallback callback = new OnBackPressedCallback(true ) {
        @Override
        public void handleOnBackPressed() {
            // Handle the back button event
            FragmentManager fm= getFragmentManager();
            if (fm != null) {
                if (fm.getBackStackEntryCount() > 0) {
                    fm.popBackStack();
                    Log.e( "Frag","back" );

                }

                List<Fragment> fragList = fm.getFragments();
                if (fragList != null && fragList.size() > 0) {
                    for (Fragment frag : fragList) {
                        if (frag == null) {
                            continue;
                        }
                        if (frag.isVisible()) {
                          Log.e( "Frag","Visible" );
                        }
                    }
                }
            }


        }
    };
    requireActivity().getOnBackPressedDispatcher().addCallback(this, callback);
    super.onCreate( savedInstanceState );
}

0

मैंने वर्तमान में गतिविधि की संपत्ति में खंडित रखकर इस मुद्दे को हल किया। फिर मैंने विधि को ओवरराइड किया

 // INSIDE ACTIVITY
 override fun onBackPressed() {
    val fragment = currentFragment

    if(fragment != null && fragment.childFragmentManager.fragments.any()){
        fragment.childFragmentManager.popBackStack()
    }
    else {
        super.onBackPressed()
    }
}

इस तरह मैं अपने भीतर से वर्तमान में खोले गए टुकड़े में बाल टुकड़ा जोड़ देता हूं।

 // INSIDE FRAGMENT
 menu_fragment_view.setBackgroundColor(-((Math.random() * 10000 ).toInt() % 30000)) // to see change
 add_fragment_button.setOnClickListener {
        val transaction = childFragmentManager.beginTransaction()

        transaction.add(R.id.menu_fragment_fragment_holder, MenuFragment())

        transaction.addToBackStack(null)

        transaction.commit()
    }

यह माता-पिता के टुकड़े का xml लेआउट और जोड़ा हुआ टुकड़ा है

 <?xml version="1.0" encoding="utf-8"?>
 <androidx.constraintlayout.widget.ConstraintLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:id="@+id/menu_fragment_view">
     <Button
         app:layout_constraintTop_toTopOf="parent"
         app:layout_constraintLeft_toLeftOf="parent"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:id="@+id/add_fragment_button"
         android:text="Just add fragment"/>
     <FrameLayout
         android:id="@+id/menu_fragment_fragment_holder"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         app:layout_constraintTop_toTopOf="parent"
         app:layout_constraintLeft_toLeftOf="parent"/> 
</androidx.constraintlayout.widget.ConstraintLayout>

0

यहाँ प्रस्तुत कुछ समाधानों का अवलोकन करने के बाद, मैंने पाया कि स्टैक को पॉप करने के दौरान या जब बैक एक्शन को अनदेखा किया जाना चाहिए, तब मैं इस तरह के कार्यान्वयन का उपयोग करता हूं:

"पेरेंटफ्रैगमेंट" इंटरफ़ेस को परिभाषित करना:

interface ParentFragment {
/**
 * Fragments that host child fragments and want to control their BackStack behaviour when the back button is pressed should implement this
 *
 * @return true if back press was handled, false otherwise
 */
fun onBackPressed(): Boolean

}

मूल गतिविधि में (या बेसअक्टिविटी) में "onBackPressed" को ओवरराइड करना:

override fun onBackPressed() {
    val fm: FragmentManager = supportFragmentManager
    for (frag in fm.fragments) {
        if (frag.isVisible && frag is ParentFragment && frag.onBackPressed()) {
            return
        }
    }
    super.onBackPressed()
}

और फिर माता-पिता के टुकड़े को अपनी इच्छानुसार संभालने की अनुमति दें, उदाहरण के लिए:

override fun onBackPressed(): Boolean {
    val childFragment = childFragmentManager.findFragmentByTag(SomeChildFragment::class.java.simpleName)
    if (childFragment != null && childFragment.isVisible) {
        // Only for that case, pop the BackStack (perhaps when other child fragments are visible don't)
        childFragmentManager.popBackStack()
        return true
    }
    return false
}

यह सोचने से बचने की अनुमति देता है कि उदाहरण के लिए एक दृश्य पेजर का उपयोग करते समय हटाने के लिए कुछ वैध बाल टुकड़ा है (और स्टैक एंट्री काउंट 0: 0)।


-1

यदि आपके पास DialogFragmentबदले में नेस्टेड टुकड़े हैं, तो 'वर्कअराउंड' थोड़ा अलग है। इसके बजाय एक स्थापित करने के onKeyListenerलिए rootView, आप के साथ ऐसा करने की आवश्यकता होगी Dialog। इसके अलावा, आप एक की स्थापना करेंगे DialogInterface.OnKeyListenerऔर एक की नहीं View। बेशक, याद रखना addToBackStack!

Btw, गतिविधि के लिए कॉल वापस सौंपने के लिए बैकस्टैक पर 1 टुकड़ा होना मेरा व्यक्तिगत उपयोग मामला है। गणना के लिए विशिष्ट परिदृश्य 0 हो सकते हैं।

यहाँ है कि आप onCreateDialog के भीतर क्या करते हैं

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        Dialog dialog =  super.onCreateDialog(savedInstanceState);
        dialog.setOnKeyListener(new DialogInterface.OnKeyListener() {
            @Override
            public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
                if(keyCode == KeyEvent.KEYCODE_BACK){
                    FragmentManager cfm = getChildFragmentManager();
                    if(cfm.getBackStackEntryCount()>1){
                        cfm.popBackStack();
                        return true;
                    }   
                }   
                return false;
            }
        });
        return dialog;
    }

वास्तव में समझ में नहीं आता है या पूरा नहीं है। DialogFragment या यह superclass Fragment सेटऑनकेलिस्ट नहीं है।
Ixx

@ XXX आपको श्रोता को Dialog(नहीं DialogFragment) पर सेट करना है और आपके द्वारा उपयोग किया जाने वाला श्रोता है DialogInterface.OnKeyListener- कोड काम कर रहा है और पूरा (और उपयोग में) है। आप अपनी स्थिति क्यों नहीं प्रदान करते हैं और शायद मैं मदद कर सकता हूं।
सचिन आहूजा

-1

ChildFragments के लिए यह काम करता है ।।

@Override
    public void onBackPressed() {

 if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
            getSupportFragmentManager().popBackStack();
        } else {
            doExit(); //super.onBackPressed();
        }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.