OnRetainCustomConfigurationInstance之后所有EditText中的重复文本

时间:2018-10-11 14:44:50

标签: java android android-edittext rotation

当我使用onRetainCustomNonConfigurationInstance保存自定义EditText中包含的文本时,旋转设备后,最后一个EditText中包含的文本将与目标布局中的所有其他文本重复。

在使用断点进行调试时,我发现所有文本值一直都是正确的。但是,更改完成后,所有EditText都会显示相同的文本(最后一个文本),并将焦点放在布局中的第一个文本上。

我在最简单的项目中复制了此行为。我尝试使用24级和28级android API。

此行为来自何处,如何解决?

MainActivity.java:

public class MainActivity extends AppCompatActivity {
    private ArrayList<CustomEdit> editList=new ArrayList<CustomEdit>();
    private LinearLayout layout;
    private Button addButton;

    @RequiresApi(api = Build.VERSION_CODES.M)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        addButton=findViewById(R.id.add_button);
        layout = findViewById(R.id.layout);

        addButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                addEdit();
            }
        });
        CustomSave data = (CustomSave)getLastCustomNonConfigurationInstance();
        if(data==null) return;

        for(int i = 0; i<data.texts.size();i++){
            addEdit(data.texts.get(i));
        }
    }
    @Override
    public Object onRetainCustomNonConfigurationInstance() {
        CustomSave data = save();
        return data;}

    private CustomSave save(){
        ArrayList<String> texts = new ArrayList<String>();
        for(int i =0; i<editList.size(); i++)
            texts.add(editList.get(i).getText());
        return new CustomSave(texts);}

    /**
     * Create a new custom EditText with hint
     */
    private void addEdit(){
        CustomEdit newEdit = new CustomEdit(this,editList.size());
        layout.addView(newEdit,editList.size());
        editList.add(newEdit);}
    /**
     * Create a new custom editText with text
     * @param text
     */
    private void addEdit(String text){
        CustomEdit newEdit;
        if(text==null) newEdit = new CustomEdit(this, editList.size());
        else  newEdit = new CustomEdit(this, editList.size(),text);
        layout.addView(newEdit,editList.size());
        editList.add(newEdit);}
}

activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
    <TextView
        android:id="@+id/title"
        android:layout_width="match_parent"
        android:layout_height="20dp"
        android:text="Title"
        android:gravity ="center"/>
    <HorizontalScrollView
        android:id="@+id/scroll1"
        android:layout_below="@id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingStart="10dp"
        android:paddingLeft="10dp">
        <LinearLayout
            android:id="@+id/layout"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
            <Button
                android:id="@+id/add_button"
                android:layout_width="100dp"
                android:layout_height="wrap_content"
                android:text="+"/>
        </LinearLayout>
    </HorizontalScrollView>
</RelativeLayout>

CustomEdit.java:

public class CustomEdit extends RelativeLayout {
    private EditText editText;
    private Button closeButton;
    private int indexNumber;

    public CustomEdit(Context context, int indexNumber) {
        super(context);
        this.indexNumber =indexNumber;
        init();
    }
    public CustomEdit(Context context, int indexNumber, String text){
        super(context);
        this.indexNumber=indexNumber;
        init();
        editText.setText(text);
    }
    public CustomEdit(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }
    public CustomEdit(Context context, AttributeSet attrs, int defStyleAttr){
        super(context, attrs, defStyleAttr);
        init();
    }
    private void init(){
        inflate(getContext(),R.layout.custom_edit_text,this);

        editText = (EditText)findViewById(R.id.edit);
        editText.setHint("EditText "+(indexNumber+1));

        closeButton = (Button)findViewById(R.id.close_button);
        closeButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                ((MainActivity)getContext()).closeEdit(indexNumber);
            }
        });
    }
    public String getText(){
        return editText.getText().toString();
    }
}

custom_edit_text.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="100dp"
    android:layout_height="40dp">
    <EditText
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/edit"
        android:inputType="text"
        android:hint="Element"
        android:maxLength="30"/>
    <Button
        android:id="@+id/close_button"
        android:layout_width="20dp"
        android:layout_height="20dp"
        android:layout_alignRight="@id/edit"
        android:layout_alignEnd="@id/edit"
        android:layout_alignTop="@id/edit"
        android:text="X"/>
</RelativeLayout>

CustomSave.java:

public class CustomSave {
    public ArrayList<String> texts;

    CustomSave(ArrayList<String> texts){
        this.texts = texts;
    }
}

谢谢。

2 个答案:

答案 0 :(得分:2)

您有两种选择来解决此问题:将代码移到活动生命周期的不同点或更改CustomEdit的xml定义。

Android生命周期

将此代码移出onCreate()

CustomSave data = (CustomSave)getLastCustomNonConfigurationInstance();
if(data==null) return;

for(int i = 0; i<data.texts.size();i++){
    addEdit(data.texts.get(i));
}

并将其放在onRestoreInstanceState()中:

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
    super.onRestoreInstanceState(savedInstanceState);

    CustomSave data = (CustomSave)getLastCustomNonConfigurationInstance();
    if(data==null) return;

    for(int i = 0; i<data.texts.size();i++){
        addEdit(data.texts.get(i));
    }
}

XML定义

将此属性添加到<EditText>文件中的custom_edit_text.xml标记中:

android:saveEnabled="false"

您编写的用于保存/恢复文本值的代码没有错。但是,在 onCreate()之后,Android会自动执行其 保存/恢复逻辑,这将覆盖您所做的工作。

如果您将代码从onCreate()移至onRestoreInstanceState(),那么您的代码将在Android自动保存/恢复后运行,因此您将“获胜”。或者,您可以通过添加saveEnabled=false属性来禁用自动保存/恢复。

Android的自动保存/恢复不起作用的原因是它基于每个视图的android:id属性,并且您的EditText标签都具有相同的ID。这意味着所有四个值都使用相同的键保存,因此最后一个值将覆盖所有先前的值。

答案 1 :(得分:1)

因此,这里发生的是Android还在为您的自定义EditText实现处理状态保存,这将覆盖您的实现。由于CustomEdit实例的列表是动态生成的,因此您可能不希望依赖Android为视图保存状态,因为它们没有唯一的ID。

由于您的CustomEdit夸大了custom_edit_text.xml(将EditText的ID声明为@+id/edit),这意味着您添加到布局中的每个CustomEdit具有相同的内部EditText-R.id.edit的ID。由于它们都具有相同的ID,因此每个视图都将其状态保存为该ID,因此保存状态的最后一个视图将是还原状态时应用于所有视图的文本。

有两种方法可以避免这种情况:

  1. 在您的custom_edit_text.xml中,将android:saveEnabled="false"添加到EditText中。这样可以防止保存View的状态。这样做是可取的,因为这样可以避免进行不必要的工作。

  2. onRestoreInstanceState()中执行状态恢复,该状态当前正在恢复视图状态。