26

我正在尝试编写一个由 aTextView和一个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);

}
   }

现在,如果我View在 _activity_main.xml_ 中使用其中的 2 个:

<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>

并且在 Activity 代码中,我只对 RelativeLayout 进行膨胀,而不管理 onSaveInstanceState:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

}

然后当我在 2nd 中写一些东西EditText并旋转我的设备时,text第一个 custom 的 EditText 中也会出现同样的情况View

为什么会发生这种行为?

编辑:

我通过删除android:id并在 Compound_view.xml 中使用android:tag解决了这个问题EditText,然后在 CompoundView 类中管理 EditText 状态的保存:

@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);
}
4

9 回答 9

17

您需要使用禁用EditText 的SaveEnabled属性android:saveEnabled="false"

在您的自定义视图中,您正在从定义了 ID 的 XML 扩展布局。如果视图定义了 ID,Android 操作系统具有保存和恢复视图状态的默认功能。

这意味着它将在 Activity 暂停时保存 EditText 的文本,并在 Activity 恢复时自动恢复。在您的情况下,您多次使用此自定义视图,并且正在膨胀相同的布局,因此您的所有 EditText 都具有相同的 ID。现在,当 Activity 将暂停时,Android 将检索 EditText 的值并保存它们的 ID,但由于每个 EditText 具有相同的 ID,值将被覆盖,因此它将在所有 EditText 中恢复相同的值。

于 2018-04-17T11:44:49.000 回答
10

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

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

在您的情况下,您可以尝试不在复合视图中使用 id,而是通过标签 ( View.findViewWithTag) 或名称以另一种方式查找元素,看看这是否会有所不同。

就我而言,我通过后者解决了这个问题。

于 2012-12-17T14:17:11.373 回答
1

我有同样的问题,这就是我让它工作的方式。首先需要为editText的saveEnabled设置false。我们可以在布局中保留android:id

<EditText
  android:id="@+id/editText"
  android:saveEnabled="false" 

然后在您的复合视图中覆盖以下方法并自行管理状态。如果需要,请随时询问工作示例。

@Override
protected Parcelable onSaveInstanceState() {
    Bundle bundle = new Bundle();
    bundle.putParcelable("state", super.onSaveInstanceState());
    String text = editText.getText().toString();
    bundle.putString("text", text);
    return bundle;
}

@Override
protected void onRestoreInstanceState(Parcelable state) {
    if (state instanceof Bundle) {
        Bundle bundle = (Bundle) state;
        String text = bundle.getString("text");
        state = bundle.getParcelable("state");
        editText.setText(text);
    }
    super.onRestoreInstanceState(state);
}
于 2021-08-21T19:00:51.893 回答
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);
于 2012-12-17T13:46:20.327 回答
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

于 2019-05-04T20:38:17.250 回答
0

问题的发生是由于From your codeid上的字段compound_view.xml
,我刚刚注意到您在课堂 上夸大compound_view了布局文件。 一旦您在文件中创建并放入和id ,android studio 就会自动将这些s 创建到类中的值中。 因此,当您在布局文件中放置两次时间时,您只需创建两个实例,但是,这两个实例都只有一个地址位置。因此,它们指向在类中声明的相同地址位置。CompoundView

compound_view.xmlandroid:id="@+id/textLabel"android:id="@+id/textEdit"layout xmlidintR

CompoundViewactivity_main.xmlCompoundViewtextEdittextLabelR


这就是为什么,每当您以编程方式更改textEdittextLabel发送文本时,它们也会更改textEdittextLabelCompoundView

于 2020-01-09T20:07:01.087 回答
0

我想强调一篇很棒的文章,它让我大开眼界。它基于重新实现onSaveInstanceState()onRestoreInstanceState(state: Parcelable?).

这样做的好处是您可以在同一个布局中多次使用同一个复合视图(没有重复的 id 问题)。

于 2021-03-08T15:08:23.320 回答
0

如果有人在屏幕旋转后由于内部视图的共享 id 而出现不正确的焦点恢复问题,您可以通过覆盖 findFocus 方法来控制将哪个 id 保存为焦点视图,如下所示:

    override fun findFocus(): View {
      if (focusedChild != null) {
          return this
      }

      return super.findFocus()
    }

然后焦点恢复到您的复合视图,但是您应该处理 requestFocus 调用,以便正确的子视图在恢复时获得焦点。

于 2021-05-28T10:11:32.203 回答
-1

我遇到了同样烦人的问题,并用另一种解决方案解决了它,而不是建议的解决方案。当自定义视图膨胀时,我没有找到带有 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

于 2016-02-15T11:45:38.890 回答