18

假设你想从现有的 View 实现中派生出你自己的 View 类,增加一点价值,从而维护一些变量,以一种有意义的方式代表你的 View 的状态。

如果您的 View 能像其他人一样自动保存其状态(如果分配了 ID),那就太好了,这样您就想覆盖onRestoreInstanceState()and onSaveInstanceState()

当然,你需要调用你的基类各自的方法,你需要将你的状态信息和你的基类的状态信息结合起来。

显然,这样做的唯一安全方法是将您的超类包装Parcelable在一个自己的中Parcelable,这样键就不会混淆。

现在有View.BaseSavedState它有趣的getSuperState()方法,但我不知何故无法理解这如何真正增加价值,只是将基类与派生视图的状态值一起存储Parcelable在 a 中并返回它。Bundle另一方面,也许其他一些系统组件会期望所有 InstanceState 信息都是类型的View.AbsSavedState(例如,getSuperState()可以调用的)?

你有什么愿意分享的经验吗?

4

2 回答 2

16

为了补充 James Chen 的回答,这里有一个完整的例子,说明如何使用这种方法,基于 Charles Harley 的博客文章

来自链接的代码:

public class LockCombinationPicker extends LinearLayout {
    private NumberPicker numberPicker1;
    private NumberPicker numberPicker2;
    private NumberPicker numberPicker3;

    public LockCombinationPicker(Context context) {
        this(context, null);
    }

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

    public LockCombinationPicker(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        loadViews();
    }

    private void loadViews() {
        LayoutInflater.from(getContext()).inflate(R.layout.lock_combination_picker, this, true);
        numberPicker1 = (NumberPicker) findViewById(R.id.number1);
        numberPicker1.setMinValue(0);
        numberPicker1.setMaxValue(10);
        numberPicker2 = (NumberPicker) findViewById(R.id.number2);
        numberPicker2.setMinValue(0);
        numberPicker2.setMaxValue(10);
        numberPicker3 = (NumberPicker) findViewById(R.id.number3);
        numberPicker3.setMinValue(0);
        numberPicker3.setMaxValue(10);
    }

    @Override
    protected Parcelable onSaveInstanceState() {
        Parcelable superState = super.onSaveInstanceState();
        return new SavedState(superState, numberPicker1.getValue(), numberPicker2.getValue(), numberPicker3.getValue());
    }

    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        SavedState savedState = (SavedState) state;
        super.onRestoreInstanceState(savedState.getSuperState());
        numberPicker1.setValue(savedState.getNumber1());
        numberPicker2.setValue(savedState.getNumber2());
        numberPicker3.setValue(savedState.getNumber3());
    }

    @Override
    protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
        // As we save our own instance state, ensure our children don't save and restore their state as well.
        super.dispatchFreezeSelfOnly(container);
    }

    @Override
    protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
        /** See comment in {@link #dispatchSaveInstanceState(android.util.SparseArray)} */
        super.dispatchThawSelfOnly(container);
    }

    /**
     * Convenience class to save / restore the lock combination picker state. Looks clumsy but once created is easy to maintain and use.
     */
    protected static class SavedState extends BaseSavedState {
        private final int number1;
        private final int number2;
        private final int number3;

        private SavedState(Parcelable superState, int number1, int number2, int number3) {
            super(superState);
            this.number1 = number1;
            this.number2 = number2;
            this.number3 = number3;
        }

        private SavedState(Parcel in) {
            super(in);
            number1 = in.readInt();
            number2 = in.readInt();
            number3 = in.readInt();
        }

        public int getNumber1() {
            return number1;
        }

        public int getNumber2() {
            return number2;
        }

        public int getNumber3() {
            return number3;
        }

        @Override
        public void writeToParcel(Parcel destination, int flags) {
            super.writeToParcel(destination, flags);
            destination.writeInt(number1);
            destination.writeInt(number2);
            destination.writeInt(number3);
        }

        public static final Parcelable.Creator<SavedState> CREATOR = new Creator<SavedState>() {
            public SavedState createFromParcel(Parcel in) {
                return new SavedState(in);
            }

            public SavedState[] newArray(int size) {
                return new SavedState[size];
            }
        };
    }
}
于 2013-08-20T10:43:02.983 回答
15

我认为设计需要我们,顾名思义,实现 View.BaseSavedState 的子类,通过覆盖 Parcelable 的接口来存储值。

TextView.SavedState 就是一个很好的例子

public static class SavedState extends BaseSavedState {
    int selStart;
    int selEnd;
    CharSequence text;
    boolean frozenWithFocus;
    CharSequence error;

    SavedState(Parcelable superState) {
        super(superState);
    }

    @Override
    public void writeToParcel(Parcel out, int flags) {
        super.writeToParcel(out, flags);
        out.writeInt(selStart);
        out.writeInt(selEnd);
        out.writeInt(frozenWithFocus ? 1 : 0);
        TextUtils.writeToParcel(text, out, flags);

        if (error == null) {
            out.writeInt(0);
        } else {
            out.writeInt(1);
            TextUtils.writeToParcel(error, out, flags);
        }
    }

    @Override
    public String toString() {
        String str = "TextView.SavedState{"
                + Integer.toHexString(System.identityHashCode(this))
                + " start=" + selStart + " end=" + selEnd;
        if (text != null) {
            str += " text=" + text;
        }
        return str + "}";
    }

    @SuppressWarnings("hiding")
    public static final Parcelable.Creator<SavedState> CREATOR
            = new Parcelable.Creator<SavedState>() {
        public SavedState createFromParcel(Parcel in) {
            return new SavedState(in);
        }

        public SavedState[] newArray(int size) {
            return new SavedState[size];
        }
    };

    private SavedState(Parcel in) {
        super(in);
        selStart = in.readInt();
        selEnd = in.readInt();
        frozenWithFocus = (in.readInt() != 0);
        text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);

        if (in.readInt() != 0) {
            error = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
        }
    }
}
于 2013-04-24T04:05:56.897 回答