जब मैं बैक स्टैक में जोड़ा जाता हूं तो मैं खंड स्थिति कैसे बना सकता हूं?


160

मैंने एक डमी गतिविधि लिखी है जो दो टुकड़ों के बीच स्विच करती है। जब आप FragmentA से FragmentB पर जाते हैं, तो FragmentA बैक स्टैक में जुड़ जाता है। हालाँकि, जब मैं FragmentA पर वापस लौटता हूँ (वापस दबाकर), तो एक पूरी तरह से नया FragmentA बन जाता है और वह जिस अवस्था में था वह खो जाता है। मुझे लग रहा है कि मैं इस प्रश्न के समान चीज के बाद हूं , लेकिन मैंने इस मुद्दे को जड़ से समाप्त करने में मदद करने के लिए एक पूर्ण कोड नमूना शामिल किया है:

public class FooActivity extends Activity {
  @Override public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    final FragmentTransaction transaction = getFragmentManager().beginTransaction();
    transaction.replace(android.R.id.content, new FragmentA());
    transaction.commit();
  }

  public void nextFragment() {
    final FragmentTransaction transaction = getFragmentManager().beginTransaction();
    transaction.replace(android.R.id.content, new FragmentB());
    transaction.addToBackStack(null);
    transaction.commit();
  }

  public static class FragmentA extends Fragment {
    @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
      final View main = inflater.inflate(R.layout.main, container, false);
      main.findViewById(R.id.next_fragment_button).setOnClickListener(new View.OnClickListener() {
        public void onClick(View v) {
          ((FooActivity) getActivity()).nextFragment();
        }
      });
      return main;
    }

    @Override public void onSaveInstanceState(Bundle outState) {
      super.onSaveInstanceState(outState);
      // Save some state!
    }
  }

  public static class FragmentB extends Fragment {
    @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
      return inflater.inflate(R.layout.b, container, false);
    }
  }
}

कुछ लॉग संदेशों के साथ:

07-05 14:28:59.722 D/OMG     ( 1260): FooActivity.onCreate
07-05 14:28:59.742 D/OMG     ( 1260): FragmentA.onCreateView
07-05 14:28:59.742 D/OMG     ( 1260): FooActivity.onResume
<Tap Button on FragmentA>
07-05 14:29:12.842 D/OMG     ( 1260): FooActivity.nextFragment
07-05 14:29:12.852 D/OMG     ( 1260): FragmentB.onCreateView
<Tap 'Back'>
07-05 14:29:16.792 D/OMG     ( 1260): FragmentA.onCreateView

यह कभी भी FragmentA.onSaveInstanceState को कॉल नहीं कर रहा है और जब आप वापस आते हैं तो यह एक नया FragmentA बनाता है। हालाँकि, अगर मैं FragmentA पर हूँ और मैं स्क्रीन लॉक करता हूँ, तो FragmentA.onSaveInstanceState को कॉल नहीं किया जाता है। इतना अजीब ... क्या मैं पुन: निर्माण की आवश्यकता नहीं करने के लिए पिछले हिस्से में जोड़े गए एक टुकड़े की अपेक्षा में गलत हूं? यहाँ डॉक्स का कहना है:

जबकि, यदि आप किसी खंड को हटाते समय AddToBackStack () कॉल करते हैं, तो उपयोगकर्ता द्वारा नेविगेट किए जाने पर टुकड़ा बंद हो जाता है और फिर से चालू हो जाएगा।


3
@ जान-हेंक उन चीजों के बारे में क्या जिन्हें लाना है? उदाहरण के लिए, स्क्रॉल की स्थिति ListView। एक स्क्रॉल श्रोता को संलग्न करने और एक उदाहरण चर को अपडेट करने के लिए बहुत अधिक घेरा-कूद की तरह लगता है।
जेक व्हार्टन

2
@JakeWharton मैं मानता हूं कि यह आसान होना चाहिए, लेकिन जहां तक ​​मुझे पता है कि इसके आसपास कोई रास्ता नहीं है क्योंकि onCreateView कहा जाता है जब बैकस्टैक से एक टुकड़ा बहाल होता है। लेकिन मैं गलत हो सकता है :)
Jan-Henk

1
onCreate को बुलाया नहीं जाता है। तो जाहिरा तौर पर यह एक ही उदाहरण का फिर से उपयोग कर रहा है, लेकिन onCreateView को फिर से कॉल कर रहा है? लंगड़ा। मुझे लगता है कि मैं सिर्फ onCreateView के परिणाम को कैश कर सकता हूं और यदि onCreateView को फिर से कॉल किया जाता है तो बस मौजूदा दृश्य वापस कर दें।
एरिक

1
बिल्कुल वही जो मैं घंटों से देख रहा था। क्या आप पोस्ट कर सकते हैं कि आपने उदाहरण चर का उपयोग करके इसे कैसे प्राप्त किया?
उमा

1
इसलिए मैंने हाल ही में github.com/frostymarvelous/Folio में अपना खुद का कार्यान्वयन शुरू किया और एक समस्या आई। ओओएम क्रैश होने से पहले मैं लगभग 5 जटिल पेज / फ्रेगमेंट बनाने में सक्षम हूं। यही मुझे यहां ले गया। छिपाना और दिखाना बस पर्याप्त नहीं है। दृश्य बहुत भारी हैं।
ठंढाईदार

जवाबों:


120

यदि आप बैक स्टैक से एक टुकड़े में लौटते हैं, तो यह टुकड़े को फिर से नहीं बनाता है लेकिन उसी उदाहरण का फिर से उपयोग करता है और onCreateView()खंड जीवनचक्र में शुरू होता है , Fragment जीवनचक्र देखें ।

इसलिए यदि आप राज्य को संग्रहीत करना चाहते हैं तो आपको उदाहरण चर का उपयोग करना चाहिए और भरोसा नहीं करना चाहिए onSaveInstanceState()


32
प्रलेखन का वर्तमान संस्करण इस दावे का खंडन करता है। फ़्लोचार्ट कहता है कि आप क्या कहते हैं, लेकिन पृष्ठ के मुख्य क्षेत्र में पाठ onCreateView () केवल यह कहता है कि पहली बार फ़्रैगमेंट प्रदर्शित होता है: developer.android.com/guide/compenders/fragments.html इस पर लड़ रहे हैं। मुद्दा अब, और मुझे बैकस्टैक से एक टुकड़ा लौटते समय किसी भी तरीके को नहीं बुलाया जाता है। (Android 4.2)
कॉलिन एम।

10
इसके व्यवहार को लॉग करने की कोशिश की। OnCreateView () को हमेशा कहा जाता है जब टुकड़ा प्रदर्शित किया जा रहा है।
प्रिंसेपिएरो

4
@ColinM। समस्या का कोई समाधान?
बर्फानी तूफान

9
यह मेरे लिए काम नहीं करता है। मेरे उदाहरण चर खंड में लौटने पर अशक्त हैं! मैं राज्य को कैसे बचा सकता हूं?
डॉन रम्मी

5
इसलिए अगर हमें सेविंग इंस्टेंस पर रिले नहीं करना चाहिए तो फ्रैगमेंट स्टेटस और डेटा को कैसे सेव करना चाहिए?
महदी

80

एप्पल की तुलना UINavigationControllerऔर UIViewController, गूगल एंड्रॉयड सॉफ्टवेयर वास्तुकला में अच्छी तरह से नहीं करता है। और एंड्रॉइड के बारे में दस्तावेज़ Fragmentबहुत मदद नहीं करता है।

जब आप FragmentA से FragmentB में प्रवेश करते हैं, तो मौजूदा FragmentA उदाहरण नष्ट नहीं होता है। जब आप वापस FragmentB में प्रेस करते हैं और FragmentA पर वापस आते हैं, तो हम एक नया FragmentA उदाहरण नहीं बनाते हैं। मौजूदा FragmentA उदाहरण onCreateView()को बुलाया जाएगा।

महत्वपूर्ण बात यह है कि हमें फ्रैगमेंटा में फिर से दृश्य नहीं बढ़ाना चाहिए onCreateView(), क्योंकि हम मौजूदा फ्रैगमेंटा के उदाहरण का उपयोग कर रहे हैं। हमें rootView को बचाने और पुन: उपयोग करने की आवश्यकता है।

निम्न कोड अच्छी तरह से काम करता है। यह न केवल टुकड़ा स्थिति रखता है, बल्कि रैम और सीपीयू लोड को भी कम करता है (क्योंकि हम केवल लेआउट को बढ़ाते हैं यदि आवश्यक हो)। मैं विश्वास नहीं कर सकता कि Google का नमूना कोड और दस्तावेज़ कभी भी इसका उल्लेख नहीं करता है लेकिन हमेशा लेआउट को बढ़ाता है

संस्करण 1 (संस्करण 1 का उपयोग न करें। संस्करण 2 का उपयोग करें)

public class FragmentA extends Fragment {
    View _rootView;
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        if (_rootView == null) {
            // Inflate the layout for this fragment
            _rootView = inflater.inflate(R.layout.fragment_a, container, false);
            // Find and setup subviews
            _listView = (ListView)_rootView.findViewById(R.id.listView);
            ...
        } else {
            // Do not inflate the layout again.
            // The returned View of onCreateView will be added into the fragment.
            // However it is not allowed to be added twice even if the parent is same.
            // So we must remove _rootView from the existing parent view group
            // (it will be added back).
            ((ViewGroup)_rootView.getParent()).removeView(_rootView);
        }
        return _rootView;
    }
}

------ अपडेट 3 मई 2005 को: -------

जैसा कि उल्लेख किया गया है, कभी-कभी _rootView.getParent()अशक्त होता है onCreateView, जो दुर्घटना का कारण बनता है। संस्करण 2 ने dro116 के सुझाव के अनुसार onDestroyView () में _rootView को हटा दिया। एंड्रॉइड 4.0.3, 4.4.4, 5.1.0 पर परीक्षण किया गया।

संस्करण 2

public class FragmentA extends Fragment {
    View _rootView;
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        if (_rootView == null) {
            // Inflate the layout for this fragment
            _rootView = inflater.inflate(R.layout.fragment_a, container, false);
            // Find and setup subviews
            _listView = (ListView)_rootView.findViewById(R.id.listView);
            ...
        } else {
            // Do not inflate the layout again.
            // The returned View of onCreateView will be added into the fragment.
            // However it is not allowed to be added twice even if the parent is same.
            // So we must remove _rootView from the existing parent view group
            // in onDestroyView() (it will be added back).
        }
        return _rootView;
    }

    @Override
    public void onDestroyView() {
        if (_rootView.getParent() != null) {
            ((ViewGroup)_rootView.getParent()).removeView(_rootView);
        }
        super.onDestroyView();
    }
}

चेतावनी !!!

यह एक हैक है! हालांकि मैं इसे अपने ऐप में उपयोग कर रहा हूं, लेकिन आपको टिप्पणियों का ध्यानपूर्वक परीक्षण करने और पढ़ने की आवश्यकता है।


38
संपूर्ण खंड के रूटव्यू का संदर्भ रखना एक बुरा विचार है IMO। यदि आप लगातार कई अंशों को बैकस्टैक में जोड़ रहे हैं और उनमें से सभी इसके रूटव्यू (जिसमें एक बड़ी मेमोरी फुटप्रिंट है) को पकड़ रहे हैं, तो संभावना है कि आप आउटऑफमेमोरीयर के साथ समाप्त हो सकते हैं क्योंकि सभी टुकड़े रूटव्यू संदर्भ और जीसी कैंट नहीं रखते हैं इसे इकट्ठा करो। मुझे लगता है कि बेहतर दृष्टिकोण यह है कि हर समय दृश्य को बढ़ाया जाए (और एंड्रॉइड सिस्टम अपने दृश्य निर्माण / विनाश को संभालने दें) और onActivityCreated / onViewCreated चेक करें यदि आपका डेटा शून्य है। यदि हाँ, तो इसे लोड करें, अन्यथा डेटा को विचारों पर सेट करें।
traninho

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

4
@AllDayAmazing यह एक अच्छा बिंदु है। सच कहूं तो, मैं अभी बहुत उलझन में हूं। क्या कोई यह समझाने का प्रयास कर सकता है कि किसी खंड के रूटव्यू का संदर्भ रखना ठीक नहीं है, लेकिन केवल रूटव्यू के किसी भी बच्चे के लिए एक संदर्भ (जिसमें वैसे भी रूटव्यू का संदर्भ है) ठीक है?
ट्रिनिहो

2
जब तक आप 5 घंटे बर्बाद नहीं करना चाहते हैं, तब तक यह पता लगाने के लिए कि आपका कोड क्या है ... तब केवल यह पता लगाने के लिए कि यह कारण है। अब मुझे सामान का एक गुच्छा फिर से भरना है क्योंकि मैंने इस हैक का उपयोग किया है। यदि आप किसी अन्य को देखने में लाना चाहते हैं, तो यह बेहतर है कि यदि आप किसी अन्य को देखने में लाना चाहते हैं, तो खंड के UI का उपयोग करें। fragmentTransaction.replace () खंड के विचारों को नष्ट करने के लिए है ..... सिस्टम से न लड़ें।
dell116

2
@ विंसयुआन - मैंने एंड्रॉइड 5.1 पर नवीनतम v7-appcompat लाइब्रेरी के साथ परीक्षण किया और इसने खंड के 6 उदाहरणों को छोड़ दिया, जिन्हें मेरी गतिविधि के FragmentManager में हटा दिया जाना चाहिए था। भले ही जीसी इसे सही ढंग से संभाल लेगा (जो मुझे विश्वास नहीं है) यह आपके ऐप के साथ-साथ सामान्य रूप से डिवाइस के लिए मेमोरी पर अनावश्यक तनाव पैदा कर रहा है। बस .add () का उपयोग करने से इस हैक कोड की सभी की आवश्यकता पूरी तरह से दूर हो जाती है। ऐसा करना पूरी तरह से उस चीज के खिलाफ है जो FragmentTransaction.replace () का उपयोग करके पहले स्थान पर करने के लिए थी।
dell116

53

मुझे लगता है कि आप जो खोज रहे हैं उसे हासिल करने का एक वैकल्पिक तरीका है। मैं इसका पूर्ण समाधान नहीं कहता, लेकिन इसने मेरे मामले में उद्देश्य को पूरा किया।

मैंने जो कुछ किया, उसके स्थान पर मैंने अभी-अभी लक्ष्य के टुकड़े को जोड़ा था। तो मूल रूप से आप add()इसके बजाय विधि का उपयोग करने जा रहे हैं replace()

मैंने और क्या किया। मैं अपने वर्तमान टुकड़े को छुपाता हूं और इसे बैकस्टैक में भी जोड़ता हूं।

इसलिए यह अपने दृश्य को नष्ट किए बिना वर्तमान टुकड़े पर नए टुकड़े को ओवरलैप करता है। (यह देखें कि इसकी onDestroyView()विधि को बुलाया नहीं जा रहा है। प्लस backstateमुझे इसे फिर से शुरू करने का लाभ देने के लिए जोड़ रहा है ।

यहाँ कोड है:

Fragment fragment=new DestinationFragment();
FragmentManager fragmentManager = getFragmentManager();
android.app.FragmentTransaction ft=fragmentManager.beginTransaction();
ft.add(R.id.content_frame, fragment);
ft.hide(SourceFragment.this);
ft.addToBackStack(SourceFragment.class.getName());
ft.commit();

AFAIK सिस्टम केवल तभी कॉल करता है onCreateView()जब दृश्य नष्ट हो जाता है या नहीं बनाया जाता है। लेकिन यहां हमने इसे स्मृति से नहीं हटाकर दृश्य को बचाया है। इसलिए यह एक नया दृष्टिकोण नहीं बनाएगा।

और जब आप डेस्टिनेशन फ्रैगमेंट से वापस आएंगे, तो यह अंतिम FragmentTransactionहटाने वाले शीर्ष टुकड़े को पॉप करेगा जो स्क्रीन पर दिखाई देने के लिए सबसे ऊपर (SourceFragment) का दृश्य बनाएगा।

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

इसलिए यह वास्तव में नहीं है कि आप राज्य को कैसे बनाए रखते हैं, बल्कि आप किस तरह से दृष्टिकोण को बनाए रखते हैं।


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

FirstFragment में आप मतदान का उपयोग कैसे कर रहे हैं? आपको यह करना होगा कि मैन्युअल रूप से दोनों खंड स्मृति में बने रहें। यदि आप आवश्यक कार्रवाई करने के लिए उनके उदाहरणों का उपयोग कर सकते हैं। सुराग का, मुख्य गतिविधि में एक घटना उत्पन्न करें जो दूसरा टुकड़ा जोड़ते समय कुछ करें। आशा है कि यह मदद करेगा।
कौशल त्रिवेदी

1
शुक्रिया के लिए धन्यवाद =)। मैंने ऐसा किया है। लेकिन क्या ऐसा करने का एकमात्र तरीका है? और एक उपयुक्त तरीका? इसके अलावा जब मैं होम बटन दबाता हूं और फिर से एप्लिकेशन लॉन्च करता हूं तो सभी टुकड़े फिर से सक्रिय हो जाते हैं। मान लीजिए कि इस तरह से बी बी फ्रैगमेंट बी में है। Activity A{Fragment A --> Fragment B}जब मैं होम बटन दबाने के बाद फिर से एप्लिकेशन लॉन्च करता हूं, तो दोनों को खंड onResume()कहा जाता है और इसलिए वे अपना मतदान शुरू करते हैं। मैं इसे कैसे नियंत्रित कर सकता हूं?
उमा

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

1
बेशक, आखिरी में मैं इस दृष्टिकोण के साथ नहीं जाने के लिए कहूंगा जब तक कि आप किसी अन्य समाधान को नहीं ढूंढते। तब तक इसे प्रबंधित करना मुश्किल है।
कौशल त्रिवेदी

7

मैं एक बहुत ही सरल उपाय सुझाऊंगा।

संदर्भ चर देखें और OnCreateView में दृश्य सेट करें। जांचें कि क्या दृश्य पहले से ही इस चर में मौजूद है, तो उसी दृश्य को वापस करें।

   private View fragmentView;

   public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        super.onCreateView(inflater, container, savedInstanceState);

        if (fragmentView != null) {
            return fragmentView;
        }
        View view = inflater.inflate(R.layout.yourfragment, container, false);
        fragmentView = view;
        return view;
    }

1
स्मृति रिसाव का एक मौका है यदि हमने onDestroy () में 'fragmentView' चर को नहीं हटाया है
अरुण पीएम

@ArunPM तो onDestroy () में फ्रेग्मेंट व्यू कैसे निकालें? if (_rootView.getParent() != null) { ((ViewGroup)_rootView.getParent()).removeView(_rootView); }स्मृति को साफ करने के लिए उपयुक्त है?
मेहमत गूर

1
@ MehmetGür मैं कई बार इस समाधान का उपयोग कर रहा हूं। अब तक मुझे कोई मेमोरी लीक त्रुटि नहीं मिली। लेकिन अगर आप चाहें तो अरुणपीएम समाधान का उपयोग कर सकते हैं। मुझे लगता है कि वह OnDestroy () मेथड में nulmentView को null करने के लिए कह रहा है।
मंदीप सिंह

1
मैं LeakCanary का उपयोग मेमोरी लीक का पता लगाने के लिए कर रहा हूं और जब मैंने इस विधि का पालन किया तो लीक होने वाले मुद्दों को फेंक दिया। लेकिन जैसे-जैसे @Mandeep टिप्पणी में आपका उल्लेख आह हम बताए द्वारा इस मुद्दे को दूर कर सकते हैं nullकरने के लिए fragmentView में चर onDestroy()विधि।
अरुण पीएम

1
मेरे ज्ञान के अनुसार, जब एक टुकड़ा नष्ट हो रहा होता है, तो टुकड़े से जुड़ा हुआ दृश्य साफ हो जाता है onDestroyView()। हमारे बैकअप दृश्य चर (यहां fragmentView ) के लिए यह क्लियरिंग नहीं हो रहा है और यह खंडित होने / नष्ट होने पर मेमोरी लीक का कारण होगा। आप LeakCanery परिचय में [स्मृति रिसाव के सामान्य कारणों] ( square.github.io/leakcanary/fundamentals/… ) में एक ही संदर्भ पा सकते हैं ।
अरुण पीएम

6

मैं इस समस्या को लेकर आया था जिसमें एक मानचित्र युक्त फ्रैगमेंट था, जिसमें बहुत सारे सेटअप विवरण हैं जिन्हें सहेजना / पुनः लोड करना है। मेरा समाधान मूल रूप से इस फ्रैगमेंट को पूरे समय सक्रिय रखने के लिए था (जैसा कि @kaushal ने उल्लेख किया है)।

मान लें कि आपके पास वर्तमान फ़्रैग्मेंट A है और वह फ़्रैगमेंट B को प्रदर्शित करना चाहता है। परिणामों को सारांशित करना:

  • बदलें () - फ्रैगमेंट ए को हटा दें और इसे फ्रैगमेंट बी के साथ बदलें। फ्रैगमेंट ए को एक बार फिर से सामने लाया जाएगा।
  • जोड़ें () - (बनाएं और) एक फ्रैगमेंट बी जोड़ें और यह फ्रैगमेंट ए को ओवरलैप करता है, जो अभी भी पृष्ठभूमि में सक्रिय है
  • remove () - Fragment B को हटाने के लिए इस्तेमाल किया जा सकता है और A. Fragment B पर वापस लौटने पर बाद में कॉल किया जाएगा

इसलिए, यदि आप दोनों फ्रेग्मेंट को "सहेजे" रखना चाहते हैं, तो उन्हें छिपाने () / शो () का उपयोग करके टॉगल करें।

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


क्या आप मुझे बता सकते हैं कि जब हम खंड बी को हटाते हैं और ए पर लौटते हैं तो फ्रैगमेंट ए में किस विधि को कहा जाता है? जब हम खंड बी को हटाते हैं तो मैं कुछ कार्रवाई करना चाहता हूं
Google

5

onSaveInstanceState() केवल तभी कहा जाता है जब कॉन्फ़िगरेशन परिवर्तन होता है।

एक टुकड़े से दूसरे में बदलने के बाद से कोई विन्यास परिवर्तन नहीं है इसलिए कोई कॉल नहीं onSaveInstanceState() है। किस राज्य को नहीं बचाया जा रहा है? क्या आप निर्दिष्ट कर सकते हैं?

यदि आप EditText में कुछ पाठ दर्ज करते हैं तो यह स्वचालित रूप से सहेजा जाएगा। बिना किसी आईडी के कोई भी यूआई आइटम ऐसा आइटम है, जिसका व्यू स्टेट सेव नहीं होगा।


onSaveInstanceState()यह भी कहा जाता है जब सिस्टम गतिविधि को नष्ट कर देता है क्योंकि इसमें संसाधनों की कमी होती है।
मार्सेल ब्रो

0

यहाँ, onSaveInstanceStateजब आप बैकस्टैक में खंड जोड़ते हैं तो खंड में कॉल नहीं करता है। बैकस्टैक में खंडित जीवनचक्र जब शुरू onCreateViewऔर अंत में बहाल होता है, onDestroyViewजबकि onSaveInstanceStateबीच में onDestroyViewऔर कहा जाता है onDestroy। मेरा समाधान उदाहरण चर और init में बना है onCreate। नमूना कोड:

private boolean isDataLoading = true;
private ArrayList<String> listData;
public void onCreate(Bundle savedInstanceState){
     super.onCreate(savedInstanceState);
     isDataLoading = false;
     // init list at once when create fragment
     listData = new ArrayList();
}

और इसमें जांच करें onActivityCreated:

public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    if(isDataLoading){
         fetchData();
    }else{
         //get saved instance variable listData()
    }
}

private void fetchData(){
     // do fetch data into listData
}

0
getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener()
    {
        @Override
        public void onBackStackChanged()
        {
            if (getSupportFragmentManager().getBackStackEntryCount() == 0)
            {
                //setToolbarTitle("Main Activity");
            }
            else
            {
                Log.e("fragment_replace11111", "replace");
            }
        }
    });


YourActivity.java
@Override
public void onBackPressed()
{
 Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.Fragment_content);
  if (fragment instanceof YourFragmentName)
    {
        fragmentReplace(new HomeFragment(),"Home Fragment");
        txt_toolbar_title.setText("Your Fragment");
    }
  else{
     super.onBackPressed();
   }
 }


public void fragmentReplace(Fragment fragment, String fragment_name)
{
    try
    {
        fragmentTransaction = fragmentManager.beginTransaction();
        fragmentTransaction.replace(R.id.Fragment_content, fragment, fragment_name);
        fragmentTransaction.setCustomAnimations(R.anim.enter_from_right, R.anim.exit_to_left, R.anim.enter_from_left, R.anim.exit_to_right);
        fragmentTransaction.addToBackStack(fragment_name);
        fragmentTransaction.commitAllowingStateLoss();
    }
    catch (Exception e)
    {
        e.printStackTrace();
    }
}

0

मेरी समस्या भी ऐसी ही थी लेकिन मैंने उस खंड को जीवित रखे बिना ही उस पर काबू पा लिया। मान लीजिए आपके पास एक गतिविधि है जिसमें 2 टुकड़े हैं - F1 और F2। एफ 1 को शुरू में शुरू किया गया है और इसमें कुछ उपयोगकर्ता जानकारी शामिल है और फिर कुछ शर्तो पर F2 पॉप से ​​उपयोगकर्ता को अतिरिक्त विशेषता भरने के लिए कहने पर कहते हैं - उनका फोन नंबर। इसके बाद, आप चाहते हैं कि फ़ोन नंबर F1 पर वापस आ जाए और साइनअप पूरा हो जाए, लेकिन आपको महसूस होता है कि पिछली सभी उपयोगकर्ता जानकारी खो गई है और आपके पास अपना पिछला डेटा नहीं है। टुकड़ा खरोंच से फिर से बनाया गया है और यहां तक ​​कि अगर आप onSaveInstanceStateबंडल में इस जानकारी को सहेजते हैं तो वापस शून्य में आता है onActivityCreated

समाधान: कॉलिंग गतिविधि में उदाहरण चर के रूप में आवश्यक जानकारी सहेजें। फिर उस उदाहरण चर को अपने टुकड़े में पास करें।

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);

    Bundle args = getArguments();

    // this will be null the first time F1 is created. 
    // it will be populated once you replace fragment and provide bundle data
    if (args != null) {
        if (args.get("your_info") != null) {
            // do what you want with restored information
        }
    }
}

इसलिए मेरे उदाहरण के साथ निम्नलिखित: एफ 2 प्रदर्शित करने से पहले मैं कॉलबैक का उपयोग करके उदाहरण चर में उपयोगकर्ता डेटा को बचाता हूं। फिर मैं F2 शुरू करता हूं, उपयोगकर्ता फोन नंबर में भरता है और प्रेस को बचाता है। मैं गतिविधि में एक और कॉलबैक का उपयोग करता हूं, इस जानकारी को इकट्ठा करता हूं और मेरे टुकड़े F1 को प्रतिस्थापित करता हूं, इस बार इसमें बंडल डेटा है जिसे मैं उपयोग कर सकता हूं।

@Override
public void onPhoneAdded(String phone) {
        //replace fragment
        F1 f1 = new F1 ();
        Bundle args = new Bundle();
        yourInfo.setPhone(phone);
        args.putSerializable("you_info", yourInfo);
        f1.setArguments(args);

        getFragmentManager().beginTransaction()
                .replace(R.id.fragmentContainer, f1).addToBackStack(null).commit();

    }
}

कॉलबैक के बारे में अधिक जानकारी यहां पाई जा सकती है: https://developer.android.com/training/basics/fragments/communicating.html


0

पहला : केवल FragmentTransaction वर्ग की प्रतिस्थापित विधि के बजाय ऐड मेथड का उपयोग करें फिर आपको addToBackStack मेथड द्वारा स्टैक के लिए सेकंडफ्रीगमेंट जोड़ना होगा

दूसरा : बैक पर आपको popBackStackImmediate () कॉल करना होगा

Fragment sourceFragment = new SourceFragment ();
final Fragment secondFragment = new SecondFragment();
final FragmentTransaction ft = getChildFragmentManager().beginTransaction();
ft.add(R.id.child_fragment_container, secondFragment );
ft.hide(sourceFragment );
ft.addToBackStack(NewsShow.class.getName());
ft.commit();
                                
((SecondFragment)secondFragment).backFragmentInstanceClick = new SecondFragment.backFragmentNewsResult()
{
        @Override
        public void backFragmentNewsResult()
        {                                    
            getChildFragmentManager().popBackStackImmediate();                                
        }
};

0

निम्नलिखित कोड का उपयोग करके एक टुकड़ा बदलें:

Fragment fragment = new AddPaymentFragment();
getSupportFragmentManager().beginTransaction().replace(R.id.frame, fragment, "Tag_AddPayment")
                .addToBackStack("Tag_AddPayment")
                .commit();

एक्टिविटी का ऑनबैकेपर्ड () है:

  @Override
public void onBackPressed() {
    android.support.v4.app.FragmentManager fm = getSupportFragmentManager();
    if (fm.getBackStackEntryCount() > 1) {

        fm.popBackStack();
    } else {


        finish();

    }
    Log.e("popping BACKSTRACK===> ",""+fm.getBackStackEntryCount());

}

0
Public void replaceFragment(Fragment mFragment, int id, String tag, boolean addToStack) {
        FragmentTransaction mTransaction = getSupportFragmentManager().beginTransaction();
        mTransaction.replace(id, mFragment);
        hideKeyboard();
        if (addToStack) {
            mTransaction.addToBackStack(tag);
        }
        mTransaction.commitAllowingStateLoss();
    }
replaceFragment(new Splash_Fragment(), R.id.container, null, false);

1
इस कोड स्निपेट के लिए धन्यवाद, जो कुछ सीमित, तत्काल सहायता प्रदान कर सकता है। एक उचित व्याख्या यह दर्शाती है कि समस्या का एक अच्छा समाधान क्यों है, यह दिखा कर इसके दीर्घकालिक मूल्य में बहुत सुधार होगा , और यह भविष्य के पाठकों को अन्य, समान प्रश्नों के साथ और अधिक उपयोगी बना देगा। कृपया कुछ स्पष्टीकरण जोड़ने के लिए अपने उत्तर को संपादित करें, जिसमें आपके द्वारा की गई धारणाएँ शामिल हैं।
मचावत

0

सही समाधान जो स्टैक में पुराने टुकड़े को ढूंढता है और यदि स्टैक में मौजूद है तो इसे लोड करें।

/**
     * replace or add fragment to the container
     *
     * @param fragment pass android.support.v4.app.Fragment
     * @param bundle pass your extra bundle if any
     * @param popBackStack if true it will clear back stack
     * @param findInStack if true it will load old fragment if found
     */
    public void replaceFragment(Fragment fragment, @Nullable Bundle bundle, boolean popBackStack, boolean findInStack) {
        FragmentManager fm = getSupportFragmentManager();
        FragmentTransaction ft = fm.beginTransaction();
        String tag = fragment.getClass().getName();
        Fragment parentFragment;
        if (findInStack && fm.findFragmentByTag(tag) != null) {
            parentFragment = fm.findFragmentByTag(tag);
        } else {
            parentFragment = fragment;
        }
        // if user passes the @bundle in not null, then can be added to the fragment
        if (bundle != null)
            parentFragment.setArguments(bundle);
        else parentFragment.setArguments(null);
        // this is for the very first fragment not to be added into the back stack.
        if (popBackStack) {
            fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
        } else {
            ft.addToBackStack(parentFragment.getClass().getName() + "");
        }
        ft.replace(R.id.contenedor_principal, parentFragment, tag);
        ft.commit();
        fm.executePendingTransactions();
    }

इसका उपयोग करें

Fragment f = new YourFragment();
replaceFragment(f, null, boolean true, true); 
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.