处理屏幕旋转而不丢失数据 - Android

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10126845/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 02:35:46  来源:igfitidea点击:

Handle screen rotation without losing data - Android

androidandroid-layoutandroid-intent

提问by Riccardo Neri

I'm becoming crazy figuring out what is the best way to handle screen rotation. I read hundreds of questions/answers here but I'm really confused.

我正在疯狂地弄清楚处理屏幕旋转的最佳方法是什么。我在这里阅读了数百个问题/答案,但我真的很困惑。

How can I save myClass data before the activity is re-created so I can keep everything for redrawing activity without another useless initialization?

如何在重新创建活动之前保存 myClass 数据,以便我可以保留重绘活动的所有内容,而无需再次进行无用的初始化?

Is there a cleaner and better way than parcelable?

有没有比 Parcelable 更清洁更好的方法?

I need to handle rotation because I want to change layout in Landscape mode.

我需要处理旋转,因为我想在横向模式下更改布局。

public class MtgoLifecounterActivity extends Activity {

    MyClass myClass;

    // Called when the activity is first created
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        If ( ?? first run...myClass == null ? ) {
            myClass = new MyClass();
        } else {
            // do other stuff but I need myClass istance with all values.
        }
        // I want that this is called only first time. 
        // then in case of rotation of screen, i want to restore the other instance of myClass which
        // is full of data.
    }

采纳答案by Mohammed Azharuddin Shaikh

can use override method onSaveInstanceState()and onRestoreInstanceState(). or to stop calling onCreate()on screen rotation just add this line in your manifest xml android:configChanges="keyboardHidden|orientation"

可以使用覆盖方法onSaveInstanceState()onRestoreInstanceState()。或停止调用onCreate()屏幕旋转只需在清单 xml 中添加这一行android:configChanges="keyboardHidden|orientation"

note: your custom class must implements Parcelableexample below.

注意:您的自定义类必须实现Parcelable下面的示例。

@Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putParcelable("obj", myClass);
    }

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
 // TODO Auto-generated method stub
 super.onRestoreInstanceState(savedInstanceState);
 myClass=savedInstanceState.getParcelable("obj"));
}

public class MyParcelable implements Parcelable {
     private int mData;

 public int describeContents() {
     return 0;
 }

 /** save object in parcel */
 public void writeToParcel(Parcel out, int flags) {
     out.writeInt(mData);
 }

 public static final Parcelable.Creator<MyParcelable> CREATOR
         = new Parcelable.Creator<MyParcelable>() {
     public MyParcelable createFromParcel(Parcel in) {
         return new MyParcelable(in);
     }

     public MyParcelable[] newArray(int size) {
         return new MyParcelable[size];
     }
 };

 /** recreate object from parcel */
 private MyParcelable(Parcel in) {
     mData = in.readInt();
 }


}

回答by Ragnar

In Activity Tag of Manifest you should have to mention

在清单的活动标签中,你应该提到

<activity
        android:name="com.example.ListActivity"
        android:label="@string/app_name" 
        android:configChanges="keyboardHidden|orientation">

If you are using Android 2.3(API level 13 ) and above use

如果您使用的是 Android 2.3(API 级别 13)及以上,请使用

<activity
        android:name="com.example.Activity"
        android:label="@string/app_name" 
        android:configChanges="keyboardHidden|orientation|screenSize">

It should have to work.

它应该工作。

It will work only with activitytag and not with applicationtag

它仅适用于活动标签而不适用于应用程序标签

回答by Zak Chapman

May be this is solved already but just for a small update for new members who stuck on it, just have a look at Google Developer Site, From API level 13, you just need to add this code to Manifest:

可能这已经解决了,但只是对坚持它的新成员的一个小更新,看看谷歌开发者网站,从 API 级别 13,你只需要将此代码添加到清单:

<activity android:name=".SplashScreensActivity"
          android:configChanges="orientation|keyboardHidden|screenSize"
          android:label="@string/app_name">

when one of these configurations change, SplashScreensActivity does not restart. Instead, the SplashScreensActivity receives a call to onConfigurationChanged(). This method is passed a Configuration object that specifies the new device configuration. By reading fields in the Configuration, you can determine the new configuration and make appropriate changes by updating the resources used in your interface. At the time this method is called, your activity's Resources object is updated to return resources based on the new configuration, so you can easily reset elements of your UI without the system restarting your activity.

当这些配置之一发生更改时,SplashScreensActivity 不会重新启动。相反,SplashScreensActivity 会收到对 onConfigurationChanged() 的调用。此方法传递一个 Configuration 对象,该对象指定新的设备配置。通过阅读配置中的字段,您可以确定新配置并通过更新界面中使用的资源进行适当的更改。在调用此方法时,您的 Activity 的 Resources 对象会更新以根据新配置返回资源,因此您可以轻松重置 UI 元素,而无需系统重新启动您的 Activity。

回答by devDeejay

The problem here is that you are losing the "state" of the App. In OOPs, What is a State? The Variables! Exactly! Hence when you are losing the data of your variables.

这里的问题是您正在失去应用程序的“状态”。在 OOP 中,什么是状态?变量!确切地!因此,当您丢失变量数据时。

Now here is what you can do, Find Out the variables which are losing their states.

现在您可以这样做,找出正在失去状态的变量。

enter image description here

在此处输入图片说明

When you rotate your device, your present activity gets completely destroyed, ie goes through onSaveInstanceState()onPause() onStop() onDestroy()and a new activity is created completely which goes through onCreate() onStart()onRestoreInstanceState.

当您旋转设备时,您当前的 Activity 将被完全销毁,即通过onSaveInstanceState()onPause() onStop() onDestroy()并完全创建一个通过onCreate() onStart()onRestoreInstanceState的新Activity

The Two Methods in the bold, onSaveInstanceState()saves the instance of the present activity which is going to be destroyed. onRestoreInstanceStateThis method restores the saved state of the previous activity. This way you don't lose your previous state of the app.

粗体中的两个方法onSaveInstanceState()保存将要销毁的当前活动的实例。onRestoreInstanceState该方法恢复之前活动的保存状态。这样您就不会丢失应用程序的先前状态。

Here is how you use these methods.

以下是您如何使用这些方法。

 @Override
    public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
        super.onSaveInstanceState(outState, outPersistentState);

        outState.putString("theWord", theWord); // Saving the Variable theWord
        outState.putStringArrayList("fiveDefns", fiveDefns); // Saving the ArrayList fiveDefns
    }

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

        theWord = savedInstanceState.getString("theWord"); // Restoring theWord
        fiveDefns = savedInstanceState.getStringArrayList("fiveDefns"); //Restoring fiveDefns
    }

EDIT : A Better Approach:The above approach toward maintaining your data isn't the best way to maintain data in production code/apps. Google IO 2017 introduced ViewModel to protect your data against configurational changes (like Screen Rotation). Keeping all data inside the activity using variables isn't a good software design and violates the Single Responsibility Principlehence separate your data storage using ViewModel from the activities.

编辑:更好的方法:上述维护数据的方法不是在生产代码/应用程序中维护数据的最佳方法。Google IO 2017 引入了 ViewModel 来保护您的数据免受配置更改(如屏幕旋转)的影响。使用变量将所有数据保存在活动中并不是一个好的软件设计,并且违反了单一职责原则,因此将使用 ViewModel 的数据存储与活动分开。

  • ViewModel will be responsible for the data to be displayed.
  • Activity will be responsible for how to display the data.
  • Use additional repository class if you have an increasing complexity of storing the data.
  • ViewModel 将负责要显示的数据。
  • Activity 将负责如何显示数据。
  • 如果存储数据的复杂性越来越高,请使用额外的存储库类。

That's just one of the way to separate classes and their responsibility, which will go a long way while making well-architectured apps.

这只是分离类及其职责的一种方式,这对于构建结构良好的应用程序大有帮助。

回答by dmon

There are two (good) ways about this. Make your class implement Parcelable and put it in a bundle in onSaveInstanceState(), or, if it's more complex (e.g. an AsyncTask), return it in onRetainNonConfigurationInstance().

对此有两种(好的)方法。使您的类实现 Parcelable 并将onSaveInstanceState()其放入onRetainNonConfigurationInstance().

Then there's also the lazy way where you just stop reacting to configuration changes.

然后还有一种懒惰的方式,您只需停止对配置更改做出反应。

回答by Bobbake4

If you do not have a need for your activity to be restarted just set the configChanges attribute on your activity in the AndroidManifest.xml to this:

如果您不需要重新启动您的活动,只需将 AndroidManifest.xml 中您的活动的 configChanges 属性设置为:

    android:configChanges="keyboard|keyboardHidden|orientation"

This will tell the OS that you are going to take care of handling a rotation and will not restart your activity. Using this method will remove the need for you to have to save any kind of state.

这将告诉操作系统您将负责处理轮换并且不会重新启动您的活动。使用此方法将使您无需保存任何类型的状态。

回答by Mohammed Fathi

from the documentation:

从文档:

Note: If your application targets Android 3.2 (API level 13) or higher, then you should also declare the "screenSize" and "screenLayout" configurations, because they might also change when a device switches between portrait and landscape orientations.

注意:如果您的应用程序面向 Android 3.2(API 级别 13)或更高版本,那么您还应该声明“screenSize”和“screenLayout”配置,因为当设备在纵向和横向方向之间切换时,它们也可能会发生变化。

so what you shpuld write will be :

所以你应该写的是:

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