android-layout - 在复合视图部件上,保存状态

  显示原文与译文双语对照的内容
0 0

问题

如何保存视图小部件实例状态状态,使用xml定义的小部件布局,各个部件实例的组件都具有相同的标识。

例子

例如 TimePicker 小部件中使用的NumberPicker 小部件( 请注意,NumberPicker 不公开到 SDK ) 。 这是一个简单的小部件,其中三个组件从 number_picker.xml 中得到了扩展: 一个递增按钮,一个减量按钮和一个 EditText,你可以直接输入数字。 为了让代码与这些小部件交互,它们都有 IDs ( R.id.incrementR.id.decrementR.id.timepicker_input ) 。

假设你有三个 NumberPicker 在一个XML布局中,你给它们提供了不同的id ( 例如。 我们决定改变 Activity的方向,所以 Activity.onSaveInstanceState(Bundle) 帮助保存每个具有 ID ( 这是默认行为)的视图的视图状态。

不幸的是,三个 NumberPickerEditText 都拥有相同的ID — R.id.timepicker_input 。 因此,在恢复 Activity 时,视图层次结构中最远的一个是,它的状态为所有三个状态的状态。 此外,当恢复时,焦点将转到第一个 NumberPicker,而不考虑在保存时。

TimePicker 通过单独保留状态本身来解决这个问题。 但是,如果没有更多的工作,这将不会保留光标位置或者聚焦视图。 我不确定如果它在所有( 并且快速播放一个时间输入对话框似乎表明它可以) 中都能保持状态。

请参见示例代码以演示这里问题: https://github.com/xxv/AndroidNumberPickerBug


时间: 原作者:

0 0

当试图创建自己的复合视图时,我遇到了同样的问题。 从查看Android源代码,我相信实现复合视图的正确方法是将复合视图的compound视图itself保存,并防止调用的实例状态被传递到子视图并将实例状态传递到子视图。 这解决了子视图 NOT的问题,当你在 Activity 中拥有更多相同复合视图的实例时,是惟一的。

这听起来可能很复杂,但实际上很简单,api实际上会提供这个精确的场景。 我写了一篇博客文章在本质上这是如何实现的,但在你的复合视图需要实现以下 4方法、定制 onSaveInstanceState() 和 onRestoreInstanceState() 来满足你的特定需求。


@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 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 container) {
/** See comment in {@link #dispatchSaveInstanceState(android.util.SparseArray)} */
 super.dispatchThawSelfOnly(container);
}

关于 NumberPicker/TimePicker的问题,如另一评论中提到的,有一个带有NumberPicker和TimePicker的Bug 。 要修复它,你可以重写并实现我所描述的解决方案。

原作者:
0 0

我刚刚遇到了一个具有三个NumberPickers的复合视图的完全相同的问题。 我在另一个站点上找到一个解决方案,它涉及将timepicker_input的Id重新分配到唯一的随机标识。 这是一个很复杂的工作,因为一旦选择了一个新的Id,该Id必须在配置更改中保持,因此需要。

对于我的应用程序,可能是其他的99%,一个更简单的方法是( 黑客攻击) 。 我意识到,Ids的名称空间有 65536个惟一标识符( 0 x7f090000到 0 x7f09ffff ),但是我的应用程序只使用 20个或者一个字节,而它们是从一个。 此外,NumberPicker小部件本身在树( 例如在原始日志中,R.id. 小时,R.id.minute 和 R.id. 秒) 中拥有惟一的identfier 。 我的解决方案是将EditText小部件的Id重新分配到NumberPicker小部件的Id和一个偏移量。

这是对NumberPicker代码的一行修改。 简单地添加:


mText.setId(getId() + 1000);

在 NumberPicker.java 中的以下行之后:


mText = (EditText) findViewById(R.id.timepicker_input);

课程的偏移可以根据应用需求进行调整。

我想同样的方法对于其他复合部件也适用。

对于上面的示例,这里方法允许保存和恢复单个EditText小部件的状态,并将焦点视图也保存在一起。

0 0

我有点晚了但是我想给我的输入。

可以使用 android:tag 对每个 View 进行分组。 因此,你可以'重新载入'每个状态。

Android开发人员网站:

android:tag

为包含字符串的这里视图提供标记,稍后用 View.getTag() 检索或者使用 View.findViewWithTag() 搜索。

原作者:
0 0

我有一个复合小部件,它的状态描述由单个整数 mValue 组成
现在重写以下两种方法:


@Override
protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
 if(getId()!= NO_ID) {
 Parcel p = Parcel.obtain();
 p.writeInt(mValue);
 p.setDataPosition(0);
 container.put(getId(), new MyParcelable(p));
 }
}

@Override
protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
 if(getId()!= NO_ID) {
 Parcelable p = container.get(getId());
 if(p!= null && p instanceof MyParcelable) {
 MyParcelable mp = (MyParcelable) p;
 updateAllValues(mp.getValue());
 }
 }
}

dispatchSaveInstanceState() 中,你正在序列化你的状态。 更复杂的状态需要更复杂的存储结构。
确保 setDataPosition() 正确。
我只保存状态,如果小部件有指定的标识,我将使用它作为包裹的标签

dispatchRestoreInstanceState() 里你要把包裹包起来。
如果小部件有指定的标识并且容器包含与这里id匹配的标签,那么我才执行解包。
Parcelable 必须是正确的类。
对于更复杂的小部件,解包将更加复杂。

最后需要的组件是 Parcelable 类似以下的子类:


static class MyParcelable implements Parcelable {

 private int mValue;

 private MyParcelable(Parcel in) {
 mValue = in.readInt();
 }

 public int describeContents() {
 return 0;
 }

 public void writeToParcel(Parcel dest, int flags) {
 dest.writeInt(mValue);
 }

 int getValue() {
 return mValue;
 }

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

 public MyParcelable createFromParcel(Parcel source) {
 return new MyParcelable(source);
 }

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

}

使用这个框架比操作 Fragments 和管理配置要简单得多

原作者:
0 0

很简单:你不。只是去禁用取向改变垃圾在你的清单文件。 视图状态保存机制存在缺陷,它们根本不考虑这个问题。

如果希望保持状态,则不能在单个 Activity 中重用 id 。 这意味着,你不能多次使用一次布局,这样更复杂的小部件就像TimePicker不可能做得很好。

可以让孩子保持状态通过重写 dispatchSaveInstanceState 和黑客,但我确实 NOT 找到一个方法来保持关注,除了管理自己。

我认为他们可以在不破坏API的情况下解决这个问题,但不要让你的呼吸。

原作者:
...