如何保存按下按钮时 运行 时间创建的多个 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。
您应该在清单中的 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 作为 EditText 的 ID,那么 "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());
}
}
当用户按下 "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。
您应该在清单中的 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 作为 EditText 的 ID,那么 "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());
}
}