我正在尝试编写由TextView
和EditText
,_ compound_view.xml _组成的自定义复合视图:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/compoundText"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:id="@+id/textLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Label" />
<EditText
android:id="@+id/textEdit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="enter text here" >
</EditText>
这是扩展LinearLayout
的类:
public class CompoundView extends LinearLayout {
public CompoundView(Context context, AttributeSet attrs) {
super(context, attrs);
readAttributes(context, attrs);
init(context);
}
public CompoundView(Context context) {
super(context);
init(context);
}
private void init(Context c) {
final LayoutInflater inflater = (LayoutInflater) c
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.compound_view, this);
}
}
现在,如果我在_activity_main.xml _中使用其中2个View
:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<it.moondroid.compoundview.example.CompoundView
android:id="@+id/compoundview1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true" />
<it.moondroid.compoundview.example.CompoundView
android:id="@+id/compoundview2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/compoundview1" />
</RelativeLayout>
并且在活动代码中我只会给RelativeLayout充气,而不管理onSaveInstanceState:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
然后当我在第二个EditText
中写一些内容并旋转设备时,第一个自定义text
的EditText中会显示相同的View
。
为什么会发生这种行为?
修改
我通过删除 android:id 并使用 android:tag 为compound_view.xml中的EditText
解决了这个问题,然后管理了EditText的保存在CompoundView类中的状态:
@Override
protected Parcelable onSaveInstanceState() {
Bundle bundle = new Bundle();
bundle.putParcelable("instanceState", super.onSaveInstanceState());
bundle.putString("currentEdit", mEditText.getText().toString());
bundle.putBoolean("isFocused", mEditText.hasFocus());
return bundle;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
if (state instanceof Bundle) {
Bundle bundle = (Bundle) state;
mEditText.setText(bundle.getString("currentEdit"));
if (bundle.getBoolean("isFocused")) {
mEditText.requestFocus();
}
super.onRestoreInstanceState(bundle.getParcelable("instanceState"));
return;
}
super.onRestoreInstanceState(state);
}
答案 0 :(得分:10)
我首先要说的是我没有证实这一点......但是在使用复合视图时遇到了同样的问题,类似于你正在做的事情。
我认为问题的根源是Android如何自动保存EditText
控件的状态,我认为它会将其保存为“id”。因此,如果xml中有多个具有相同“id”的控件,那么当它保存状态,然后恢复状态时,具有相同id的所有控件都会获得相同的值。您可以通过向普通xml添加2 EditText
个控件来尝试此操作,并为它们提供相同的android:id
值,并查看它们是否最终获得相同的值。
在您的情况下,您可以尝试在复合视图中不使用ID,而是通过标记(View.findViewWithTag
)或名称以另一种方式查找元素,并查看是否会产生影响。< / p>
就我而言,我通过做后者来解决问题。
答案 1 :(得分:2)
您需要使用android:saveEnabled="false"
在自定义视图中,您正在从已定义ID的XML中扩展布局。如果视图已定义ID,则Android OS具有保存和恢复视图状态的默认功能。
这意味着它将在Activity暂停时保存EditText的文本,并在Activity恢复时自动恢复。在您的情况下,您正在多次使用此自定义视图,这会使相同的布局膨胀,因此您的所有EditText都具有相同的ID。现在当Activity停止时,Android将检索EditText的值并保存其ID,但由于每个EditText具有相同的ID,因此值将被覆盖,因此它将在所有EditText中恢复相同的值。
答案 2 :(得分:0)
在您的问题中查看我的评论,并确保您正确获得对您观点的引用。
我正在使用你的代码:
CompoundView cv1 = (CompoundView) findViewById(R.id.compoundview1);
TextView tv1 = (TextView) cv1.findViewById(R.id.textEdit);
CompoundView cv2 = (CompoundView) findViewById(R.id.compoundview2);
TextView tv2 = (TextView) cv2.findViewById(R.id.textEdit);
答案 3 :(得分:0)
如果您具有更复杂的复合视图和许多子视图,则可以考虑覆盖最外部的dispatchSaveInstanceState
类的ViewGroup
,而不必调用超级实现。
赞:
@Override
protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
//If you don't call super.dispatchRestoreInstanceState(container) here,
//no child view gets its state saved
}
之所以使用它,是因为在我的情况下,我具有许多不同复合视图的层次结构,这些视图具有共同的超类,在这些超类中进行了此覆盖。 (我一直忘记将新布局的SaveEnabled属性设置为false。)但是,有很多注意事项,例如,您需要手动保存焦点视图,然后请求其焦点,因此当屏幕旋转时,您的应用程序不会出现奇怪的情况。
编辑:
如果您实际上需要保存复合视图的状态,则用空的正文覆盖dispatchSaveInstanceState
会导致onSave/RestoreInstanceState
未被调用。如果要使用它们,但仍不保存任何子视图的状态,则需要在覆盖中调用super.dispatchFreezeSelfOnly(container)
。
此处的更多信息:http://charlesharley.com/2012/programming/views-saving-instance-state-in-android
答案 4 :(得分:0)
由于id
上的compound_view.xml
字段而发生了问题
从您的代码中,我刚刚注意到您夸大了compound_view
类中的CompoundView
布局文件。
创建compound_view.xml
并将android:id="@+id/textLabel"
和android:id="@+id/textEdit"
id放入layout xml
文件后,android studio会自动将这些id
创建到int
中R
类中的值一次。
因此,当您两次将CompoundView
放入activity_main.xml
布局文件中时,您只创建了两个CompoundView
实例,但是textEdit
和textLabel
这两个实例仅具有每个地址1个地址。因此,它们指向R
类中声明的相同地址位置。
因此,每当您以编程方式更改textEdit
或textLabel
文本时,它们也会同时更改同时显示在textEdit
CompoundView
答案 5 :(得分:0)
我想强调一个great article,它让我大开眼界。它基于重新实现 onSaveInstanceState()
和 onRestoreInstanceState(state: Parcelable?)
。
这样做的好处是你可以在同一个布局中多次使用同一个复合视图(没有重复的 id 问题)。
答案 6 :(得分:0)
如果有人在屏幕旋转后无法恢复不正确的焦点,这是由于内部视图的共享 id 而发生的,您可以通过覆盖 findFocus 方法来控制将哪个 id 保存为焦点视图,如下所示:
override fun findFocus(): View {
if (focusedChild != null) {
return this
}
return super.findFocus()
}
然后焦点将恢复到复合视图,但是您应该处理 requestFocus 调用,以便正确的子视图在恢复时得到焦点。
答案 7 :(得分:-2)
我遇到了同样恼人的问题,并使用另一种解决方案解决了问题。当自定义视图夸大时,我无法使用ID或标记找到它们,我使用getChildAt
方法获取它们。我没有保存实例状态。
以下是一个例子:
自定义XML以充气(custom_view.xml):
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android" >
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:textAppearance="?android:attr/textAppearanceMedium"/>
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"/>
</merge>
然后在视图初始化期间以这种方式获取视图:
private void initView(Context context) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.custom_view, this, true);
setOrientation(LinearLayout.HORIZONTAL);
setGravity(Gravity.CENTER);
TextView mTitleTV = (TextView) getChildAt(0);
EditText mContentET = (EditText) getChildAt(1);
}
重要:您必须从XML中删除所有ID