【问题标题】:Save state on screen rotate when a compound component is used multiple times多次使用复合组件时在屏幕旋转上保存状态
【发布时间】:2012-03-29 16:36:23
【问题描述】:

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

新组件的 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相同,保存的值会互相覆盖。

我正在寻找有关如何实际实施的任何建议。目前,我看不到任何更改这些标识符的方法。

【问题讨论】:

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


    【解决方案1】:

    似乎我对这个问题有一些解决方案。我试着找了一段时间。

    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.override onAttachedToWindow() 并设置为edittext "value"。

    CustomView{
         String value;  //some text value from edittext 
    
         EditText edittext;
         ...
         @Override
         protected void onAttachedToWindow() {
               edittext.setText(value);
               super.onAttachedToWindow();
         }
         ...
    }
    

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

    【讨论】:

    • 谢谢,您的代码正在运行 :) 我花了一段时间才明白为什么,但最后我明白了。不用担心你的英语,没关系。欢迎来到 StackOverflow :)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-01-04
    • 1970-01-01
    • 2012-04-22
    • 1970-01-01
    • 1970-01-01
    • 2011-01-31
    • 1970-01-01
    相关资源
    最近更新 更多