btalb
btalb

Reputation: 7037

Correct way to save and restore state of multiple versions of the one layout

Consider the following overly simplified example. I have a LinearLayout with a list of items (EditTexts). The number of items is not known at compile time (for example it might be a game menu screen where the user has just selected the number of players and on this screen they are entering each individual player's name).

The obvious way to do this is to have a container layout defined in xml (R.layout.list), and individual item layouts defined in xml (R.layout.item); which would look something like this:

public class Main extends Activity {
    private static final String FRAG_TAG = "ITEMS_FRAGMENT";
    private static final int NUM_ITEMS = 3;

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

        LinearLayout ll = (LinearLayout) getLayoutInflater().inflate(R.layout.list, null, false);
        for (int i = 0; i<NUM_ITEMS; i++) {
            ll.addView(getLayoutInflater().inflate(R.layout.item, null, false));
        }
        setContentView(ll);
    }
}

With the R.layout.list and R.layout.item layouts respectively:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/list" />
<EditText xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/item"
    android:singleLine="true"/>

The problem is that on an orientation change, the fact that all items have the same ID (R.id.item) confuses the restoration process. The end result is that the state of the last item is restored to all items in the list (i.e. if the last item's EditText has "hi" in it, every EditText will have "hi" in it after the orientation chance).

In the instance of this in my code, each item has a CheckBox that I can get the state of with a isCheckboxChecked(LinearLayout list, int itemNumber) function that relies on the fact that each item has a view with ID R.id.checkbox. Is there a proper way to do view state restoration that accepts there may be multiple items inflated from the same XML resource (and consequently will have the same ID)?

(ListView is not appropriate for this case, but it handles this correctly. How does it restore state correctly when all of it's items typically have the same ID?)

Upvotes: 2

Views: 944

Answers (1)

sergej shafarenka
sergej shafarenka

Reputation: 20406

Android stores states by associating them with element's ID. If all elements have same ID, then a state associated with this ID will be restored to all elements with this ID. LisView does it very differently. It always reads values from Adapter by element position. Because views with the same ID will have different position in the list, they will have different values too.

If your case you have couple of options.

1) You need to assign different ID'S to your items before adding them to the LinearLayout by calling setId() method. You need to define those ID's in Android resources firs (e.g. <item type="id" name="item_01" /> etc.).

2) You need to override onSaveInstanceState(Bundle outState), go through the children of LinearLayout and store their texts into outState bundle associating text with children's index. Then in onRestoreInstanceState() you need to read states from savedInstanceState and set them to your views correspondingly.

3) You use ListView and when user enters text, your adapter persists data (e.g. into a file or database). When configuration changes, or on the next start of Activity your adapter will read those stored values, and they will be restored properly.

Hope one of the options can help you.

Upvotes: 4

Related Questions