在不丢失数据的情况下处理屏幕旋转 - Android

Posted

技术标签:

【中文标题】在不丢失数据的情况下处理屏幕旋转 - Android【英文标题】:Handle screen rotation without losing data - Android 【发布时间】:2012-04-24 23:48:37 【问题描述】:

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

如何在重新创建 Activity 之前保存 myClass 数据,这样我就可以保留用于重绘 Activity 的所有内容,而无需进行其他无用的初始化?

有没有比parcelable更干净更好的方式?

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

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.
    

【问题讨论】:

使用onConfigurationChanged的东西,见:***.com/questions/456211/… 【参考方案1】:

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

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

如果您使用的是 Android 2.3(API 级别 13)及更高版本,请使用

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

它应该可以工作。

它只适用于 activity 标签,不适用于 application 标签

【讨论】:

我的问题在于 screenSize 属性,感谢 Prashant【参考方案2】:

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

注意:您的自定义类必须实现下面的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();
 



【讨论】:

但我需要做什么 onSaveInstanceState 和 onRestoreInstanceState ?实现parcelable? pffff 不存在任何方法 + 简单?我需要从 AutoCompleteTextView 扩展的类 InstantAutoComplete 从 EditText 扩展实现 Filter.FilterListener .... 我失去了 1 天的时间来获取所有值 .... super.onSaveInstanceState(outState); 最终被调用。【参考方案3】:

可能这个问题已经解决了,但是对于坚持使用它的新成员来说只是一个小更新,只需查看Google Developer Site,从 API 级别 13 开始,您只需将此代码添加到 Manifest:

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

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

【讨论】:

【参考方案4】:

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

现在你可以做的是,找出正在失去状态的变量。

当您旋转您的设备时,您当前的活动被完全破坏,即通过 onSaveInstanceState() onPause() onStop() onDestroy() 并完全创建一个新活动,通过onCreate() onStart() onRestoreInstanceState。

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

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

 @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
    

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

ViewModel 将负责要显示的数据。 Activity 将负责如何显示数据。 如果您存储数据的复杂性越来越高,请使用额外的存储库类。

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

【讨论】:

【参考方案5】:

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

    android:configChanges="keyboard|keyboardHidden|orientation"

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

【讨论】:

事实并非如此,Google 开发人员建议不要采用这种做法,因为这是假设方向更改是 Activity 可能被处置和重新创建的唯一原因,而事实上还有许多其他可能发生这种情况的原因。【参考方案6】:

对此有两种(好的)方法。让你的类实现 Parcelable 并将其放在 onSaveInstanceState() 的一个包中,或者,如果它更复杂(例如 AsyncTask),则将它返回到 onRetainNonConfigurationInstance()

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

【讨论】:

这似乎已被弃用!是的,这是一个相当复杂的对象——onRetainNonConfigurationInstance() 是的,他们弃用了onRetainNonConfigurationInstance(),但据我所知没有提供替代方案。 @dmon 可以使用没有 UI 的片段作为替代方案:developer.android.com/reference/android/app/…【参考方案7】:

它解决了我的问题。

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

【讨论】:

【参考方案8】:

来自文档:

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

所以你应该写的是:

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

【讨论】:

以上是关于在不丢失数据的情况下处理屏幕旋转 - Android的主要内容,如果未能解决你的问题,请参考以下文章

Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案

Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案

在不丢失引用的情况下更新 ImmutableJS OrderedMap 上的记录

如何在不丢失身份验证的情况下模拟Session.request?

在不丢失 Exif 数据的情况下调整图像大小?

Spring JPA:如何在不丢失数据的情况下进行更新插入