android - एक एक बार सभी संग बीती भजन




एक बार सभी के लिए, बैक स्टैक में उदाहरण की स्थिति को सही तरीके से कैसे सहेजना है? (4)

मुझे SO पर एक समान प्रश्न के कई उदाहरण मिले हैं लेकिन दुर्भाग्यवश कोई जवाब मेरी आवश्यकताओं को पूरा नहीं करता है।

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

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

TextView vstup;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.whatever);
    vstup = (TextView)findViewById(R.id.whatever);
    /* (...) */
}

@Override
public void onSaveInstanceState(Bundle state) {
    super.onSaveInstanceState(state);
    state.putCharSequence(App.VSTUP, vstup.getText());
}

@Override
public void onRestoreInstanceState(Bundle state) {
    super.onRestoreInstanceState(state);
    vstup.setText(state.getCharSequence(App.VSTUP));
}

Fragment साथ, यह केवल बहुत विशिष्ट स्थितियों में काम करता है। विशेष रूप से, जो खंड टूटता है वह एक टुकड़े को बदलता है, इसे पीछे की ढेर में डालता है और फिर नया टुकड़ा दिखाए जाने पर स्क्रीन को घूर्णन करता है। जो मैंने समझा, उससे पुराने खंड को प्रतिस्थापित होने पर onSaveInstanceState() कॉल प्राप्त नहीं होता है, लेकिन किसी भी तरह से Activity से जुड़ा रहता है और इस विधि को बाद में तब कहा जाता है जब उसका View अब मौजूद नहीं होता है, इसलिए मेरे किसी भी onSaveInstanceState() View परिणाम की तलाश में एक NullPointerException

साथ ही, मैंने पाया कि मेरे TextViews के संदर्भ को रखना Fragment साथ अच्छा विचार नहीं है, भले ही यह Activity साथ ठीक था। उस स्थिति में, onSaveInstanceState() वास्तव में राज्य को बचाता है लेकिन अगर समस्या छिपी हुई है तो स्क्रीन दो बार घुमाएगी, तो समस्या फिर से दिखाई onSaveInstanceState() , onSaveInstanceState() इसके onSaveInstanceState() को नए उदाहरण में नहीं कहा जाता है।

मैंने कुछ Bundle टाइप क्लास सदस्य तत्व (यह वास्तव में अधिक डेटा है, न केवल एक onSaveInstanceState() onDestroyView() में राज्य को सहेजने और इसे सहेजने के बारे में सोचा था, और इसे सहेजने के लिए onSaveInstanceState() पर अन्य दोष हैं। मुख्य रूप से, अगर खंड वर्तमान में दिखाया गया है, तो दो कार्यों को कॉल करने का क्रम उलट दिया गया है, इसलिए मुझे दो अलग-अलग स्थितियों के लिए खाते की आवश्यकता होगी। एक क्लीनर और सही समाधान होना चाहिए!


इस तरह मैं इस पल में उपयोग कर रहा हूं ... यह बहुत जटिल है लेकिन कम से कम यह सभी संभावित परिस्थितियों को संभालता है। अगर कोई दिलचस्पी लेता है।

public final class MyFragment extends Fragment {
    private TextView vstup;
    private Bundle savedState = null;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.whatever, null);
        vstup = (TextView)v.findViewById(R.id.whatever);

        /* (...) */

        /* If the Fragment was destroyed inbetween (screen rotation), we need to recover the savedState first */
        /* However, if it was not, it stays in the instance from the last onDestroyView() and we don't want to overwrite it */
        if(savedInstanceState != null && savedState == null) {
            savedState = savedInstanceState.getBundle(App.STAV);
        }
        if(savedState != null) {
            vstup.setText(savedState.getCharSequence(App.VSTUP));
        }
        savedState = null;

        return v;
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        savedState = saveState(); /* vstup defined here for sure */
        vstup = null;
    }

    private Bundle saveState() { /* called either from onDestroyView() or onSaveInstanceState() */
        Bundle state = new Bundle();
        state.putCharSequence(App.VSTUP, vstup.getText());
        return state;
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        /* If onDestroyView() is called first, we can use the previously savedState but we can't call saveState() anymore */
        /* If onSaveInstanceState() is called first, we don't have savedState, so we need to call saveState() */
        /* => (?:) operator inevitable! */
        outState.putBundle(App.STAV, (savedState != null) ? savedState : saveState());
    }

    /* (...) */

}

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


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

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

यहाँ एक उदाहरण है:

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

    if (savedInstanceState == null) {
        myFragment = MyFragment.newInstance();
        getSupportFragmentManager()
                .beginTransaction()
                .add(R.id.my_container, myFragment, MY_FRAGMENT_TAG)
                .commit();
    } else {
        myFragment = (MyFragment) getSupportFragmentManager()
                .findFragmentByTag(MY_FRAGMENT_TAG);
    }
...
}

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


Fragment की आवृत्ति स्थिति को सही ढंग से सहेजने के लिए आपको निम्न कार्य करना चाहिए:

1. खंड में, onSaveInstanceState() ओवरराइड करके इंस्टेंस स्थिति को सहेजें और onSaveInstanceState() पुनर्स्थापित करें:

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    ...
    if (savedInstanceState != null) {
        //Restore the fragment's state here
    }
}
...
@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);

    //Save the fragment's state here
}

2. और महत्वपूर्ण बिंदु , गतिविधि में, आपको खंड में इंस्टेंस को onSaveInstanceState() में onSaveInstanceState() और onSaveInstanceState() पुनर्स्थापित करना होगा।

public void onCreate(Bundle savedInstanceState) {
    ...
    if (savedInstanceState != null) {
        //Restore the fragment's instance
        mContent = getSupportFragmentManager().getFragment(savedInstanceState, "myFragmentName");
        ...
    }
    ...
}

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);

    //Save the fragment's instance
    getSupportFragmentManager().putFragment(outState, "myFragmentName", mContent);
}

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


DroidT के लिए धन्यवाद, मैंने यह बनाया:

मुझे एहसास है कि अगर FragmentCreateView () पर निष्पादित नहीं होता है, तो इसका दृश्य तत्काल नहीं है। इसलिए, यदि बैक स्टैक पर टुकड़े अपने विचार नहीं बनाते हैं, तो मैं अंतिम संग्रहीत स्थिति को सहेजता हूं, अन्यथा मैं उस डेटा के साथ अपना स्वयं का बंडल बनाता हूं जिसे मैं सहेजना / पुनर्स्थापित करना चाहता हूं।

1) इस वर्ग को बढ़ाएं:

import android.os.Bundle;
import android.support.v4.app.Fragment;

public abstract class StatefulFragment extends Fragment {

    private Bundle savedState;
    private boolean saved;
    private static final String _FRAGMENT_STATE = "FRAGMENT_STATE";

    @Override
    public void onSaveInstanceState(Bundle state) {
        if (getView() == null) {
            state.putBundle(_FRAGMENT_STATE, savedState);
        } else {
            Bundle bundle = saved ? savedState : getStateToSave();

            state.putBundle(_FRAGMENT_STATE, bundle);
        }

        saved = false;

        super.onSaveInstanceState(state);
    }

    @Override
    public void onCreate(Bundle state) {
        super.onCreate(state);

        if (state != null) {
            savedState = state.getBundle(_FRAGMENT_STATE);
        }
    }

    @Override
    public void onDestroyView() {
        savedState = getStateToSave();
        saved = true;

        super.onDestroyView();
    }

    protected Bundle getSavedState() {
        return savedState;
    }

    protected abstract boolean hasSavedState();

    protected abstract Bundle getStateToSave();

}

2) आपके टुकड़े में, आपके पास यह होना चाहिए:

@Override
protected boolean hasSavedState() {
    Bundle state = getSavedState();

    if (state == null) {
        return false;
    }

    //restore your data here

    return true;
}

3) उदाहरण के लिए, आप atSavedState पर isSavedState को कॉल कर सकते हैं:

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

    if (hasSavedState()) {
        return;
    }

    //your code here
}




android-fragments