Сохранить состояние на экране поворота, когда составной компонент используется несколько раз - PullRequest
3 голосов
/ 29 марта 2012

Я столкнулся с проблемой, когда я знаю причину, но не вижу способа ее исправить.Если пользовательский составной компонент используется в действии несколько раз, значения, сохраненные в представлениях, будут перезаписывать друг друга.Чтобы объяснить это проще, я сделал следующий пример.

xml для нового компонента, только EditText, чтобы сделать его короче.

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android" >

    <EditText
        android:id="@+id/custom_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ems="10"
        android:inputType="number" >
    </EditText>

</merge>

Класс, реализующий новое поведение, только раздувающийlayout.

public class CustomView extends LinearLayout {
    public CustomView(Context context) {
        this(context, null);
    }

    public CustomView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CustomView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        LayoutInflater inflater = (LayoutInflater) context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        inflater.inflate(R.layout.custom_view, this, true);
    }
}

И макет с использованием 2 из них.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <test.customview.CustomView
        android:id="@+id/customView1"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" >
    </test.customview.CustomView>

    <test.customview.CustomView
        android:id="@+id/customView2"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" >
    </test.customview.CustomView>

</LinearLayout>

При повороте экрана значение из второго просмотра также восстанавливается в первом.

Копаясь в коде фреймворка, я обнаружил, что объекты Parcelable, возвращаемые из onSaveInstanceState, определенного в классе View, помещаются в SparseArray с идентификатором ключевого объекта.Поскольку я включаю CustomView несколько раз, EditText с идентификатором «custom_text» также добавляется несколько раз.Имея тот же идентификатор, сохраненные значения будут перезаписывать друг друга.

Я ищу любые предложения о том, как это должно быть на самом деле реализовано.Сейчас я не вижу способа изменить эти идентификаторы.

1 Ответ

2 голосов
/ 03 апреля 2012

Похоже, у меня есть какое-то решение этой проблемы.Я пытаюсь найти его в течение некоторого времени.

1. Сначала вы должны создать внутренний класс, который расширяет BaseSavedState, внутри вашего CustomView.

CustomView{
     String value;  //some text value from edittext 

     EditText edittext;
      ...

     private static class Save extends BaseSavedState{

    String savedValue;

    public Save(Parcel incoming) {
        super(incoming);
        savedValue = incoming.readString();
        Log.i("Save", "Parcel");
    }

    public Save(Parcelable parcelable) {
        super(parcelable);
        Log.i("Save", "Parcelable");
    }

    @Override
    public void writeToParcel(Parcel outcoming, int flags) {
        super.writeToParcel(outcoming, flags);
        outcoming.writeString(savedValue );
        Log.i("Save", "writeToParcel");
    }

    public static final Parcelable.Creator<Save> CREATOR =
                 new Creator<CustomView.Save>() {

        @Override
        public Save[] newArray(int size) {
            Log.i("Parcelable.Creator<Save>", "newArray");
            return new Save[size];
        }

        @Override
        public Save createFromParcel(Parcel incoming) {
            Log.i("Parcelable.Creator<Save>", "createFromParcel");
            return new Save(incoming);
        }
    };
}

}

2. затем переопределить эти два метода в CustomView

CustomView{
     String value;  //some text value from edittext 

     EditText edittext;

     ...

    @Override
protected Parcelable onSaveInstanceState() {
    Log.i("CustomView", "onSaveInstanceState");

    Parcelable p = super.onSaveInstanceState();

    Save save = new Save(p);
    save.savedValue = value; // value is from CustomView class

    return save;
}

@Override
protected void onRestoreInstanceState(Parcelable state) {
    Log.i("CustomView", "onRestoreInstanceState");
    if(!(state instanceof Save)){
        super.onRestoreInstanceState(state);
        return;
    }

    Save save = (Save) state;
    value = save.savedValue;
    //setting in this place value to edittext will not do anything.
            //instead, you have to do this in step 3
    super.onRestoreInstanceState(save.getSuperState());
}

  ...
}

3. переопределить onAttachedToWindow () и установить для edittext «значение».

CustomView{
     String value;  //some text value from edittext 

     EditText edittext;
     ...
     @Override
     protected void onAttachedToWindow() {
           edittext.setText(value);
           super.onAttachedToWindow();
     }
     ...
}

, и теперь вы можете иметь несколько экземпляров пользовательского представления, которые устойчивы к изменению ориентации- они будут иметь правильные значения. Я не тестировал это решение на 100%, но оно кажется хорошим.

...