为什么自定义复合视图中的EditText会重复使用在另一个复合视图实例中输入的文本?

时间:2012-12-17 13:19:12

标签: android android-edittext android-view android-custom-view

我正在尝试编写由TextViewEditText,_ 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);
}

8 个答案:

答案 0 :(得分:10)

我首先要说的是我没有证实这一点......但是在使用复合视图时遇到了同样的问题,类似于你正在做的事情。

我认为问题的根源是Android如何自动保存EditText控件的状态,我认为它会将其保存为“id”。因此,如果xml中有多个具有相同“id”的控件,那么当它保存状态,然后恢复状态时,具有相同id的所有控件都会获得相同的值。您可以通过向普通xml添加2 EditText个控件来尝试此操作,并为它们提供相同的android:id值,并查看它们是否最终获得相同的值。

在您的情况下,您可以尝试在复合视图中不使用ID,而是通过标记(View.findViewWithTag)或名称以另一种方式查找元素,并查看是否会产生影响。< / p>

就我而言,我通过做后者来解决问题。

答案 1 :(得分:2)

您需要使用android:saveEnabled="false"

禁用EditText的SaveEnabled属性

在自定义视图中,您正在从已定义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创建到intR类中的值一次。

因此,当您两次将CompoundView放入activity_main.xml布局文件中时,您只创建了两个CompoundView实例,但是textEdittextLabel这两个实例仅具有每个地址1个地址。因此,它们指向R类中声明的相同地址位置。


因此,每当您以编程方式更改textEdittextLabel文本时,它们也会同时更改同时显示在textEdit textLabel或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