एंड्रॉइड टुकड़ा- एक खंड में विचारों के राज्यों को कैसे बचाया जाए जब एक और टुकड़ा उसके ऊपर धकेल दिया जाता है


137

Android में, एक टुकड़ा (कहते हैं FragA) बैकस्ट में जुड़ जाता है और दूसरा टुकड़ा (कहते हैं FragB) शीर्ष पर आ जाता है। अब मारने पर वापस FragAऊपर की तरफ आता है और उसे onCreateView()बुलाया जाता है। अब मैं FragAएक विशेष स्थिति में था इससे पहले कि FragBइसके शीर्ष पर धकेल दिया गया।

मेरा प्रश्न यह है कि मैं FragAअपनी पिछली स्थिति को कैसे पुनर्स्थापित कर सकता हूं ? क्या राज्य को बचाने का एक तरीका है (जैसे कि एक बंडल में कहें) और यदि ऐसा है तो मुझे किस विधि को ओवरराइड करना चाहिए?

जवाबों:


98

में टुकड़ा गाइड FragmentList उदाहरण आप पा सकते हैं:

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putInt("curChoice", mCurCheckPosition);
}

जिसे आप इस तरह बाद में उपयोग कर सकते हैं:

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    if (savedInstanceState != null) {
        // Restore last state for checked position.
        mCurCheckPosition = savedInstanceState.getInt("curChoice", 0);
    }
}

मैं खंडों में एक शुरुआत कर रहा हूँ, लेकिन यह आपकी समस्या का समाधान की तरह लगता है;) OnActivityCreated वापस स्टैक से टुकड़ा रिटर्न के बाद आह्वान किया गया है।


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

57
यह onSaveInstanceState को कॉल नहीं करता है - यह क्यों होगा? तो, यह दृष्टिकोण काम नहीं करता है।
आधी रात

10
क्या यह दृष्टिकोण वास्तव में उसी स्थिति में काम करेगा जब हम उसी गतिविधि में किसी अन्य खंड से लौटते समय खंड स्थिति को बनाए रखना चाहते हैं? onSaveInstanceState () को केवल गतिविधि onPause / onStop इवेंट्स कहा जाता है। दस्तावेज़ों के अनुसार: "एक गतिविधि की तरह, आप एक बंडल का उपयोग करके एक टुकड़े की स्थिति को बनाए रख सकते हैं, यदि गतिविधि की प्रक्रिया को मार दिया जाता है और गतिविधि के फिर से आने पर आपको टुकड़े की स्थिति को पुनर्स्थापित करने की आवश्यकता होती है। आप इस दौरान राज्य को बचा सकते हैं। टुकड़े के onSaveInstanceState () कॉलबैक और इसे onCreate (), onCreateView (), या onActivityCreated () के दौरान पुनर्स्थापित करें। "
परमवीर सिंह

26
रिकॉर्ड के लिए यह दृष्टिकोण गलत है और इसके पास अप-वोट के पास कोई जगह नहीं होनी चाहिए। onSaveInstanceStateकेवल तभी कहा जाता है जब यह संबंधित गतिविधि भी बंद हो रही हो।
मार्टिन कोंकनी

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

83

onSaveInstanceState(Bundle outState)जब तक टुकड़े की गतिविधि इसे स्वयं और संलग्न टुकड़ों को नहीं कहती तब तक फ्रेगमेंट को कभी नहीं कहा जाएगा। इस प्रकार इस विधि को तब तक नहीं बुलाया जाएगा जब तक कि कुछ (आमतौर पर रोटेशन) गतिविधि को बल न दे SaveInstanceStateऔर इसे बाद में पुनर्स्थापित न करे। लेकिन अगर आपके पास केवल एक गतिविधि है और इसके अंदर बड़े टुकड़े हैं (गहन उपयोग के साथ replace) और अनुप्रयोग केवल एक अभिविन्यास गतिविधि में चलता onSaveInstanceState(Bundle outState)है, तो इसे लंबे समय तक नहीं बुलाया जा सकता है।

मुझे पता है कि तीन संभावित वर्कअराउंड हैं।

सबसे पहला:

महत्वपूर्ण डेटा रखने के लिए टुकड़े के तर्कों का उपयोग करें:

public class FragmentA extends Fragment {
    private static final String PERSISTENT_VARIABLE_BUNDLE_KEY = "persistentVariable";

    private EditText persistentVariableEdit;

    public FragmentA() {
        setArguments(new Bundle());
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_a, null);

        persistentVariableEdit = (EditText) view.findViewById(R.id.editText);

        TextView proofTextView = (TextView) view.findViewById(R.id.textView);

        Bundle mySavedInstanceState = getArguments();
        String persistentVariable = mySavedInstanceState.getString(PERSISTENT_VARIABLE_BUNDLE_KEY);

        proofTextView.setText(persistentVariable);


        view.findViewById(R.id.btnPushFragmentB).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                getFragmentManager()
                        .beginTransaction()
                        .replace(R.id.frameLayout, new FragmentB())
                        .addToBackStack(null)
                        .commit();
            }
        });

        return view;
    }

    @Override
    public void onPause() {
        super.onPause();
        String persistentVariable = persistentVariableEdit.getText().toString();

        getArguments().putString(PERSISTENT_VARIABLE_BUNDLE_KEY, persistentVariable);
    }
}

दूसरा लेकिन कम पांडित्यपूर्ण तरीका - एकल में वैरिएबल धारण करें

तीसरा - नहीं replace()टुकड़े लेकिन add()/ show()/ hide()बजाय उन्हें।


3
सबसे अच्छा समाधान जब Fragment.onSaveInstanceState()कभी नहीं बुलाया गया था। अपने स्वयं के डेटा को तर्क में सहेजें, सूची दृश्य में आइटम या केवल उनकी आईडी (यदि आपके पास अन्य डेटा प्रबंधक है)। सूची दृश्य की स्थिति को सहेजने की आवश्यकता नहीं है - जो सहेजा गया था और स्वचालित रूप से पुनर्स्थापित करेगा।
जॉन पैंग

मैंने अपने ऐप में आपके उदाहरण का उपयोग करने की कोशिश की, लेकिन यह String persistentVariable = mySavedInstanceState.getString(PERSISTENT_VARIABLE_BUNDLE_KEY);हमेशा होता है null। समस्या क्या है?
फ्रागॉन

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

मैंने पहले दृष्टिकोण की कोशिश की लेकिन यह मेरे काम नहीं आया। getArguments () हमेशा शून्य रहता है। यह भी समझ में आता है क्योंकि टुकड़े को बदल दिया जाता है और ऑनक्रिएट () में आप एक नया बंडल सेट करते हैं ताकि पुराना बंडल खो जाए। मुझे क्या याद आ रहा है और क्या मैं गलत था?
ज़वी

@Zvi, मैंने यह कोड 1,5 साल पहले लिखा था और सभी विवरणों को याद नहीं है, लेकिन जैसा कि मुझे याद है, रिप्लेसमेंट टुकड़े को रिप्लेस नहीं करता है, केवल आपके कोड से नए इंस्ट्रूमेंट बनाए जाने पर ही फ्रेग्मेंट किया जाता है। इस मामले में, जाहिर है, निर्माता ने setArguments(new Bundle());पुराने बंडल को बुलाया और अधिलेखित किया। इसलिए सुनिश्चित करें कि आपने केवल एक बार ही टुकड़ा बनाया है, और फिर हर बार नया बनाने के बजाय इस उदाहरण का उपयोग करें।
फ्योडोर वोल्चोक

20

बस ध्यान दें कि यदि आप ViewPager का उपयोग करके फ्रेगमेंट के साथ काम करते हैं, तो यह बहुत आसान है। आपको केवल इस विधि को कॉल करने की आवश्यकता है setOffscreenPageLimit():।

डॉक्स के लिए असाइन करें:

निष्क्रिय अवस्था में दृश्य पदानुक्रम में वर्तमान पृष्ठ के दोनों ओर बनाए रखने वाले पृष्ठों की संख्या निर्धारित करें। इस सीमा से परे पृष्ठों को जरूरत पड़ने पर एडॉप्टर से फिर से बनाया जाएगा।

इसी तरह का मुद्दा यहाँ


5
यह भिन्न है। setOffScreenPageLimit कैश की तरह काम करता है (मतलब कि व्यू पेजर को एक निश्चित समय में कितने पेज को हैंडल करना है) लेकिन इसका उपयोग किसी फ्रैगमेंट की स्थिति को बचाने के लिए नहीं किया जाता है।
रेनॉड मैथ्यू

यह मेरा मामला यह setOffscreenPageLimit () के साथ काम करता है - हालांकि टुकड़े नष्ट हो गए थे, दृश्य स्थिति को बचाया और बहाल किया गया था।
डेविचो

धन्यवाद, मेरी भी मदद की।
एलिय्याह

वर्षों बाद और यह अभी भी इतना प्रासंगिक है। हालांकि यह वास्तव में सवाल का जवाब नहीं देता है, लेकिन यह एक समस्या हल करता है
सुप्रीम डॉल्फिन

19

बस एक बार के लिए अपने विचार को बढ़ाएँ।

छूट इस प्रकार है:

public class AFragment extends Fragment {

private View mRootView;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    if(mRootView==null){
        mRootView = inflater.inflate(R.id.fragment_a, container, false);
        //......
    }
    return mRootView;
}

}


सरणी या कुछ में मौजूदा टुकड़े भी रखना चाहिए
अमीर

मैंने सुना है कि एक खंड के rootView के संदर्भ को रखना बुरा अभ्यास है - जिसके परिणामस्वरूप लीक हो सकता है [उद्धरण वांछित]?
जिराफ.गुरु

1
@ giraffe.guru Fragmentअपने मूल दृष्टिकोण को संदर्भित करता है ऐसा नहीं करेगा। जबकि कुछ जीसी-रूट तत्वों द्वारा संदर्भ , वैश्विक स्थिर संपत्ति की तरह, गैर-यूआई धागा चर। एक Fragmentउदाहरण जीसी-रूट नहीं है, इसलिए यह कचरा एकत्र किया जा सकता है। तो इसका मूल दृश्य होगा।
लाइम ज़ॉय

तुम मेरा दिन वही हो।
विजेंद्र पाटीदार

मीठा और सरल।
रूपम दास

8

मैंने इसी के साथ एक मुद्दे पर काम किया। चूंकि मुझे पता था कि मैं अक्सर एक पिछले टुकड़े में वापस आ जाऊंगा, मैंने यह देखने के लिए जाँच किया कि क्या टुकड़ा .isAdded()सच था, और यदि ऐसा है, तो बजाय इसके कि transaction.replace()मैं सिर्फ एक काम करूं transaction.show()। यदि यह पहले से ही स्टैक पर है - तो खंड को फिर से बनाए रखने से बचा रहता है - कोई राज्य की बचत की आवश्यकता नहीं है।

Fragment target = <my fragment>;
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
if(target.isAdded()) {
    transaction.show(target);
} else {
    transaction.addToBackStack(button_id + "stack_item");
    transaction.replace(R.id.page_fragment, target);
}
transaction.commit();

ध्यान रखने वाली एक और बात यह है कि जब यह टुकड़ों के लिए प्राकृतिक क्रम को बनाए रखता है, तो आपको अभी भी खुद को नष्ट होने वाली गतिविधि को संभालने की आवश्यकता हो सकती है और ओरिएंटेशन (कॉन्फिग) परिवर्तन पर इसे फिर से बनाना होगा। अपने नोड के लिए AndroidManifest.xml में इसे पाने के लिए:

android:configChanges="orientation|screenSize"

एंड्रॉइड 3.0 और उच्चतर में, screenSizeजाहिरा तौर पर आवश्यक है।

सौभाग्य


transaction.addToBackStack (button_id + "stack_item"); // यह लाइन क्या करती है। यहां बटन_id क्या है?
रघु_ ३

button_id सिर्फ एक बना हुआ चर है। AddToBackStack में दिए गए स्ट्रिंग तर्क बैकस्टैक राज्य के लिए सिर्फ एक वैकल्पिक नाम है - यदि आप केवल एक बैकस्टैक का प्रबंधन कर रहे हैं, तो आप इसे शून्य पर सेट कर सकते हैं।
रमीराबेल

, मैं टुकड़े के साथ इस के समान एक समस्या हो रही है, क्या आप इसे देख सकते हैं- stackoverflow.com/questions/22468977/…
raghu_3

1
android:configChanges=everythinYouCanThinkOf|moreThingsYouFoundOnTheInternetsअपने प्रकट में कभी मत जोड़ो। इसके बजाय, उदाहरण के लिए, यहां से राज्य को बचाने और पुनर्स्थापित करने का तरीका जानें: स्पीकरडेक
Marcin Kozi'ski

और फिर एक बार आप सीखा है कैसे पागलपन की हद तक बोझल बचत राज्य है और कि समाधान अपने विशेष स्थिति में सबसे सफाई से समस्या का हल प्रस्तुत किया, उन लोगों से downvotes जो असहमत के लिए आगे जाना है और इसका इस्तेमाल चिंता के बिना, ;-)
rmirabelle

5

सबसे अच्छा समाधान मैंने पाया है:

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

OnDestroyView () के टुकड़े का उपयोग करें और उस विधि के अंदर पूरी वस्तु को बचाएं। फिर OnActivityCreated (): जांचें कि ऑब्जेक्ट शून्य है या नहीं (क्योंकि यह विधि हर बार कॉल करती है)। अब यहां किसी ऑब्जेक्ट की स्थिति को पुनर्स्थापित करें।

इसके काम हमेशा!


4

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

<activity
    android:name=".courses.posts.EditPostActivity"
    android:configChanges="keyboardHidden|orientation"
    android:screenOrientation="unspecified" />

तब onSaveInstanceStateटुकड़े का आह्वान नहीं किया जाएगा और savedInstanceStateवस्तु हमेशा अशक्त रहेगी।


1

i donot think onSaveInstanceStateएक अच्छा उपाय है। यह केवल उस गतिविधि के लिए उपयोग करता है जिसे नष्ट कर दिया गया था।

एंड्रॉइड 3.0 से FragmenManager द्वारा Fragmen के प्रबंधक रहे हैं, शर्त है: मैन्नी के टुकड़े को मैप करते समय एक गतिविधि, जब टुकड़ा जोड़ा जाता है (प्रतिस्थापित नहीं: इसे फिर से बनाया जाएगा) बैकस्टैक में, दृश्य को नष्ट कर दिया जाएगा। पिछले एक पर वापस आने पर, यह पहले की तरह प्रदर्शित होगा।

इसलिए मुझे लगता है कि टुकड़ा और लेनदेन इसे संभालने के लिए काफी अच्छा है।


0

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

public void addFragment(Fragment currentFragment, Fragment targetFragment, String tag) {
    FragmentManager fragmentManager = getSupportFragmentManager();
    FragmentTransaction transaction = fragmentManager.beginTransaction();
    transaction.setCustomAnimations(0,0,0,0);
    transaction.hide(currentFragment);
    // use a fragment tag, so that later on we can find the currently displayed fragment
    transaction.add(R.id.frame_layout, targetFragment, tag)
            .addToBackStack(tag)
            .commit();
}

जब भी कोई सूची आइटम क्लिक / टैप किया जाता है (और इस प्रकार मुझे विवरण को लॉन्च करने / प्रदर्शित करने की आवश्यकता होती है) तो मैं इस विधि का उपयोग अपने टुकड़े (सूची दृश्य से युक्त) में करता हूं:

FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
SearchFragment currentFragment = (SearchFragment) fragmentManager.findFragmentByTag(getFragmentTags()[0]);
DetailsFragment detailsFragment = DetailsFragment.newInstance("some object containing some details");
((MainActivity) getActivity()).addFragment(currentFragment, detailsFragment, "Details");

getFragmentTags()जब मैं एक नया अंश जोड़ता हूं, तो मैं अलग-अलग अंशों के टैग के रूप में उपयोग किए जाने वाले स्ट्रिंग की एक सरणी देता हूं ( ऊपर transaction.addविधि में addFragmentविधि देखें )।

सूची दृश्य वाले टुकड़े में, मैं इसे अपने ऑनपॉज़ () विधि में करता हूँ:

@Override
public void onPause() {
    // keep the list view's state in memory ("save" it) 
    // before adding a new fragment or replacing current fragment with a new one
    ListView lv =  (ListView) getActivity().findViewById(R.id.listView);
    mListViewState = lv.onSaveInstanceState();
    super.onPause();
}

तब खंड के onCreateView में (वास्तव में onCreateView में लागू किया गया है कि एक विधि में), मैं बहाल कर रहा हूँ:

// Restore previous state (including selected item index and scroll position)
if(mListViewState != null) {
    Log.d(TAG, "Restoring the listview's state.");
    lv.onRestoreInstanceState(mListViewState);
}

0

अंत में इन जटिल समाधानों में से कई को आजमाने के बाद, क्योंकि मुझे केवल अपने फ्रैगमेंट (एक एडिटटेक्स्ट की सामग्री) में एक ही मूल्य को बचाने / पुनर्स्थापित करने की आवश्यकता थी, और हालांकि यह एक सबसे अच्छा समाधान नहीं हो सकता है, एक साझाकरण बनाकर मेरी स्थिति को रोक देता है वहाँ मेरे लिए काम किया


0

एक गतिविधि में विभिन्न टुकड़ों में फ़ील्ड के मूल्यों को रखने का एक सरल तरीका

खंडों के उदाहरण बनाएँ और प्रतिस्थापित करने और हटाने के बजाय जोड़ें

    FragA  fa= new FragA();
    FragB  fb= new FragB();
    FragC  fc= new FragB();
    fragmentManager = getSupportFragmentManager();
    fragmentTransaction = fragmentManager.beginTransaction();
    fragmentTransaction.add(R.id.fragmnt_container, fa);
    fragmentTransaction.add(R.id.fragmnt_container, fb);
    fragmentTransaction.add(R.id.fragmnt_container, fc);
    fragmentTransaction.show(fa);
    fragmentTransaction.hide(fb);
    fragmentTransaction.hide(fc);
    fragmentTransaction.commit();

फिर बस फिर से जोड़ने और हटाने के बजाय टुकड़ों को दिखाएं और छिपाएं

    fragmentTransaction = fragmentManager.beginTransaction();
    fragmentTransaction.hide(fa);
    fragmentTransaction.show(fb);
    fragmentTransaction.hide(fc);
    fragmentTransaction.commit()

;


-1
private ViewPager viewPager;
viewPager = (ViewPager) findViewById(R.id.pager);
mAdapter = new TabsPagerAdapter(getSupportFragmentManager());
viewPager.setAdapter(mAdapter);
viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {

        @Override
        public void onPageSelected(int position) {
            // on changing the page
            // make respected tab selected
            actionBar.setSelectedNavigationItem(position);
        }

        @Override
        public void onPageScrolled(int arg0, float arg1, int arg2) {
        }

        @Override
        public void onPageScrollStateChanged(int arg0) {
        }
    });
}

@Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
}

@Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
    // on tab selected
    // show respected fragment view
    viewPager.setCurrentItem(tab.getPosition());
}

@Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
}

5
कृपया कोड को पोस्ट करने के बजाय अपने उत्तर के बारे में कुछ जानकारी सहित विचार करें। हम न केवल 'सुधार' प्रदान करने की कोशिश करते हैं, बल्कि लोगों को सीखने में मदद करते हैं। आपको यह बताना चाहिए कि मूल कोड में क्या गलत था, आपने अलग-अलग क्या किया और आपके परिवर्तन ने क्या काम किया।
एंड्रयू बार्बर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.