वापस ढेर से टुकड़े पर


96

मैं एंड्रॉइड 2.2 के साथ फ्रेगमेंट का उपयोग करने के लिए संगतता पैकेज का उपयोग कर रहा हूं। टुकड़ों का उपयोग करते समय, और उनके बीच बैकस्टैक में बदलाव जोड़ते हुए, मैं एक गतिविधि के onResume के समान व्यवहार को प्राप्त करना चाहूंगा, अर्थात, जब भी कोई टुकड़ा "अग्रभूमि" (उपयोगकर्ता को दिखाई देता है) को पॉप आउट करने के बाद लाया जाता है बैकस्टैक, मैं टुकड़े के भीतर सक्रिय होने के लिए किसी तरह का कॉलबैक चाहूंगा (उदाहरण के लिए, एक साझा UI संसाधन पर कुछ परिवर्तन करने के लिए)।

मैंने देखा कि टुकड़े के ढांचे के भीतर कॉलबैक में कोई निर्मित नहीं है। क्या इसे प्राप्त करने के लिए कोई अच्छा अभ्यास है?


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

3
आपकी गतिविधि दिए गए शीर्षक को निर्धारित कर सकती है कि यह जानता है कि कौन से टुकड़े किसी भी समय प्रदर्शित किए जा रहे हैं। इसके अलावा, मुझे लगता है कि आप कुछ कर सकते हैं onCreateViewजिसमें एक पॉप के बाद टुकड़े के लिए कहा जाएगा।
PJL

1
@PJL +1 उस संदर्भ के अनुसार जिस तरह से किया जाना चाहिए। हालाँकि मैं श्रोता के तरीके को पसंद करता हूँ
मोमो

जवाबों:


115

एक बेहतर समाधान की कमी के लिए, मुझे यह काम मिल गया: मान लें कि मेरे पास 1 गतिविधि (MyActivity) और कुछ टुकड़े हैं जो एक-दूसरे को प्रतिस्थापित करते हैं (केवल एक समय में एक ही दिखाई देता है)।

MyActivity में, इस श्रोता को जोड़ें:

getSupportFragmentManager().addOnBackStackChangedListener(getListener());

(जैसा कि आप देख सकते हैं कि मैं संगतता पैकेज का उपयोग कर रहा हूं)।

getListener कार्यान्वयन:

private OnBackStackChangedListener getListener()
    {
        OnBackStackChangedListener result = new OnBackStackChangedListener()
        {
            public void onBackStackChanged() 
            {                   
                FragmentManager manager = getSupportFragmentManager();

                if (manager != null)
                {
                    MyFragment currFrag = (MyFragment) manager.findFragmentById(R.id.fragmentItem);

                    currFrag.onFragmentResume();
                }                   
            }
        };

        return result;
    }

MyFragment.onFragmentResume()"बैक" दबाए जाने के बाद बुलाया जाएगा। हालांकि कुछ चेतावनी:

  1. यह मानता है कि आपने बैकस्टैक में सभी लेन-देन जोड़े (उपयोग करते हुए FragmentTransaction.addToBackStack())
  2. यह प्रत्येक स्टैक परिवर्तन पर सक्रिय किया जाएगा (आप बैक स्टैक जैसे एनीमेशन में अन्य सामान स्टोर कर सकते हैं) ताकि आपको एक ही उदाहरण के टुकड़े के लिए कई कॉल मिल सकें।

3
यदि आपने आपके लिए काम किया है तो आपको अपना उत्तर सही मानना ​​चाहिए।
पॉवरज 1984 18

7
उस खंड आईडी के संदर्भ में आप कैसे काम कर रहे हैं? यह प्रत्येक टुकड़े के लिए अलग कैसे है और सही एक बैक स्टैक में पाया जाता है?
मैनफ्रेड मोजर

2
@Arpzit उस पद्धति को नहीं कहा जाता है, और एंड्रॉइड डॉक्स चुपचाप स्वीकार करते हैं कि इसे कभी नहीं बुलाया जाएगा (यह केवल तभी कहा जाता है जब गतिविधि फिर से शुरू हो जाती है - जो कभी नहीं, यदि वह गतिविधि पहले से ही सक्रिय है)
एडम

2
हास्यास्पद है कि हमें यह करना है! लेकिन ख़ुशी है कि कोई और इस "फीचर" को खोजने में सक्षम था
स्टीवेबोट

3
onResume () नहीं कहा जाता है
मार्टी मिलर

33

मैंने सुझाए गए समाधान को थोड़ा बदल दिया है। मेरे लिए इस तरह बेहतर काम करता है:

private OnBackStackChangedListener getListener() {
    OnBackStackChangedListener result = new OnBackStackChangedListener() {
        public void onBackStackChanged() {
            FragmentManager manager = getSupportFragmentManager();
            if (manager != null) {
                int backStackEntryCount = manager.getBackStackEntryCount();
                if (backStackEntryCount == 0) {
                    finish();
                }
                Fragment fragment = manager.getFragments()
                                           .get(backStackEntryCount - 1);
                fragment.onResume();
            }
        }
    };
    return result;
}

1
कृपया अंतर स्पष्ट करें, और इसका उपयोग क्यों करें।
मैथ चिलर

2
मेरे समाधान और पहले वाले के बीच का अंतर यह है कि टुकड़ों में जोड़ने, बदलने या पीछे की ओर जाने वाले टुकड़ों में हर खंड परिवर्तन पर, शीर्ष टुकड़े के "onResume" विधि को कहा जाएगा। इस विधि में हार्डकूट आईडी नहीं है। मूल रूप से यह onResume () उस खंड में कहता है जिसे आप प्रत्येक परिवर्तन पर देख रहे हैं, इससे कोई फर्क नहीं पड़ता कि यह मेमोरी में पहले से लोड था या नहीं।
Brotoo25

9
getFragments()SupportFragmentManager में बुलाया विधि के लिए कोई रिकॉर्ड नहीं है ... -1: - /
प्रोटॉस्टोम

1
OnResume () को कॉल किया जाता है। लेकिन मैं ब्लैंक स्क्रीन प्राप्त कर रहा हूं..इस मुद्दे को हल करने का कोई अन्य तरीका नहीं है?
कावी

मुझे लगता है कि यह एक ArrayOutOfBounds अपवाद की ओर जाता है अगर backStackEntryCount 0. क्या मैं गलत हूं? पोस्ट को संपादित करने की कोशिश की गई लेकिन इसे अस्वीकार कर दिया गया।
माइक टी

6

एक के बाद popStackBack()आप निम्नलिखित कॉलबैक का उपयोग कर सकते हैं: onHiddenChanged(boolean hidden)अपने टुकड़ा के भीतर


2
यह ऐप कंपेटिटिव टुकड़ों के साथ काम नहीं करता है।
लो-टैन

यही मैंने प्रयोग किया है। धन्यवाद!
अल्बर्ट विला कैल्वो

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

4

एंड्रॉइड डेवलपर्स पर निम्न अनुभाग एक संचार तंत्र का वर्णन करता है जो गतिविधि को घटना कॉलबैक बनाता है । इससे एक पंक्ति उद्धृत करने के लिए:

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

संपादित करें: टुकड़ा के पास एक onStart(...)ऐसा यंत्र होता है जब टुकड़ा उपयोगकर्ता को दिखाई देता है। इसी तरह onResume(...)जब दिखाई और सक्रिय रूप से चल रहा हो। ये उनकी गतिविधि के समकक्षों से बंधे हैं। संक्षेप में: का उपयोग करेंonResume()


1
मैं सिर्फ फ्रेगमेंट वर्ग में एक विधि की तलाश कर रहा हूं जो उपयोगकर्ता को दिखाए जाने पर सक्रिय हो। चाहे एक FragmentTransaction.add () / प्रतिस्थापित (), या एक FragmentManager.popBackStack () के कारण।
oriharel

@oriharel अद्यतन उत्तर देखें। जब गतिविधि भरी हुई है तो आपको विभिन्न कॉल, onAttach, onCreate, onActivityCreated, onStart, onResume मिलेंगे। यदि आपने `setRetainInstance (सच्चा) 'कहा है, तो आपको एक onCreate नहीं मिलेगा जब टुकड़े को वापस क्लिक करने पर पुन: व्यवस्थित किया जा रहा हो।
PJL

15
एक ही गतिविधि में टुकड़ों के बीच "बैक" पर क्लिक करने पर onStart () कभी नहीं कहा जाता है। मैंने प्रलेखन में अधिकांश कॉलबैक की कोशिश की और उनमें से किसी को भी इस तरह के परिदृश्य में नहीं बुलाया गया। वर्कअराउंड के लिए मेरा उत्तर देखें।
oriharel

हम्म एफबीआई v4 के साथ हम्म मैं अपने टुकड़े के लिए एक onResume देख रहा हूं। मुझे @ oriharel का उत्तर पसंद है, हालांकि यह पॉपबैकस्टैक के माध्यम से दृश्यमान होने के बीच अंतर करता है, न कि केवल अग्रभूमि में लाने के बजाय।
PJL

3

यदि बैकस्टैक पर एक टुकड़ा रखा जाता है, तो एंड्रॉइड बस अपने दृश्य को नष्ट कर देता है। टुकड़ा उदाहरण खुद को नहीं मारा है। शुरू करने के लिए एक सरल तरीका onViewCreated ईवेंट को सुनने के लिए होना चाहिए, एक पुट "onResume ()" लॉजिक है।

boolean fragmentAlreadyLoaded = false;
    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onViewCreated(view, savedInstanceState);

        if (savedInstanceState == null && !fragmentAlreadyLoaded) {
            fragmentAlreadyLoaded = true;

            // Code placed here will be executed once
        }

        //Code placed here will be executed even when the fragment comes from backstack
    }

3

मेरी गतिविधि में शामिल हों ()

getSupportFragmentManager().addOnBackStackChangedListener(getListener());

विशिष्ट फ्रैगमेंट को पकड़ने और onResume () पर कॉल करने के लिए इस विधि का उपयोग करें

private FragmentManager.OnBackStackChangedListener getListener()
    {
        FragmentManager.OnBackStackChangedListener result = new FragmentManager.OnBackStackChangedListener()
        {
            public void onBackStackChanged()
            {
                Fragment currentFragment = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
                if (currentFragment instanceof YOURFRAGMENT) {
                    currentFragment.onResume();
                }
            }
        };

        return result;
    }

2

थोड़ा सुधार और एक प्रबंधक समाधान में लिपटे।

ध्यान रखने योग्य बातें। FragmentManager एक सिंगलटन नहीं है, यह केवल गतिविधि के भीतर Fragments का प्रबंधन करता है, इसलिए प्रत्येक गतिविधि में यह नया होगा। इसके अलावा, यह समाधान अब तक ViewPager को ध्यान में नहीं रखता है जो कि सेटUserVanishHint () पद्धति को फ्रैगमेंट की चिपचिपाहट को नियंत्रित करने में मदद करता है।

इस समस्या से निपटने के लिए निम्न वर्गों का उपयोग करने के लिए स्वतंत्र महसूस करें (Dagger2 इंजेक्शन का उपयोग करता है)। गतिविधि में कॉल करें:

//inject FragmentBackstackStateManager instance to myFragmentBackstackStateManager
FragmentManager fragmentManager = getSupportFragmentManager(); 
myFragmentBackstackStateManager.apply(fragmentManager);

FragmentBackstackStateManager.java:

@Singleton
public class FragmentBackstackStateManager {

    private FragmentManager fragmentManager;

    @Inject
    public FragmentBackstackStateManager() {
    }

    private BackstackCallback backstackCallbackImpl = new BackstackCallback() {
        @Override
        public void onFragmentPushed(Fragment parentFragment) {
            parentFragment.onPause();
        }

        @Override
        public void onFragmentPopped(Fragment parentFragment) {
            parentFragment.onResume();
        }
    };

    public FragmentBackstackChangeListenerImpl getListener() {
        return new FragmentBackstackChangeListenerImpl(fragmentManager, backstackCallbackImpl);
    }

    public void apply(FragmentManager fragmentManager) {
        this.fragmentManager = fragmentManager;
        fragmentManager.addOnBackStackChangedListener(getListener());
    }
}

FragmentBackstackChangeListenerImpl.java:

public class FragmentBackstackChangeListenerImpl implements FragmentManager.OnBackStackChangedListener {

    private int lastBackStackEntryCount = 0;
    private final FragmentManager fragmentManager;
    private final BackstackCallback backstackChangeListener;

    public FragmentBackstackChangeListenerImpl(FragmentManager fragmentManager, BackstackCallback backstackChangeListener) {
        this.fragmentManager = fragmentManager;
        this.backstackChangeListener = backstackChangeListener;
        lastBackStackEntryCount = fragmentManager.getBackStackEntryCount();
    }

    private boolean wasPushed(int backStackEntryCount) {
        return lastBackStackEntryCount < backStackEntryCount;
    }

    private boolean wasPopped(int backStackEntryCount) {
        return lastBackStackEntryCount > backStackEntryCount;
    }

    private boolean haveFragments() {
        List<Fragment> fragmentList = fragmentManager.getFragments();
        return fragmentList != null && !fragmentList.isEmpty();
    }


    /**
     * If we push a fragment to backstack then parent would be the one before => size - 2
     * If we pop a fragment from backstack logically it should be the last fragment in the list, but in Android popping a fragment just makes list entry null keeping list size intact, thus it's also size - 2
     *
     * @return fragment that is parent to the one that is pushed to or popped from back stack
     */
    private Fragment getParentFragment() {
        List<Fragment> fragmentList = fragmentManager.getFragments();
        return fragmentList.get(Math.max(0, fragmentList.size() - 2));
    }

    @Override
    public void onBackStackChanged() {
        int currentBackStackEntryCount = fragmentManager.getBackStackEntryCount();
        if (haveFragments()) {
            Fragment parentFragment = getParentFragment();

            //will be null if was just popped and was last in the stack
            if (parentFragment != null) {
                if (wasPushed(currentBackStackEntryCount)) {
                    backstackChangeListener.onFragmentPushed(parentFragment);
                } else if (wasPopped(currentBackStackEntryCount)) {
                    backstackChangeListener.onFragmentPopped(parentFragment);
                }
            }
        }

        lastBackStackEntryCount = currentBackStackEntryCount;
    }
}

BackstackCallback.java:

public interface BackstackCallback {
    void onFragmentPushed(Fragment parentFragment);

    void onFragmentPopped(Fragment parentFragment);
}

0

यह सही जवाब है जिसे आप onResume () कह सकते हैं, बशर्ते कि टुकड़ा गतिविधि से जुड़ा हुआ है। वैकल्पिक रूप से आप onAttach और onDetach का उपयोग कर सकते हैं


1
आप उदाहरण पोस्ट कर सकते हैं?
कावी

0

onResume () टुकड़े के लिए ठीक काम करता है ...

public class listBook extends Fragment {

    private String listbook_last_subtitle;
...

    @Override
       public void onCreate(Bundle savedInstanceState) {

        String thisFragSubtitle = (String) getActivity().getActionBar().getSubtitle();
        listbook_last_subtitle = thisFragSubtitle;
       }
...

    @Override
        public void onResume(){
            super.onResume();
            getActivity().getActionBar().setSubtitle(listbook_last_subtitle);
        }
...

0
public abstract class RootFragment extends Fragment implements OnBackPressListener {

 @Override
 public boolean onBackPressed() {
  return new BackPressImpl(this).onBackPressed();
 }

 public abstract void OnRefreshUI();

}


public class BackPressImpl implements OnBackPressListener {

 private Fragment parentFragment;

 public BackPressImpl(Fragment parentFragment) {
  this.parentFragment = parentFragment;
 }

 @Override
 public boolean onBackPressed() {
  ((RootFragment) parentFragment).OnRefreshUI();
 }
}

और प्रभाव को देखने के लिए RootFragment से अपने मूल को अंतिम रूप दें


0

मेरा वर्कअराउंड एक्शनबार का वर्तमान शीर्षक फ्रैगमेंट में नए शीर्षक पर सेट करने से पहले प्राप्त करना है। इस तरह, एक बार जब फ्रैगमेंट पॉप हो जाता है, तो मैं उस शीर्षक पर वापस बदल सकता हूं।

@Override
public void onResume() {
    super.onResume();
    // Get/Backup current title
    mTitle = ((ActionBarActivity) getActivity()).getSupportActionBar()
            .getTitle();
    // Set new title
    ((ActionBarActivity) getActivity()).getSupportActionBar()
        .setTitle(R.string.this_fragment_title);
}

@Override
public void onDestroy() {
    // Set title back
    ((ActionBarActivity) getActivity()).getSupportActionBar()
        .setTitle(mTitle);

    super.onDestroy();
}

0

मैंने FragmentTagsअपने सभी खंडों को परिभाषित करने के लिए enum का उपयोग किया है ।

TAG_FOR_FRAGMENT_A(A.class),
TAG_FOR_FRAGMENT_B(B.class),
TAG_FOR_FRAGMENT_C(C.class)

उत्तीर्ण करना FragmentTags.TAG_FOR_FRAGMENT_A.name()टुकड़ा टैग के रूप में ।

और अब

@Override
public void onBackPressed(){
   FragmentManager fragmentManager = getFragmentManager();
   Fragment current
   = fragmentManager.findFragmentById(R.id.fragment_container);
    FragmentTags fragmentTag = FragmentTags.valueOf(current.getTag());

  switch(fragmentTag){
    case TAG_FOR_FRAGMENT_A:
        finish();
        break;
   case TAG_FOR_FRAGMENT_B:
        fragmentManager.popBackStack();
        break;
   case default: 
   break;
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.