android - स्क्रीन अभिविन्यास परिवर्तनों में राज्य खोने से कस्टम दृश्यों को कैसे रोकें




android-activity state (3)

आप इसे View#onSaveInstanceState को लागू View#onSaveInstanceState और View#onSaveInstanceState View#onRestoreInstanceState और View.BaseSavedState क्लास को विस्तारित करते हुए करते हैं।

public class CustomView extends View {

  private int stateToSave;

  ...

  @Override
  public Parcelable onSaveInstanceState() {
    //begin boilerplate code that allows parent classes to save state
    Parcelable superState = super.onSaveInstanceState();

    SavedState ss = new SavedState(superState);
    //end

    ss.stateToSave = this.stateToSave;

    return ss;
  }

  @Override
  public void onRestoreInstanceState(Parcelable state) {
    //begin boilerplate code so parent classes can restore state
    if(!(state instanceof SavedState)) {
      super.onRestoreInstanceState(state);
      return;
    }

    SavedState ss = (SavedState)state;
    super.onRestoreInstanceState(ss.getSuperState());
    //end

    this.stateToSave = ss.stateToSave;
  }

  static class SavedState extends BaseSavedState {
    int stateToSave;

    SavedState(Parcelable superState) {
      super(superState);
    }

    private SavedState(Parcel in) {
      super(in);
      this.stateToSave = in.readInt();
    }

    @Override
    public void writeToParcel(Parcel out, int flags) {
      super.writeToParcel(out, flags);
      out.writeInt(this.stateToSave);
    }

    //required field that makes Parcelables from a Parcel
    public static final Parcelable.Creator<SavedState> CREATOR =
        new Parcelable.Creator<SavedState>() {
          public SavedState createFromParcel(Parcel in) {
            return new SavedState(in);
          }
          public SavedState[] newArray(int size) {
            return new SavedState[size];
          }
    };
  }
}

कार्य दृश्य और दृश्य की सहेजे गए वर्ग वर्ग के बीच विभाजित है। आपको SavedState स्तर में Parcel को पढ़ने और लिखने के सभी काम करना चाहिए। फिर आपकी व्यू क्लास राज्य के सदस्यों को निकालने और कक्षा को वैध राज्य में वापस लाने के लिए आवश्यक कार्य करने का काम कर सकती है।

नोट्स: View#onSavedInstanceState View#onRestoreInstanceState और View#onRestoreInstanceState स्वचालित रूप से आपके लिए बुलाया जाता है यदि View#getId एक मान देता है> = 0. ऐसा तब होता है जब आप इसे xml में आईडी देते हैं या मैन्युअल रूप से कॉल setId । अन्यथा आपको View#onSaveInstanceState पर कॉल करना होगा और पार्सल को उस स्थिति में वापस भेजना होगा जिसे आप Activity#onSaveInstanceState पर सहेजते हैं, राज्य को सहेजने के लिए SaveInstanceState पर वापस Activity#onSaveInstanceState और बाद में इसे पढ़ते हैं और Activity#onRestoreInstanceState View#onRestoreInstanceState को View#onRestoreInstanceState लिए पास करते हैं।

इसका एक और सरल उदाहरण CompoundButton

स्क्रीन अभिविन्यास परिवर्तनों में कुछ महत्वपूर्ण घटकों को सहेजने और पुनर्स्थापित करने के लिए मैंने अपनी मुख्य Activity लिए onRetainNonConfigurationInstance() सफलतापूर्वक कार्यान्वित किया है।

लेकिन ऐसा लगता है, जब अभिविन्यास बदलता है तो मेरे कस्टम विचार खरोंच से फिर से बनाए जा रहे हैं। यह समझ में आता है, हालांकि मेरे मामले में यह असुविधाजनक है क्योंकि सवाल में कस्टम दृश्य एक एक्स / वाई प्लॉट है और प्लॉट किए गए अंक कस्टम व्यू में संग्रहीत हैं।

कस्टम व्यू के लिए onRetainNonConfigurationInstance() समान कुछ लागू करने का कोई चालाक तरीका है, या क्या मुझे कस्टम दृश्य में विधियों को लागू करने की आवश्यकता है जो मुझे "राज्य" प्राप्त करने और सेट करने की अनुमति देता है?


मुझे लगता है कि यह एक बहुत ही सरल संस्करण है। Bundle एक अंतर्निहित प्रकार है जो Parcelable लागू Parcelable

public class CustomView extends View
{
  private int stuff; // stuff

  @Override
  public Parcelable onSaveInstanceState()
  {
    Bundle bundle = new Bundle();
    bundle.putParcelable("superState", super.onSaveInstanceState());
    bundle.putInt("stuff", this.stuff); // ... save stuff 
    return bundle;
  }

  @Override
  public void onRestoreInstanceState(Parcelable state)
  {
    if (state instanceof Bundle) // implicit null check
    {
      Bundle bundle = (Bundle) state;
      this.stuff = bundle.getInt("stuff"); // ... load stuff
      state = bundle.getParcelable("superState");
    }
    super.onRestoreInstanceState(state);
  }
}

यहां जवाब पहले से ही महान हैं, लेकिन कस्टम व्यू ग्रुप के लिए जरूरी नहीं है। अपने राज्य को बनाए रखने के लिए सभी कस्टम दृश्य प्राप्त करने के लिए, आपको प्रत्येक कक्षा में onSaveInstanceState() और onRestoreInstanceState(Parcelable state) ओवरराइड करना होगा। आपको यह सुनिश्चित करने की भी आवश्यकता है कि उनके पास अद्वितीय आईडी हों, चाहे वे xml से फुले हों या प्रोग्रामेटिक रूप से जोड़े गए हों।

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

मैटो द्वारा साझा किया गया लिंक काम करेगा, लेकिन इसका मतलब है कि कोई भी व्यक्तिगत दृश्य अपने स्वयं के राज्य का प्रबंधन नहीं करता है - संपूर्ण राज्य ViewGroup विधियों में सहेजा जाता है।

समस्या यह है कि जब इनमें से कई व्यू समूह को लेआउट में जोड़ा जाता है, तो XML से उनके तत्वों की आईडी अब अद्वितीय नहीं होती है (यदि यह xml में परिभाषित होती है)। रनटाइम पर, आप दृश्य के लिए एक अद्वितीय आईडी प्राप्त करने के लिए स्थिर विधि View.generateViewId() को कॉल कर सकते हैं। यह केवल एपीआई 17 से उपलब्ध है।

व्यू ग्रुप से मेरा कोड यहां है (यह सार है, और mOriginalValue एक प्रकार परिवर्तनीय है):

public abstract class DetailRow<E> extends LinearLayout {

    private static final String SUPER_INSTANCE_STATE = "saved_instance_state_parcelable";
    private static final String STATE_VIEW_IDS = "state_view_ids";
    private static final String STATE_ORIGINAL_VALUE = "state_original_value";

    private E mOriginalValue;
    private int[] mViewIds;

// ...

    @Override
    protected Parcelable onSaveInstanceState() {

        // Create a bundle to put super parcelable in
        Bundle bundle = new Bundle();
        bundle.putParcelable(SUPER_INSTANCE_STATE, super.onSaveInstanceState());
        // Use abstract method to put mOriginalValue in the bundle;
        putValueInTheBundle(mOriginalValue, bundle, STATE_ORIGINAL_VALUE);
        // Store mViewIds in the bundle - initialize if necessary.
        if (mViewIds == null) {
            // We need as many ids as child views
            mViewIds = new int[getChildCount()];
            for (int i = 0; i < mViewIds.length; i++) {
                // generate a unique id for each view
                mViewIds[i] = View.generateViewId();
                // assign the id to the view at the same index
                getChildAt(i).setId(mViewIds[i]);
            }
        }
        bundle.putIntArray(STATE_VIEW_IDS, mViewIds);
        // return the bundle
        return bundle;
    }

    @Override
    protected void onRestoreInstanceState(Parcelable state) {

        // We know state is a Bundle:
        Bundle bundle = (Bundle) state;
        // Get mViewIds out of the bundle
        mViewIds = bundle.getIntArray(STATE_VIEW_IDS);
        // For each id, assign to the view of same index
        if (mViewIds != null) {
            for (int i = 0; i < mViewIds.length; i++) {
                getChildAt(i).setId(mViewIds[i]);
            }
        }
        // Get mOriginalValue out of the bundle
        mOriginalValue = getValueBackOutOfTheBundle(bundle, STATE_ORIGINAL_VALUE);
        // get super parcelable back out of the bundle and pass it to
        // super.onRestoreInstanceState(Parcelable)
        state = bundle.getParcelable(SUPER_INSTANCE_STATE);
        super.onRestoreInstanceState(state);
    } 
}






screen-orientation