android 横竖屏幕切换

本文详细探讨了Android中横竖屏切换的工作原理,包括如何使用android:screenOrientation和android:configChanges属性来控制屏幕方向及配置变化。此外,文章还深入分析了横竖屏切换对Activity和Fragment生命周期的影响,并提供了实用的代码示例。

Android开发中,大多APP可能根据实际情况直接将APP的界面方向设死了,或竖屏或横屏。但是,我们还是会遇到横竖屏切换的功能需求,不管是通过物理重力感应触发,还是用户手动触发。所以,我们有必要去弄清楚Android中横竖屏切换到底做了什么。

Follow me ......

一、android:screenOrientation & android:configChanges

  • android:screenOrientation
    是用来设置 activity在设备上的显示方向的,只能是下面的唯一值:
描述
unspecified默认值。系统自动选择屏幕方向
behind跟activity堆栈中的下面一个activity的方向一致
landscape横屏方向,显示的宽比高长
portrait竖屏方向,显示的高比宽长
sensor由设备的物理方向传感器决定,如果用户旋转设备,这屏幕就会横竖屏切换
nosensor忽略物理方向传感器,这样就不会随着用户旋转设备而横竖屏切换了("unspecified"设置除外)
user用户当前首选的方向
reverseLandscapeAPI 9 以上,反向横屏
reversePortraitAPI 9 以上,反向竖屏
sensorLandscapeAPI 9 以上,横屏,但是可以根据 物理方向传感器来切换正反向横屏
sensorPortraitAPI 9 以上,竖屏,但是可以根据 物理方向传感器来切换正反向竖屏
fullSensorAPI 9 以上,上下左右四个方向,由物理方向传感器决定
lockedAPI 18 以上,锁死当前屏幕的方向

如果你上传应用到 Google Play,需要注意以下提示:
Note: When you declare one of the landscape or portrait values, it is considered a hard requirement for the orientation in which the activity runs. As such, the value you declare enables filtering by services such as Google Play so your application is available only to devices that support the orientation required by your activities. For example, if you declare either"landscape", "reverseLandscape", or "sensorLandscape", then your application will be available only to devices that support landscape orientation. However, you should also explicitly declare that your application requires either portrait or landscape orientation with the <uses-feature>
element. For example, <uses-feature android:name="android.hardware.screen.portrait"/>. This is purely a filtering behavior provided by Google Play (and other services that support it) and the platform itself does not control whether your app can be installed when a device supports only certain orientations.

  • android:configChanges
    用来设置 activity配置改变(不仅仅是屏幕方向,还有语言、地区等等)的集合。当一个配置改变在运行中发生时,activity默认情况下会先销毁然后重新创建。但是,如果通过这个属性声明了某个配置后,将可以避免上面的情况,而是依然运行,并回调 onConfigurationChanged() 方法。该属性可以设置多个值:
描述
mccIMSI移动台的国家代码(MCC)发生变化——一个SIM被探测到并且更新MCC
mncIMSI移动台的网络代码(MNC)发生变化——一个SIM被探测到并且更新MNC
locale区域发生变化——用户选择了一个文本需要显示的新语言
keyboard键盘类型发生变化——例如:用户插入了外接键盘。
keyboardHidden键盘的可访问性发生变化——例如:用户发现了硬件键盘。
screenLayout屏幕布局发生变化——这个会导致显示不同的Activity。
orientation屏幕方向发生变化——用户旋转了屏幕。注意:如果应用程序的目标API级别是13或更高(通过属性minSdkVersion和属性targetSdkVersion声明),你也需要声明配置项screenSize,因为这将在设备选择肖像和屏幕方向时发生改变。
screenSize当前可用屏幕大小发生变化。这代表一个当前可用大小的变化,和当前的比率相关,因此当用户选择不同的画面和图像,会发生变化。然而,如果你的程序目标API级别是12或更低,你的Activity总是会自己处理这个配置变化(这个变化不会引起Activity的重启,甚至在Android 3.2或更新的设备上)。在API级别13里加入的。
smallestScreenSize物理屏幕大小的变化。不管方向的变化,仅仅在实际物理屏幕打包变化的时候,如:外接显示器。这个配置项的变化引起在smallestWidth configuration里的变化。然而,如果你的程序目标API级别是12或更低,你的Activity将自己处理这个变化(这个变化不会引起Activity的重启,甚至在Android 3.2或更新的设备上)在API级别13里加入的。
layoutDirection布局方向变化。例如书写方式从左向右(LTR)转换为从右向左(RTL)

想了解更多就看官方文档

二、横竖屏切换对Activity生命周期的影响

通过打印Activity各个生命周期的执行情况,我们根据以下几种情况来分析:

1. 不配置 configChanges
2. 配置 android:configChanges="orientation"
3. 配置android:configChanges="orientation|keyboardHidden"
  • 竖屏切横屏
11-02 20:17:44.898 13324-13324/com.ys.yoosir.screenconfigchange D/MainActivity:  -- onPause
11-02 20:17:45.008 13324-13324/com.ys.yoosir.screenconfigchange D/MainActivity:  -- onSaveInstanceState
11-02 20:17:45.008 13324-13324/com.ys.yoosir.screenconfigchange D/MainActivity:  -- onStop
11-02 20:17:45.018 13324-13324/com.ys.yoosir.screenconfigchange D/MainActivity:  -- onDestroy
11-02 20:17:45.038 13324-13324/com.ys.yoosir.screenconfigchange D/MainActivity:  -- onCreate - orientation
11-02 20:17:45.088 13324-13324/com.ys.yoosir.screenconfigchange D/MainActivity:  -- onStart
11-02 20:17:45.088 13324-13324/com.ys.yoosir.screenconfigchange D/MainActivity:  -- onRestoreInstanceState
11-02 20:17:45.088 13324-13324/com.ys.yoosir.screenconfigchange D/MainActivity:  -- onResume
  • 横屏切竖屏:
11-02 20:19:00.268 13324-13324/com.ys.yoosir.screenconfigchange D/MainActivity:  -- onPause
11-02 20:19:00.268 13324-13324/com.ys.yoosir.screenconfigchange D/MainActivity:  -- onSaveInstanceState
11-02 20:19:00.268 13324-13324/com.ys.yoosir.screenconfigchange D/MainActivity:  -- onStop
11-02 20:19:00.268 13324-13324/com.ys.yoosir.screenconfigchange D/MainActivity:  -- onDestroy
11-02 20:19:00.318 13324-13324/com.ys.yoosir.screenconfigchange D/MainActivity:  -- onCreate - orientation
11-02 20:19:00.338 13324-13324/com.ys.yoosir.screenconfigchange D/MainActivity:  -- onStart
11-02 20:19:00.338 13324-13324/com.ys.yoosir.screenconfigchange D/MainActivity:  -- onRestoreInstanceState
11-02 20:19:00.338 13324-13324/com.ys.yoosir.screenconfigchange D/MainActivity:  -- onResume

从上面的 3种方式配置 打印的 Log (三种方式 Log是一样的,故合并显示),我们可以总结如下:

不设置Activity的android:configChanges时,或 设置Activity的android:configChanges="orientation"时,或设置Activity的android:configChanges="orientation|keyboardHidden"时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行一次方法。

而我们经常在其他地方看到的结论如下:

1、不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次
2、设置Activity的android:configChanges="orientation"时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次
3、设置Activity的android:configChanges="orientation|keyboardHidden"时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法

4. 所以是什么原因导致我们的结论不一样呢?

需要说明的是:** 我的测试环境是 targetSdkVersion 24,测试模拟器是 系统API 5.0!**

查看官方文档,发现有如下提示:

注意:从 Android 3.2(API 级别 13)开始,当设备在纵向和横向之间切换时,“屏幕尺寸”也会发生变化。因此,在开发针对 API 级别 13 或更高版本系统的应用时,若要避免由于设备方向改变而导致运行时重启(正如 minSdkVersion
targetSdkVersion
属性中所声明),则除了"orientation"值以外,您还必须添加 "screenSize"值。即,您必须声明 android:configChanges="orientation|screenSize"。但是,如果您的应用是面向 API 级别 12 或更低版本的系统,则 Activity 始终会自行处理此配置变更(即便是在 Android 3.2 或更高版本的设备上运行,此配置变更也不会重启 Activity)。

我们现在测试第四种配置方式。

5. 配置 android:configChanges="orientation|keyboardHidden|screenSize"
  • 竖屏切横屏
11-02 20:44:06.568 7792-7792/com.ys.yoosir.screenconfigchange D/MainActivity:  -- onConfigurationChanged
  • 横屏切竖屏
11-02 20:44:52.918 7792-7792/com.ys.yoosir.screenconfigchange D/MainActivity:  -- onConfigurationChanged

结论: 从 API 13开始,配置 android:configChanges="orientation|keyboardHidden|screenSize",才不会销毁 activity,且只调用 onConfigurationChanged方法。

6. 横竖屏切换小结
  • (一)、Android 3.2 (API 级别 13)以前
    1、不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次。
    2、设置Activity的android:configChanges="orientation"时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次。
    3、设置Activity的android:configChanges="orientation|keyboardHidden"时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法。

注意: 不设置Activity的android:configChanges时,切换竖屏activity的各个生命周期执行两次,有人说是 在API 2.x下,我未测试,大家可以考证下。

  • (二)、从 Android 3.2 (API级别 13)开始
    1、不设置Activity的android:configChanges,或设置Activity的android:configChanges="orientation",或设置Activity的android:configChanges="orientation|keyboardHidden",切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行一次。
    2、配置 android:configChanges="orientation|keyboardHidden|screenSize",才不会销毁 activity,且只调用 onConfigurationChanged方法。

三、代码中动态切换横竖屏

在代码中切换屏幕的方向主要调用 setRequestedOrientation(int requestedOrientation) 方法,此方法的作用等同于在 AndroidManifest.xml设置Activity 的android:screenOrientation,所以,其可传递的参数如 android:screenOrientation表格中一样。

示例代码:

findViewById(R.id.btn_click).setOnClickListener(new View.OnClickListener() {  
  @Override 
   public void onClick(View view) {   
     Log.d(TAG,"被点击了");  
      //判断当前屏幕方向  
      if(getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {     
       //切换竖屏   
         MainActivity.this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);   
      }else{   
         //切换横屏     
         MainActivity.this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);  
      } 
   }
});

非重启Activity模式:即设置了android:configChanges="orientation|keyboardHidden|screenSize",Log输出(横竖屏切换一样)

11-03 10:38:33.998 20317-20317/com.ys.yoosir.screenconfigchange D/MainActivity: 被点击了
11-03 10:38:34.108 20317-20317/com.ys.yoosir.screenconfigchange D/MainActivity:  -- onConfigurationChanged

重启Activity模式:即没有设置android:configChanges="orientation|keyboardHidden|screenSize",Log输出(横竖屏切换一样)

11-03 10:59:23.268 7724-7724/com.ys.yoosir.screenconfigchange D/MainActivity: 被点击了
11-03 10:59:23.368 7724-7724/com.ys.yoosir.screenconfigchange D/MainActivity:  -- onPause
11-03 10:59:23.368 7724-7724/com.ys.yoosir.screenconfigchange D/MainActivity:  -- onSaveInstanceState
11-03 10:59:23.368 7724-7724/com.ys.yoosir.screenconfigchange D/MainActivity:  -- onStop
11-03 10:59:23.368 7724-7724/com.ys.yoosir.screenconfigchange D/MainActivity:  -- onDestroy
11-03 10:59:23.428 7724-7724/com.ys.yoosir.screenconfigchange D/MainActivity:  -- onCreate - orientation
11-03 10:59:23.458 7724-7724/com.ys.yoosir.screenconfigchange D/MainActivity:  -- onStart
11-03 10:59:23.458 7724-7724/com.ys.yoosir.screenconfigchange D/MainActivity:  -- onRestoreInstanceState
11-03 10:59:23.458 7724-7724/com.ys.yoosir.screenconfigchange D/MainActivity:  -- onResume

注意: 通过 setRequestedOrientation(int requestedOrientation) 修改了屏幕方向后,就类似于设置了android:screenOrientation,效果是一样的,比如:调用setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT),无论屏幕怎么旋转,都不会切换屏幕方向。如果要恢复为响应横竖屏随物理方向传感器设备变换,那么就需要手动调用类似setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);代码进行恢复。

四、横竖屏切换我们需要做的事

  • 重启Activity模式
    在重启Activity模式下,横竖屏切换的时候会导致数据丢失,我们可以通过如下代码来保正数据不丢失:
 @Overrideprotected
 void onSaveInstanceState(Bundle outState) { 
   Log.d(TAG," -- onSaveInstanceState"); 
   Log.d(TAG," -- onSaveInstanceState save: name = yoosir,age = 24,handsome = ture"); 
   outState.putString("name","yoosir"); 
   outState.putInt("age",24); 
   outState.putBoolean("handsome",true); 
   super.onSaveInstanceState(outState);
}
....
 @Overrideprotected
 void onRestoreInstanceState(Bundle savedInstanceState) { 
   super.onRestoreInstanceState(savedInstanceState); 
   Log.d(TAG," -- onRestoreInstanceState"); 
   if(savedInstanceState != null) { 
       String name = savedInstanceState.getString("name"); 
       int age = savedInstanceState.getInt("age"); 
       boolean isHandsome = savedInstanceState.getBoolean("handsome"); 
       Log.d(TAG, " -- onRestoreInstanceState get: name = " + name + ",age = " + age + ",handsome = " + isHandsome); 
   }}

横竖屏切换Log 输出:

11-03 12:03:31.678 3663-3663/com.ys.yoosir.screenconfigchange D/MainActivity:  -- onPause
11-03 12:03:31.678 3663-3663/com.ys.yoosir.screenconfigchange D/MainActivity:  -- onSaveInstanceState
11-03 12:03:31.678 3663-3663/com.ys.yoosir.screenconfigchange D/MainActivity:  -- onSaveInstanceState save: name = yoosir,age = 24,handsome = ture
11-03 12:03:31.688 3663-3663/com.ys.yoosir.screenconfigchange D/MainActivity:  -- onStop
11-03 12:03:31.688 3663-3663/com.ys.yoosir.screenconfigchange D/MainActivity:  -- onDestroy
11-03 12:03:31.758 3663-3663/com.ys.yoosir.screenconfigchange D/MainActivity:  -- onCreate - orientation
11-03 12:03:31.758 3663-3663/com.ys.yoosir.screenconfigchange D/MainActivity:  -- onCreate get: name = yoosir,age = 24,handsome = true
11-03 12:03:31.788 3663-3663/com.ys.yoosir.screenconfigchange D/MainActivity:  -- onStart
11-03 12:03:31.788 3663-3663/com.ys.yoosir.screenconfigchange D/MainActivity:  -- onRestoreInstanceState
11-03 12:03:31.788 3663-3663/com.ys.yoosir.screenconfigchange D/MainActivity:  -- onRestoreInstanceState get: name = yoosir,age = 24,handsome = true
11-03 12:03:31.788 3663-3663/com.ys.yoosir.screenconfigchange D/MainActivity:  -- onResume

如上,大家可以看到在 onCreate() 中也是可以拿到我们之前保存的数据的。

补充一点: 如果大家在资源目录res 中添加了 layout-land(横向布局文件夹) 和 layout-port (竖想布局文件夹),重启Activity模式的横竖屏切换,系统会自动帮我们显示正确方向的布局UI。

Paste_Image.png

  • 非重启Activity模式
    非重启Activity模式下横竖屏切换,我们的Activity 不会销毁重建,数据也不会丢失。但是,如果我们想根据不同的屏幕方向来展示不同UI或做不同的事,应该怎么做呢?直接上代码:
@Overridepublic void onConfigurationChanged(Configuration newConfig) { 
   super.onConfigurationChanged(newConfig); 
   Log.d(TAG," -- onConfigurationChanged"); 
   if(newConfig.orientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT){ 
       //切换到竖屏  
      //修改布局文件  
      setContentView(R.layout.activity_main);  
      //findViewById ....   
     //TODO something  
      Log.d(TAG," -- onConfigurationChanged  可以在竖屏方向 to do something"); 
   }else{  
      //切换到横屏  
      //修改布局文件  
      setContentView(R.layout.activity_main);  
      //findViewById ....  
      //TODO something  
      Log.d(TAG," -- onConfigurationChanged  可以在横屏方向 to do something"); 
   }
}

横竖屏切换Log 输出:

11-03 14:56:50.465 28612-28612/com.ys.yoosir.screenconfigchange D/MainActivity:  -- onConfigurationChanged
11-03 14:56:50.495 28612-28612/com.ys.yoosir.screenconfigchange D/MainActivity:  -- onConfigurationChanged  可以在横屏方向 to do something

大家可以看到,在非重启Activity模式下,横竖屏切换修改UI布局文件时,其实挺麻烦的(我是这样的看法,^_~),需要重新初始化一遍UI。不过,不需要我们手动保存数据倒是挺方便的。

五、横竖屏切换对Fragment的影响

打印 Activity 和 Fragment 各个生命周期,并在Activity 的 onCreate() 中添加如下代码:

//是否已经 add 了 Fragment
if(getSupportFragmentManager().findFragmentByTag("child") == null) {
    Log.d(TAG, " -- onCreate has no child ");
    FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
    blankFragment = BlankFragment.newInstance("one", "two");
    fragmentTransaction.add(R.id.root_view, blankFragment, "child");
    fragmentTransaction.commit();
}else{
    Log.d(TAG, " -- onCreate has child ");
}
  • 重建Activity模式
    Log:
    带有 BlankFragment 即为 Fragment 的打印,其他则为 Activity 的打印
    进入Activity的Log: 我们可以关注下第 2 行log——-- onCreate has no child
11-03 17:56:19.465 26990-26990/com.ys.yoosir.screenconfigchange D/Main2Activity:  -- onCreate - orientation
11-03 17:56:19.525 26990-26990/com.ys.yoosir.screenconfigchange D/Main2Activity:  -- onCreate has no child 
11-03 17:56:19.545 26990-26990/com.ys.yoosir.screenconfigchange D/Main2Activity:  BlankFragment -- newInstance 
11-03 17:56:19.545 26990-26990/com.ys.yoosir.screenconfigchange D/Main2Activity:  BlankFragment -- onAttach 
11-03 17:56:19.545 26990-26990/com.ys.yoosir.screenconfigchange D/Main2Activity:  BlankFragment -- onCreate 
11-03 17:56:19.545 26990-26990/com.ys.yoosir.screenconfigchange D/Main2Activity:  BlankFragment -- onCreateView 
11-03 17:56:19.585 26990-26990/com.ys.yoosir.screenconfigchange D/Main2Activity:  BlankFragment -- onActivityCreated 
11-03 17:56:19.585 26990-26990/com.ys.yoosir.screenconfigchange D/Main2Activity:  BlankFragment -- onStart 
11-03 17:56:19.585 26990-26990/com.ys.yoosir.screenconfigchange D/Main2Activity:  -- onStart
11-03 17:56:19.585 26990-26990/com.ys.yoosir.screenconfigchange D/Main2Activity:  -- onResume
11-03 17:56:19.585 26990-26990/com.ys.yoosir.screenconfigchange D/Main2Activity:  BlankFragment -- onResume 
11-03 17:57:02.285 26990-26990/com.ys.yoosir.screenconfigchange D/Main2Activity: -- one btn click 
11-03 17:57:05.705 26990-26990/com.ys.yoosir.screenconfigchange D/Main2Activity: -- two btn click 

横竖屏切换的Log:

  • 1、我们关注一下 第17行 onCreate has child ,所以** Fragment 本身是没有被 destroy,它的views 是被destroy 的**
  • 2、关注一下 第5、6、19 和 20 行,Fragment 的局部变量数据被销毁了,我们可以在onSaveInstanceState 保存数据,虽然 Fragment 不像 Activity 拥有 onRestoreInstanceState 方法,但是我们可以在 onActivityCreated 中获取之前保存的数据。
11-03 17:57:40.095 26990-26990/com.ys.yoosir.screenconfigchange D/Main2Activity:  BlankFragment -- onPause 
11-03 17:57:40.095 26990-26990/com.ys.yoosir.screenconfigchange D/Main2Activity:  -- onPause
11-03 17:57:40.095 26990-26990/com.ys.yoosir.screenconfigchange D/Main2Activity:  -- onSaveInstanceState
11-03 17:57:40.095 26990-26990/com.ys.yoosir.screenconfigchange D/Main2Activity:  -- onSaveInstanceState save: name = yoosir,age = 24,handsome = ture
11-03 17:57:40.095 26990-26990/com.ys.yoosir.screenconfigchange D/Main2Activity:  BlankFragment -- onSaveInstanceState 
11-03 17:57:40.095 26990-26990/com.ys.yoosir.screenconfigchange D/Main2Activity:  BlankFragment -- onSaveInstanceState save str
11-03 17:57:40.095 26990-26990/com.ys.yoosir.screenconfigchange D/Main2Activity:  BlankFragment -- onStop 
11-03 17:57:40.095 26990-26990/com.ys.yoosir.screenconfigchange D/Main2Activity:  -- onStop
11-03 17:57:40.095 26990-26990/com.ys.yoosir.screenconfigchange D/Main2Activity:  BlankFragment -- onDestroyView 
11-03 17:57:40.095 26990-26990/com.ys.yoosir.screenconfigchange D/Main2Activity:  BlankFragment -- onDestroy 
11-03 17:57:40.095 26990-26990/com.ys.yoosir.screenconfigchange D/Main2Activity:  BlankFragment -- onDetach 
11-03 17:57:40.095 26990-26990/com.ys.yoosir.screenconfigchange D/Main2Activity:  -- onDestroy
11-03 17:57:40.165 26990-26990/com.ys.yoosir.screenconfigchange D/Main2Activity:  BlankFragment -- onAttach 
11-03 17:57:40.165 26990-26990/com.ys.yoosir.screenconfigchange D/Main2Activity:  BlankFragment -- onCreate 
11-03 17:57:40.165 26990-26990/com.ys.yoosir.screenconfigchange D/Main2Activity:  -- onCreate - orientation
11-03 17:57:40.165 26990-26990/com.ys.yoosir.screenconfigchange D/Main2Activity:  -- onCreate get: name = yoosir,age = 24,handsome = true
11-03 17:57:40.175 26990-26990/com.ys.yoosir.screenconfigchange D/Main2Activity:  -- onCreate has child 
11-03 17:57:40.175 26990-26990/com.ys.yoosir.screenconfigchange D/Main2Activity:  BlankFragment -- onCreateView 
11-03 17:57:40.185 26990-26990/com.ys.yoosir.screenconfigchange D/Main2Activity:  BlankFragment -- onActivityCreated 
11-03 17:57:40.185 26990-26990/com.ys.yoosir.screenconfigchange D/Main2Activity:  BlankFragment -- onActivityCreated get 我设置了参数
11-03 17:57:40.185 26990-26990/com.ys.yoosir.screenconfigchange D/Main2Activity:  BlankFragment -- onStart 
11-03 17:57:40.185 26990-26990/com.ys.yoosir.screenconfigchange D/Main2Activity:  -- onStart
11-03 17:57:40.195 26990-26990/com.ys.yoosir.screenconfigchange D/Main2Activity:  -- onRestoreInstanceState
11-03 17:57:40.195 26990-26990/com.ys.yoosir.screenconfigchange D/Main2Activity:  -- onRestoreInstanceState get: name = yoosir,age = 24,handsome = true
11-03 17:57:40.195 26990-26990/com.ys.yoosir.screenconfigchange D/Main2Activity:  -- onResume
11-03 17:57:40.195 26990-26990/com.ys.yoosir.screenconfigchange D/Main2Activity:  BlankFragment -- onResume 
  • 非重建Activity模式:
    横竖屏切换Log
    (进入Activity的Log 与 重建Activity模式一样 ):横竖屏切换时,Fragment 和 Activity 都只会调用 onConfigurationChanged 方法。
11-03 18:31:21.725 27309-27309/com.ys.yoosir.screenconfigchange D/Main2Activity:  BlankFragment -- onConfigurationChanged 
11-03 18:31:21.725 27309-27309/com.ys.yoosir.screenconfigchange D/Main2Activity:  -- onConfigurationChanged
11-03 18:31:21.725 27309-27309/com.ys.yoosir.screenconfigchange D/Main2Activity:  -- onConfigurationChanged  可以在横屏方向 to do something
11-03 18:31:36.545 27309-27309/com.ys.yoosir.screenconfigchange D/Main2Activity:  BlankFragment -- onConfigurationChanged 
11-03 18:31:36.545 27309-27309/com.ys.yoosir.screenconfigchange D/Main2Activity:  -- onConfigurationChanged
11-03 18:31:36.545 27309-27309/com.ys.yoosir.screenconfigchange D/Main2Activity:  -- onConfigurationChanged  可以在竖屏方向 to do something

六、其他小技巧

如果我们应用是手机和平板都可用的,且手机的只能是竖屏不可切换,平板的只能是横屏不可切换。首先,我们要区分设备是手机还是平板,然后设置屏幕方向。代码如下:

/**
 * 判断是否平板设备
 * @param context
 * @return true:平板,false:手机 
*/
private boolean isTabletDevice(Context context) {
    return (context.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE;
}
//设置屏幕方向
private void setScreenOrientation(Context context){  
    if(!isTabletDevice(context)){  
        //手机,竖屏   
       MainActivity.this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);  
    }else{ 
         //平板,横屏   
       MainActivity.this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);   
   }
}

参考学习的文章:

The Real Best Practices to Save/Restore Activity's and Fragment's state 《===== 重点推荐阅读
[译] 保存/恢复 Activity 和 Fragment 状态的最佳实践 《=== 上面一篇文章的译文
如何在Android设备旋转时暂存数据以保护当前的交互状态?
Android横竖屏切换小结

附图两张:
Activity 生命周期.png
Fragment 生命周期.png


作者:yoosir
链接:https://www.jianshu.com/p/dbc7e81aead2
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
<think>好的,我现在需要解决Android应用在横竖屏切换时数据丢失的问题。首先,我应该回顾一下Android的基本机制。当屏幕方向改变时,默认情况下,Activity会被销毁并重新创建,这会导致临时数据丢失。用户可能遇到的情况是,比如输入的表单数据在旋转屏幕后不见了,或者某些UI状态没有保存下来。 接下来,我需要考虑几种常见的解决方案。首先,保存和恢复数据的方法。Android提供了onSaveInstanceState方法,可以保存临时数据到Bundle中。然后在onCreate或onRestoreInstanceState中恢复这些数据。这适用于简单的数据,比如字符串或基本类型。例如,用户输入的文字可以通过这种方式保存。 然后是配置变更的处理。开发者可以在AndroidManifest.xml中为Activity设置android:configChanges属性,指定某些配置变更由自己处理,而不是让系统重建Activity。比如加上orientation或screenSize。这样,当屏幕旋转时,Activity不会被销毁,而是触发onConfigurationChanged方法,在这里可以手动调整布局。但这种方法需要谨慎使用,因为处理所有配置变更可能复杂,而且不推荐用于所有情况,特别是当需要加载不同资源时。 另外,ViewModel组件是Android架构组件的一部分,设计用来保存和管理UI相关的数据,生命周期比Activity长,因此在配置变更时不会被销毁。结合LiveData,可以更方便地保持数据与UI的同步。例如,用户输入的数据可以存储在ViewModel中,即使Activity重建,数据依然存在。 还有一种情况是使用Fragment的setRetainInstance(true),但这个方法在Android 3.0之后已被弃用,推荐使用ViewModel替代。 可能还需要考虑数据持久化的方式,比如将数据保存到数据库或SharedPreferences中,但这适用于需要长期存储的数据,而不仅仅是临时保存。 现在需要综合这些方法,给出具体的步骤。比如,使用onSaveInstanceState保存简单数据,使用ViewModel保存复杂数据,或者处理配置变更。同时要注意不同方法的适用场景,比如ViewModel适合保存UI相关的数据,而配置变更处理适合需要自己手动调整布局的情况。 另外,需要参考用户提供的引用内容。虽然引用主要涉及数据安全和绘图设置,但可能与数据保存机制有关。例如,引用[2]提到机密变量的隐私保护,虽然不直接相关,但提醒在处理用户数据时要注意安全性。引用[3]提到原型评估和反馈,可能暗示在实现解决方案后需要测试不同情况下的数据保存效果。 最后,生成相关问题时要涵盖其他可能的解决方案、不同方法的优缺点、数据保存的生命周期等,帮助用户更全面理解问题。</think>### Android横竖屏切换数据丢失解决方案 #### 1. 使用`onSaveInstanceState`保存临时数据 当Activity因配置变更被销毁时,系统会调用`onSaveInstanceState()`方法保存临时数据到Bundle对象。在`onCreate()`或`onRestoreInstanceState()`中恢复数据。 ```java // 保存数据 @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putString("KEY_TEXT", editText.getText().toString()); } // 恢复数据 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (savedInstanceState != null) { String savedText = savedInstanceState.getString("KEY_TEXT"); editText.setText(savedText); } } ``` #### 2. 通过`ViewModel`持久化数据 ViewModel生命周期与Activity解耦,可跨配置变更保留数据: ```kotlin class MyViewModel : ViewModel() { var inputText: String = "" } // Activity中获取ViewModel val viewModel = ViewModelProvider(this).get(MyViewModel::class.java) // 屏幕旋转前保存数据 editText.doAfterTextChanged { viewModel.inputText = it.toString() } // 屏幕旋转后恢复数据 editText.setText(viewModel.inputText) ``` #### 3. 手动处理配置变更 在`AndroidManifest.xml`中声明: ```xml <activity android:name=".MainActivity" android:configChanges="orientation|screenSize"/> ``` 然后在Activity中重写方法: ```java @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); // 手动调整布局,例如重新加载横屏布局 setContentView(R.layout.activity_main); } ``` #### 4. 结合Fragment保留实例(已弃用,仅作参考) ```java public class RetainedFragment extends Fragment { private MyData data; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setRetainInstance(true); // 已弃用但曾广泛使用 } } ``` #### 方案选择建议 - **简单数据类型**:优先使用`onSaveInstanceState`(保存效率高,系统自动触发) - **复杂数据/UI状态**:使用`ViewModel`(生命周期感知,适合MVVM架构) - **需要完全控制布局**:配置变更处理(需谨慎处理资源加载)[^3]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值