【问题标题】:How to retain EditText data on orientation change?如何在方向更改时保留 EditText 数据?
【发布时间】:2012-09-07 09:00:27
【问题描述】:

我有一个登录屏幕,其中包含 2 个用于用户名和密码的 EditText。 我的要求是,在方向更改时,EditText 中的输入数据(如果有)应保持原样,并且还应绘制新布局。我有 2 个布局 xml 文件-一个位于布局文件夹中,另一个位于在布局土地文件夹中。我正在尝试实现以下两种方法,但它们都不是完美的:

(1) configChanges:keyboardHidden - 在这种方法中,我不在清单文件的 configChanges 中提供“方向”。所以我在 onCreate() 和 onConfigurationChanged() 方法中都调用了 setContentView() 方法。它满足我的两个要求。布局已更改,EditTexts 中的输入数据也保持原样。但它有一个大问题:

当用户点击登录按钮时,ProgressDialog 会显示,直到收到服务器响应。现在,如果用户在 ProgressDialog 运行时旋转设备,应用程序会崩溃。它显示一个异常说“视图不能附加到窗口”。我尝试使用 onSaveInstanceState 处理它(在方向更改时会调用它),但应用程序仍然崩溃。

(2) configChanges:orientation|keyboardHidden - 在这种方法中,我在清单中提供了“方向”。所以现在我有两种情况:

(a) 如果我在 onCreate() 和 onConfigurationChanged() 中调用 setContentView() 方法,布局会相应更改,但 EditText 数据会丢失。

(b) 如果我在 onCreate() 中调用 setContentView() 方法,但在 onConfigurationChanged() 中没有调用,则 EditText 数据不会丢失,但布局也不会相应更改。

在这种方法中,甚至不会调用 onSaveInstanceState()。

所以我处于一个非常可怕的境地。这个问题有什么解决办法吗?请帮忙。提前谢谢。

【问题讨论】:

  • @Doomsknight:你能详细说明一下吗?
  • 这里有一些好的想法/例子stackoverflow.com/questions/5179686/… 特别是 Eric Nordvik 的回答,如果其他人不起作用
  • @VijayC :我仍在尝试所有方法。完成后我会立即通知。

标签: android android-layout android-edittext orientation


【解决方案1】:

默认情况下,Edittext 在改变方向时会保存自己的实例。

确保 2 个 Edittexts 具有唯一的 ID,并且在两个布局中具有相同的 ID。

这样,它们的状态应该被保存,你可以让 Android 处理方向变化。

如果您使用的是片段,请确保它也具有唯一 ID,并且在重新创建 Activity 时不要重新创建它。

【讨论】:

  • 确保“片段具有唯一 ID”是什么意思?
  • 每个视图都有一个 ID(EditText 和 Fragments 都是视图)。您要确保此 ID 未被任何其他 View 使用,因为这可能会在尝试自动保存和恢复状态时导致问题。
  • 如果使用 ListView 会怎样?您是否因为每件商品没有唯一 ID 而运气不佳?
  • 如果EditText是用java代码构造的,那么id可以通过预定义的资源xml id <item type= "id" name="some_id">在java中设置editText.setId(R.id.some_id)
  • 我创建了两个xml文件,一个在activity_main另一个是activity_main_land。两者都有 2 个编辑文本字段。两者都有 id 并且在两个 xml 中的 id 都是相同的。当调用 onConfigchange() 方法时,我更改了 xml 文件。现在,当我旋转设备时,编辑文本会丢失数据。你能帮我解决这个问题吗。
【解决方案2】:

更好的方法是让 android 处理方向变化。 Android 会自动从正确的文件夹中获取布局并将其显示在屏幕上。您需要做的就是将编辑文本的输入值保存在 onSaveInsanceState() 方法 中,并使用这些保存的值在 onCreate() 方法中初始化编辑文本
以下是实现此目的的方法:

@Override
protected void onCreate (Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.login_screen);
    ...
    ...
    String userName, password;
    if(savedInstanceState!=null)
    {
        userName = savedInstanceState.getString("user_name");
        password= savedInstanceState.getString("password");
    }

    if(userName != null)
        userNameEdtTxt.setText(userName);
    if(password != null)
        passEdtTxt.setText(password);
}

>

@Override
    protected void onSaveInstanceState (Bundle outState)
    {
        outState.putString("user_name", userNameEdtTxt.getText().toString());
        outState.putString("password",  passEdtTxt.getText().toString());
    }

【讨论】:

  • 如果我正在处理来自 android manifest 的方向,使用 onConfigurationChanged() 方法,会再次调用 onCreate 吗?
  • 这是否会保留每个 EditText 中的光标位置和文本选择? Yalla T. 描述的唯一 ID 方法确实保留了光标位置和文本选择。
【解决方案3】:

在 onConfigurationChanged 方法中,首先在全局变量中获取两个编辑文本的数据,然后调用 setContentView 方法。现在将保存的数据再次设置到编辑文本中。

【讨论】:

  • 这是否会保留每个 EditText 中的光标位置和文本选择? Yalla T. 描述的唯一 ID 方法确实保留了光标位置和文本选择。
【解决方案4】:

给元素一个 id,Android 会为你管理它。

android:id="@id/anything"

【讨论】:

    【解决方案5】:

    有很多方法可以做到这一点。最简单的是您问题中的 2(b)。在您的清单中提及 android:configChanges="orientation|keyboardHidden|screenSize",这样 Activity 就不会在方向更改时被破坏。

    onConfigChange() 中致电setContentView()。但在调用 setContentView() 之前将 EditText 数据转换为字符串并在调用 setContentView() 后将其设置回来

     @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        mEditTextData = mEditText.getText().tostring();//mEditTextData is a String 
                                                       //member variable
        setContentView(R.layout.myLayout);
        initializeViews();
    }
    
    private void initializeViews(){
        mEditText = (EditText)findViewById(R.id.edittext1);
        mEdiText.setText(mEditTextData);
    }
    

    【讨论】:

      【解决方案6】:

      以下应该可以工作并且是活动和片段的标准

      @Override
      public void onSaveInstanceState (Bundle outState) 
      {
           outState.putString("editTextData1", editText1.getText().toString());
           outState.putString("editTextData2", editText2.getText().toString());
      
           super.onSaveInstanceState(outState);
      }
      
      @Override
      public void onCreate(Bundle savedInstanceState)
      {
            super.onCreate();
      
            ... find references to editText1, editText2
      
            if (savedInstanceState != null)
            {
                 editText1.setText(savedInstanceState.getString("editTextData1");
                 editText2.setText(savedInstanceState.getString("editTextData2");
            }
      }
      

      【讨论】:

        【解决方案7】:

        我正在恢复实例以恢复值,它对我来说很好:)

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            // TODO Auto-generated method stub
            super.onCreate(savedInstanceState);
            setContentView(R.layout.addtask2);
            if(savedInstanceState!=null)
             onRestoreInstanceState(savedInstanceState);
        
        }
        

        【讨论】:

          【解决方案8】:

          从清单文件中删除 android:configChanges 属性,让 android 处理方向更改,您在 edittext 中的数据将自动保留。

          现在你提到的问题是进度对话框强制关闭,这是因为当方向改变时,在后台运行的线程正在尝试更新可见的旧对话框组件。您可以通过关闭已保存实例状态方法的对话框并调用您要执行的 onRestoreInstanceState 方法的过程来处理它。

          以下是一个示例,希望它有助于解决您的问题:-

          public class MyActivity extends Activity {
              private static final String TAG = "com.example.handledataorientationchange.MainActivity";
              private static ProgressDialog progressDialog;
              private static Thread thread;
              private static boolean isTaskRunnig;
          
              @Override
              public void onCreate(Bundle savedInstanceState) {
                  super.onCreate(savedInstanceState);
                  Log.d(TAG, "onCreate");
                  setContentView(R.layout.main);
                  Button button = (Button) findViewById(R.id.button);
                  button.setOnClickListener(new EditText.OnClickListener() {
          
                      @Override
                      public void onClick(View v) {
                          perform();
                          isTaskRunnig = true;
                      }
                  });
              }
          
              @Override
              public boolean onCreateOptionsMenu(Menu menu) {
                  getMenuInflater().inflate(R.menu.main, menu);
                  return true;
              }
          
              public void perform() {
                  Log.d(TAG, "perform");
                  progressDialog = android.app.ProgressDialog.show(this, null,
                          "Working, please wait...");
                  progressDialog
                          .setOnDismissListener(new DialogInterface.OnDismissListener() {
          
                              @Override
                              public void onDismiss(DialogInterface dialog) {
                                  //isTaskRunnig = false;
                              }
                          });
                  thread = new Thread() {
                      public void run() {
                          Log.d(TAG, "run");
                          int result = 0;
                          try {
          
                              // Thread.sleep(5000);
                              for (int i = 0; i < 20000000; i++) {
          
                              }
                              result = 1;
                              isTaskRunnig = false;
                          } catch (Exception e) {
                              e.printStackTrace();
                              result = 0;
                          }
                          Message msg = new Message();
                          msg.what = result;
                          handler.sendMessage(msg);
                      };
                  };
                  thread.start();
              }
          
              // handler to update the progress dialgo while the background task is in
              // progress
              private static Handler handler = new Handler() {
          
                  public void handleMessage(Message msg) {
                      Log.d(TAG, "handleMessage");
                      int result = msg.what;
                      if (result == 1) {// if the task is completed successfully
                          Log.d(TAG, "Task complete");
                          try {
                              progressDialog.dismiss();
                          } catch (Exception e) {
                              e.printStackTrace();
                              isTaskRunnig = true;
                          }
          
                      }
          
                  }
              };
          
              @Override
              protected void onRestoreInstanceState(Bundle savedInstanceState) {
                  super.onRestoreInstanceState(savedInstanceState);
                  Log.d(TAG, "onRestoreInstanceState" + isTaskRunnig);
                  if (isTaskRunnig) {
                      perform();
          
                  }
              }
          
              @Override
              protected void onSaveInstanceState(Bundle outState) {
                  super.onSaveInstanceState(outState);
                  Log.d(TAG, "onSaveInstanceState");
                  if (thread.isAlive()) {
                      thread.interrupt();
                      Log.d(TAG, thread.isAlive() + "");
                      progressDialog.dismiss();
                  }
          
              }
          

          【讨论】:

          【解决方案9】:

          正如 Yalla T 所指出的,重要的是不要重新创建片段。如果重用现有片段,EditText 不会丢失其内容。

          @Override
          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              // setContentView(R.layout.activity_frame);
              getSupportActionBar().setDisplayHomeAsUpEnabled(true);
          
              // Display the fragment as the main content.
              // Do not do this. It will recreate the fragment on orientation change!
              // getSupportFragmentManager().beginTransaction().replace(android.R.id.content, new Fragment_Places()).commit();
          
              // Instead do this
              String fragTag = "fragUniqueName";
              FragmentManager fm = getSupportFragmentManager();
              Fragment fragment = (Fragment) fm.findFragmentByTag(fragTag);
              if (fragment == null)
                  fragment = new Fragment_XXX(); // Here your fragment
              FragmentTransaction ft = fm.beginTransaction();
              // ft.setCustomAnimations(R.xml.anim_slide_in_from_right, R.xml.anim_slide_out_left,
              // R.xml.anim_slide_in_from_left, R.xml.anim_slide_out_right);
              ft.replace(android.R.id.content, fragment, fragTag);
              // ft.addToBackStack(null); // Depends on what you want to do with your back button
              ft.commit();
          
          }
          

          【讨论】:

            【解决方案10】:

            保存状态 = 保存(片段状态 + 活动状态)

            当涉及到在方向变化期间保存 Fragment 的状态时,我通常会这样做。

            1) 片段状态:

            保存和恢复 EditText 值

            // Saving State
            
            @Override
            public void onSaveInstanceState(Bundle outState) {
                super.onSaveInstanceState(outState);
                outState.putString("USER_NAME", username.getText().toString());
                outState.putString("PASSWORD", password.getText().toString());
            }
            
            @Override
            public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
            
                View view = inflater.inflate(R.layout.user_name_fragment, parent, false);
            
                username = (EditText) view.findViewById(R.id.username);
                password = (EditText) view.findViewById(R.id.password);
            
            
            // Retriving value
            
                if (savedInstanceState != null) {
                    username.setText(savedInstanceState.getString("USER_NAME"));
                    password.setText(savedInstanceState.getString("PASSWORD"));
                }
            
                return view;
            }
            

            2) 活动状态:

            在活动第一次启动时创建一个新实例 否则使用 TAGFragmentManager

            找到旧片段
            @Override
            protected void onCreate(Bundle savedInstanceState)
            {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.main_activity);
            
                fragmentManager = getSupportFragmentManager();
            
                if(savedInstanceState==null) {
                    userFragment = UserNameFragment.newInstance();
                    fragmentManager.beginTransaction().add(R.id.profile, userFragment, "TAG").commit();
                }
                else {
                    userFragment = fragmentManager.findFragmentByTag("TAG");
                }
            
            }
            

            你可以看到完整的工作代码HERE

            【讨论】:

              【解决方案11】:

              下面的代码对我有用。需要注意两件事。

              1. 每个输入字段(编辑文本或 TextInputEditText)分配唯一的 ID。
              2. 清单活动声明应具有以下值的配置更改属性。

                android:configChanges="orientation|keyboardHidden|screenSize"

              清单中的示例活动声明。

              <activity
                    android:name=".screens.register.RegisterActivity"
                    android:configChanges="orientation|keyboardHidden|screenSize"
                    android:exported="true"
                    android:label="Registration"
                    android:theme="@style/AppTheme.NoActionBar" />
              

              声明示例

               <com.google.android.material.textfield.TextInputLayout
                      android:id="@+id/inputLayout"
                      style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
                      android:layout_width="match_parent"
                      android:layout_height="wrap_content"
                      app:boxCornerRadiusBottomEnd="@dimen/boxCornerRadiusDP"
                      app:boxCornerRadiusBottomStart="@dimen/boxCornerRadiusDP"
                      app:boxCornerRadiusTopEnd="@dimen/boxCornerRadiusDP"
                      app:boxCornerRadiusTopStart="@dimen/boxCornerRadiusDP">
              
                      <com.google.android.material.textfield.TextInputEditText
                          android:id="@+id/inputEditText"
                          android:layout_width="match_parent"
                          android:layout_height="wrap_content"
                          android:focusable="true"
                          android:fontFamily="@font/proxima_nova_semi_bold"
                          android:inputType="textCapWords"
                          android:lines="1"
                          android:textColor="@color/colorInputText"
                          android:textColorHint="@color/colorInputText" />
                  </com.google.android.material.textfield.TextInputLayout>
              

              【讨论】:

                【解决方案12】:

                这可能对你有帮助

                如果您的 android:targetSdkVersion="12" 或更少

                android:configChanges="orientation|keyboardHidden">
                

                如果您的 android:targetSdkVersion="13" 或更多

                android:configChanges="orientation|keyboardHidden|screenSize">
                

                【讨论】:

                • 如果这已经足够了,我什至不会问这个问题。请仔细阅读问题。
                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2019-10-06
                相关资源
                最近更新 更多