title: Android Compound Custom View restore save state
body: <p>I created compound custom view which contains <code>TextView</code> and <code>EditText</code> called <code>LabledEditText</code>, since I will have a lot of EditText fields in a fragment.</p>
  
  <p>I have created an XML file that holds the following </p>
  
  <pre><code>&lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
      android:id="@+id/linearLayoutLabeledEditText"
      android:orientation="vertical"
      android:layout_width="match_parent"
      android:layout_height="match_parent"&gt;
      &lt;TextView
          android:id="@+id/label_textView"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:text="@string/label"
          android:textAppearance="?android:attr/textAppearanceMedium" /&gt;
  
      &lt;EditText
          android:id="@+id/value_editText"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:freezesText="true"
          android:saveEnabled="true"/&gt;
  &lt;/LinearLayout&gt;
  </code></pre>
  
  <p>and in the View class is as following</p>
  
  <pre><code>public class LabeledEditText extends LinearLayout {
      private EditText editText;
      private TextView label;
  
      public LabeledEditText(Context context, AttributeSet attrs) {
          super(context, attrs);
          inflate(context,R.layout.labeled_edit_text, this);
          label = (TextView) this.findViewById(R.id.label_textView);
          label.setText("some label");
          editText = (EditText) this.findViewById(R.id.value_editText);
      }
  
      protected void onRestoreInstanceState(Parcelable state) {
          String id= this.getId()+" ";
          if (state instanceof Bundle) // implicit null check
          {
              Bundle bundle = (Bundle) state;
              state = bundle.getParcelable(id+"super");
              super.onRestoreInstanceState(state);
              editText.setText(bundle.getString(id+"editText"));
          }
      }
  
      protected Parcelable onSaveInstanceState() {
          String id= this.getId()+" ";
          Bundle bundle = new Bundle();
          bundle.putParcelable(id+"super",super.onSaveInstanceState());
          bundle.putString(id+"editText",editText.getText().toString());
          return bundle;
      }   
  }
  </code></pre>
  
  <p>then I use it in a 3 fragments that represent 3 steps. When I insert values in the first 1 step/fragment </p>
  
  <p><a href="https://i.stack.imgur.com/SPSVv.png" rel="noreferrer"><img src="https://i.stack.imgur.com/SPSVv.png" alt="1st time after visiting the first step"></a></p>
  
  <p>then switch to other fragments and return to the 1 step/fragment again I find the following</p>
  
  <p><a href="https://i.stack.imgur.com/8wuqi.png" rel="noreferrer"><img src="https://i.stack.imgur.com/8wuqi.png" alt="2nd time visiting the first step"></a></p>
  
  <p>what is causing this problem ?</p>
  
  <p>I have been debugging it for at least 5 days, keeping in mind that each of those custom views has different id when used inside the fragment layout.</p>
  
  <p>I also have tried to add the id of the custom view as part of the key during saving the state <code>this.getId()+"editText"</code> still the same problem.</p>
  
  <p><strong>EDIT</strong>
  the <a href="https://stackoverflow.com/questions/6790623/programmatic-views-how-to-set-unique-ids">genrateViewId</a> for api &lt; 17 </p>
  
  <p>the code after alteration</p>
  
  <pre><code>import java.util.concurrent.atomic.AtomicInteger;
  
  public class LabeledEditText extends LinearLayout {
      private EditText editText;
      private TextView label;
  
      public LabeledEditText(Context context, AttributeSet attrs) {
          super(context, attrs);
          inflate(context,R.layout.labeled_edit_text, this);
          label = (TextView) this.findViewById(R.id.label_textView);
          editText = (EditText) this.findViewById(R.id.value_editText);
          editText.setId(generateViewId());
          applyAttr(context,attrs);
      }
  
      @Override
      protected Parcelable onSaveInstanceState() {
          Bundle bundle = new Bundle();
          //adding the id of the parent view as part of the key so that
          //editText state won't get overwritten by other editText 
          //holding the same id
          bundle.putParcelable("super",super.onSaveInstanceState());
          bundle.putString("editText",editText.getText().toString());
          return bundle;
      }
  
      @Override
      protected void onRestoreInstanceState(Parcelable state) {
          if (state instanceof Bundle) // implicit null check
          {
              Bundle bundle = (Bundle) state;
              state = bundle.getParcelable("super");
              super.onRestoreInstanceState(state);
              editText.setText(bundle.getString("editText"));
          }
      }
  
      private static final AtomicInteger sNextGeneratedId = new AtomicInteger(1);
  
      public static int generateViewId() {
          for (;;) {
              final int result = sNextGeneratedId.get();
              // aapt-generated IDs have the high byte nonzero; clamp to the range under that.
              int newValue = result + 1;
              if (newValue &gt; 0x00FFFFFF) newValue = 1; // Roll over to 1, not 0.
              if (sNextGeneratedId.compareAndSet(result, newValue)) {
                  return result;
              }
          }
      }
  }
  </code></pre>