如何保存按下按钮时 运行 时间创建的多个 EditText 的状态(以避免用户的移动旋转)?

How to save the state of several EditTexts (to avoid user's mobile rotation) which were created on run time on a button press?

当用户按下 "OKAY" 按钮时,我的应用正在从 EditText 获取数字输入。然后根据该输入数字创建一些 EditText。但是当用户旋转屏幕时,newly/dynamically 创建的 EditTexts 被删除,我想在用户旋转屏幕时保存它。

我使用 onSaveInstanceState()onRestoreInstanceState() 来保存 EditTexts 的状态,但它不起作用(应用程序在 phone 旋转时崩溃)。可能是我没有正确使用这些方法。

我给出我的 xml 和 java 代码:

我的xml代码:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:padding="5dp"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Enter No of EditText:"
        android:textSize="24sp"
        android:textColor="@color/colorPrimaryDark"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <EditText
            android:id="@+id/edtNoCreate"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:hint="No of EditText"
            android:inputType="number"/>

        <Button
            android:id="@+id/btnCreate"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="OKAY"/>

    </LinearLayout>


    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="5dp"
        android:background="#EDE9E9">

        <LinearLayout
            android:id="@+id/lnrDynamicEditTextHolder"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

        </LinearLayout>

    </ScrollView>


</LinearLayout>

我的java代码:

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.*;
import android.widget.*;
import java.util.*;
import static java.lang.Integer.parseInt;

public class MainActivity extends AppCompatActivity {

    private LinearLayout lnrDynamicEditTextHolder;
    private EditText edtNoCreate;
    private Button btnCreate;

    List<EditText> allEditText = new ArrayList<EditText>();

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

        lnrDynamicEditTextHolder = (LinearLayout) findViewById(R.id.lnrDynamicEditTextHolder);
        btnCreate = (Button) findViewById(R.id.btnCreate);
        edtNoCreate = (EditText) findViewById(R.id.edtNoCreate);

        btnCreate.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try{
                    if(edtNoCreate.getText().toString().length()>0 && parseInt(edtNoCreate.getText().toString())!=0) {
                        if(edtNoCreate.getText().toString().length()==0){
                            edtNoCreate.setError("This Item Cannot be Empty");
                            return;
                        }
                        allEditText.clear();
                        lnrDynamicEditTextHolder.removeAllViews();

                        int length = parseInt(edtNoCreate.getText().toString());

                        for (int i=0;i<length;i++){
                            EditText editText = new EditText(MainActivity.this);
                            editText.setId(i+1);
                            editText.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
                            allEditText.add(editText);
                            lnrDynamicEditTextHolder.addView(editText);
                        }
                    }
                } catch (Throwable e) {
                    e.printStackTrace();
                    edtNoCreate.setError("This Item Cannot be Empty");
                }
            }
        });
    }


    @Override
    public void onSaveInstanceState(Bundle savedInstanceState) {
        super.onSaveInstanceState(savedInstanceState);

        int mViewsCount = 0;
        for(EditText view : allEditText)
        {
            savedInstanceState.putInt("mViewId_" + mViewsCount, view.getId());
            mViewsCount++;
        }

        savedInstanceState.putInt("mViewsCount", mViewsCount);
    }

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

        int mViewsCount = savedInstanceState.getInt("mViewsCount");;
        for(int i = 0; i <= mViewsCount; i++)
        {
            EditText view = allEditText.get(i);

            int viewId = savedInstanceState.getInt("mViewId_" + i);
            view.setId(viewId);

            mViewsCount++;
        }
    }
}

您应该考虑将 ViewModels 合并到您的应用程序中。 ViewModel 可以从 onDeviceConfigurationChange 保存 activity 或片段的状态(除了其他配置更改)。您需要做的就是从 ViewModelProviders() 请求您想要的任何 viewModel。

ViewModels in android

您应该在清单中的 activity 声明下添加这些行,以防止在配置更改时重新创建。

<activity android:name=".YourActiyvity"
      android:configChanges="orientation">

有关详细信息,请阅读 this

使用 ViewModel 和手动处理 configChange 的现有答案都有帮助。

不过,我个人对在清单中设置 configChanges 持谨慎态度。甚至文档警告:

Caution: Handling the configuration change yourself can make it much more difficult to use alternative resources, because the system does not automatically apply them for you. This technique should be considered a last resort when you must avoid restarts due to a configuration change and is not recommended for most applications.

使用 ViewModel 确实是一般处理方向变化的好方法。但是,有人可能会争辩说它不适合您的场景,因为您正在动态创建视图,而 ViewModel 不应该知道这些视图。

无论如何,在你的情况下我会如何处理它:

  • 首先将 EditText 的 ID 保存在 ArrayList<Int>
  • 添加新的 EditText 时,只需将您的 id 设置为您的 id-List 的大小,并使用其大小的值向列表中添加一个元素
  • 然后在 onSaveInstanceState 中将这些 id 添加到您的 savedInstanceState Bundle(使用 putIntegerArrayList 函数,这样您就不必单独保存每个 id)
  • 在 onCreate 中检查 savedInstanceState 是否为 null
  • 如果 savedInstanceState 不为空,则从中获取 ID 并遍历它们,为每个 ID 创建一个 EditText(就像按下按钮一样)
  • 仅此而已:)

作为一个微小的背景信息:当旋转设备时,Activity 被重新创建,这意味着所有以编程方式添加的视图都是 "gone"(因为布局再次膨胀)。但是,如果您再次添加具有各自 ID 的视图,状态将被保留。

这里说的够多了,代码是:

public class MainActivity extends AppCompatActivity {

    private LinearLayout lnrDynamicEditTextHolder;
    private EditText edtNoCreate;
    private Button btnCreate;
    private static final String KEY_IDS = "IDS";

    List<EditText> allEditText = new ArrayList<EditText>();
    ArrayList<Integer> allIds = new ArrayList<Integer>();

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

        lnrDynamicEditTextHolder = (LinearLayout) findViewById(R.id.lnrDynamicEditTextHolder);
        btnCreate = (Button) findViewById(R.id.btnCreate);
        edtNoCreate = (EditText) findViewById(R.id.edtNoCreate);

        if (savedInstanceState != null) {
            allIds = savedInstanceState.getIntegerArrayList(KEY_IDS)

            for (int i=0;i<allIds.size();i++){
                EditText editText = new EditText(MainActivity.this);
                editText.setId(allIds.get(i));
                editText.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
                allEditText.add(editText);
                lnrDynamicEditTextHolder.addView(editText);
            }
        }

        btnCreate.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try{
                    if(edtNoCreate.getText().toString().length()>0 && parseInt(edtNoCreate.getText().toString())!=0) {
                        if(edtNoCreate.getText().toString().length()==0){
                            edtNoCreate.setError("This Item Cannot be Empty");
                            return;
                        }
                        allEditText.clear();
                        lnrDynamicEditTextHolder.removeAllViews();

                        int length = parseInt(edtNoCreate.getText().toString());

                        for (int i=0;i<length;i++){
                            EditText editText = new EditText(MainActivity.this);
                            editText.setId(allIds.size());
                            allIds.add(allIds.size());
                            editText.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
                            allEditText.add(editText);
                            lnrDynamicEditTextHolder.addView(editText);
                        }
                    }
                } catch (Throwable e) {
                    e.printStackTrace();
                    edtNoCreate.setError("This Item Cannot be Empty");
                }
            }
        });
    }


    @Override
    public void onSaveInstanceState(Bundle savedInstanceState) {
        super.onSaveInstanceState(savedInstanceState);
        savedInstanceState.putIntegerArrayList(KEY_IDS, allIds);
    }
}

Rene Ferrari的回答几乎是正确的。但是,如果我们使用 value 0 作为 EditTextID,那么 "Invalid ID 0x00000000" 打印在 Logcat.

此外,我们不需要使用allIds(ArrayList),也不需要在onSaveInstanceState中处理.

相反,我们可以简单地在 onSaveInstanceState 中保存 allEditText (List) 的大小.

不过,最终代码在这里:

public class MainActivity extends AppCompatActivity {

    private LinearLayout lnrDynamicEditTextHolder;
    private EditText edtNoCreate;
    private Button btnCreate;

    List<EditText> allEditText = new ArrayList<EditText>();

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

        lnrDynamicEditTextHolder = (LinearLayout) findViewById(R.id.lnrDynamicEditTextHolder);
        btnCreate = (Button) findViewById(R.id.btnCreate);
        edtNoCreate = (EditText) findViewById(R.id.edtNoCreate);

        if (savedInstanceState != null) {
            int sizeOfEditText = savedInstanceState.getInt("NUMBER_OF_EDITTEXT");

            for (int i=1;i<=sizeOfEditText;i++){
                EditText editText = new EditText(MainActivity.this);
                editText.setId(i);
                editText.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
                allEditText.add(editText);
                lnrDynamicEditTextHolder.addView(editText);
            }
        }

        btnCreate.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try{
                    if(edtNoCreate.getText().toString().length()>0 && parseInt(edtNoCreate.getText().toString())!=0) {
                        if(edtNoCreate.getText().toString().length()==0){
                            edtNoCreate.setError("This Item Cannot be Empty");
                            return;
                        }
                        allEditText.clear();
                        lnrDynamicEditTextHolder.removeAllViews();

                        int length = parseInt(edtNoCreate.getText().toString());

                        for (int i=1;i<=length;i++){
                            EditText editText = new EditText(MainActivity.this);
                            editText.setId(i);
                            editText.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
                            allEditText.add(editText);
                            lnrDynamicEditTextHolder.addView(editText);
                        }
                    }
                } catch (Throwable e) {
                    e.printStackTrace();
                    edtNoCreate.setError("This Item Cannot be Empty");
                }
            }
        });
    }

    @Override
    public void onSaveInstanceState(Bundle savedInstanceState) {
        super.onSaveInstanceState(savedInstanceState);
        savedInstanceState.putInt("NUMBER_OF_EDITTEXT", allEditText.size());
    }

}