当多次使用复合组件时,在屏幕旋转时保存状态

时间:2012-03-29 16:36:24

标签: android android-layout android-widget android-custom-view

我遇到一个问题,我知道根本原因,但没有办法解决它。如果在活动中多次使用自定义复合组件,则从视图中保存的值将相互覆盖。为了便于解释,我做了以下示例。

新组件的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>

实现新行为的类,只是给布局充气。

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>

旋转屏幕时,第二个视图中的值也会在第一个视图中恢复。

深入研究框架的代码,我发现从View类中定义的onSaveInstanceState返回的Parcelable对象被放入一个带有密钥对象id的SparseArray中。因为我多次包含CustomView,ID为“custom_text”的EditText也会多次添加。拥有相同的ID,保存的值将相互覆盖。

我正在寻找关于如何实际实施的任何建议。现在,我认为没有办法改变这些标识符。

1 个答案:

答案 0 :(得分:2)

好像我有一些解决这个问题的方法。我试着找一段时间。

1.首先,您必须在CustomView中创建扩展BaseSavedState的内部类。

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“value”。

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

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

现在您可以拥有多个自定义视图实例,这些实例可以抵抗更改方向 - 它们将具有正确的值。我没有100%测试此解决方案,但似乎很好。