相信大家都或多或少的为横竖屏烦恼过吧,毕竟我们在开发中用到这方面的知识还是挺多的,比如我上一篇讲VideoView的博客,在播放视频的时候,能够自如的切换横竖屏是很有必要的。所以这篇博客就来解析Android中的横竖屏。

1、screenOrientation

当手机没有关闭横竖屏切换功能时,系统一旦触发横竖屏切换,缺省状态(即系统默认状态)下,当前活动的App的界面就会进行横竖屏切换,由于横竖屏的界面尺寸等参数不同,很多软件在设计和开发中为了避免横竖屏切换时引发不必要的麻烦,通常需要让App禁止掉横竖屏的切换,这就需要通过在AndroidManifest.xml中设置Activity中的android:screenOrientation属性值来实现。

screenOrientation属性,有以下几个参数:

  • unspecified:默认值 由系统来判断显示方向,判定的策略是和设备相关的,所以不同的设备会有不同的显示方向.
  • landscape:横屏显示
  • portrait:竖屏显示
  • user:用户当前首选的方向
  • behind:和该Activity下面的那个Activity的方向一致(在Activity堆栈中的)
  • sensor:有物理的感应器来决定。如果用户旋转设备这屏幕会横竖屏切换。
  • nosensor:忽略物理感应器,这样就不会随着用户旋转设备而更改了(unspecified设置除外)。

如果android:screenOrientation=”portrait”,则无论手机如何变动,拥有这个属性的Activity都将是竖屏显示。是landscape则为横屏。

上述修改也可以在代码中通过类似如下代码来设置:

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

2、手动切换

由上面描述可知,当android:screenOrientation为默认值”unspecified”或”sensor”等时,就会有系统根据设备的旋转情况来触发横竖屏的切换,那么有没有方法我们手动在程序中触发横竖屏的变换呢,显然上面为我们提供的setRequestedOrientation就是系统提供的一个入口。

在Activity中提供了一个方法,会在设置的参数发生变化时被调用,而我们的orientation也属于设置里的参数,所以我们可以用它去监听横竖屏变化。

@Overridepublic void onConfigurationChanged(Configuration newConfig) {    super.onConfigurationChanged(newConfig);}

newConfig就是就有现在改变的所有设置参数,我们这样就可以知道是横屏还是竖屏。

String message= newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE ? "屏幕设置为:横屏" : "屏幕设置为:竖屏";

手动调用时,会无视AndroidManifest中关于screenOrientation的设置。

3、重启Activity

在切换了横竖屏后(包括用setRequestedOrientation调用)都会重新调用一轮onPause-> onStop-> onDestory-> onCreate->onStart->onResume操作,从而销毁原来的Activity对象,创建新的Activity对象,这是因为通常情况下软件在横竖屏之间切换,设置的参数发生了变化,每个参数的变化都会使Activity重启。

因为每次的重启都会导致当前数据的丢失,这对用户体验是非常差的。

要想解决这个问题有两个办法,第一个我们只要设置切换的时候不重启就可以了,改变android:configChanges属性的值即可。

  • 不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次
  • 设置Activity的android:configChanges=”orientation”时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次
  • 设置Activity android:configChanges=”orientation|keyboardHidden”时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法
  • 自从Android 3.2(API 13)后,在设置Activity的android:configChanges=”orientation|keyboardHidden”后,还是一样会重新调用各个生命周期的。因为screen size也开始跟着设备的横竖切换而改变。因此,阻止程序在运行时重新加载Activity,除了设置”orientation”,你还必须加上”ScreenSize”。

这是比较简单的方法,还有一种就是重启Activity的时候让它保留数据。就是使用onSaveInstanceState(Bundle outState)和onRestoreInstanceState(Bundle savedInstanceState)方法。

横屏切换竖屏实际上是先把当前的横屏的Activity杀掉 然后重新创建一个竖屏的Activity,我们可以使用onSaveInstanceState()方法保存数据,它是在横屏Activity将杀死前调用,可以将须要保存的数据放入Bundle封装在系统中,切换竖屏后这个Activity又重新被创建 这样可以在onCreate(Bundle)或者onRestoreInstanceState(Bundle)方法中来回复之前保存在Bundle中的数据,这样就可以实现横竖屏界面切换数据的保存与读取,当然前提是只能保存Bundle类型的数据,也就是说大量的对象数据的话就要想其它办法来恢复。

我们拿上篇博客中的VideoVIew来做说明。像VideoView,也就是在播放视频的时候重启了Activity,那么我们需要保存的数据就应该是视频播放的进度。

@Overrideprotected void onSaveInstanceState(Bundle outState) {    int progress = mVideoView.getCurrentPosition();    outState.putInt("progress", progress);    super.onSaveInstanceState(outState);}

首先我们在切换屏幕之前将数据保存在Bundle中。

@Overrideprotected void onRestoreInstanceState(Bundle savedInstanceState) {    int progress = savedInstanceState.getInt("progress");    mVideoView.seekTo(progress);    mVideoView.start();    super.onRestoreInstanceState(savedInstanceState);}

然后在切换屏幕之后在Bundle中把数据取出来,那么我们就可以让VideoView跳转到取出的进度那儿,是播放还是暂停都是由我们自己决定,既然是重启,默认是暂停的。

4、横竖屏布局

我们在切换横竖屏的时候,界面的宽高会发生转换,这样在有的时候可能会发生适配不恰当的情况,就会给用户带来不好的体验,我们的app也是不完整的,所以很多情况我们都会分别给横竖屏一个布局。

具体的布局切换可以通过如下两种方法来实现:

1)在res目录下建立layout-land和layout-port目录,相应的layout文件名不变,比如activity_main.xml。layout-land是横屏的布局,layout-port是竖屏的布局,其他的不用管,横竖屏切换时程序自己会调用Activity的onCreate方法,从而根据当前横竖屏情况自动加载响应的布局。

可以看到在横屏的布局就比竖屏的多了一个TextView,因为是自动调用的,所以我们可以在这两个布局中做不同的宽高处理,去达到更好的效果。

2)假如横竖屏布局资源是不一样的,又不按照如上设置,则需要通过代码来判断当前是横屏还是竖屏然后来加载相应的xml布局文件。因为当屏幕变为横屏的时候,系统会重新呼叫当前Activity的onCreate方法,你可以把以下方法放在你的onCreate中来检查当前的方向,然后可以让你的setContentView来载入不同的layout xml。

if ( mCurrentOrientation == Configuration.ORIENTATION_PORTRAIT ) {    setContentView(R.layout.mainP);} else if ( mCurrentOrientation == Configuration.ORIENTATION_LANDSCAPE ) {    setContentView(R.layout.mainL);}

5、configChanges

经过上面的演示,我们可以看到具体实现涉及到了Manifest工程配置里面具体Activity的screenOrientation和configChanges两个参数,这两个参数screenOrientation的优先级要高于configChanges,即假如screenOrientation设置为固定横竖屏时,那么configChanges参数无论怎么设都没有办法引发横竖屏切换,除非在代码中手动调用setRequestedOrientation函数进行修改。

让我们来看看configChanges的配置属性都有什么:

  • mcc:IMSI移动台的国家代码(MCC)发生变化——一个SIM被探测到并且更新MCC
  • mnc:IMSI移动台的网络代码(MNC)发生变化——一个SIM被探测到并且更新MNC
  • locale:区域发生变化——用户选择了一个文本需要显示的新语言
  • touchscreen:触摸屏发生变化。(这个通常不会发生。)
  • keyboard:键盘类型发生变化——例如:用户插入了外接键盘。
  • keyboardHidden:键盘的可访问性发生变化——例如:用户发现了硬件键盘。
  • navigation:导航类型(轨迹球或dpad)发生变化。(通常不会发生。)
  • screenLayout:屏幕布局发生变化——这个会导致显示不同的Activity。
  • fontScale:字体缩放因子发生变化——用户选择了新的字体大小。
  • uiMode:当UI模式发生改变的时候——当用户放置设备到桌子或/汽车或夜间模式改变的时候可以引起UI模式变化。阅读UiModeManager。在API级别8时引入。
  • orientation:屏幕方向发生变化——用户旋转了屏幕。注意:如果应用程序的目标API级别是13或更高(通过属性minSdkVersion和属性targetSdkVersion声明),你也需要声明配置项screenSize,因为这将在设备选择肖像和屏幕方向时发生改变。
  • screenSize:当前可用屏幕大小发生变化。这代表一个当前可用大小的变化,和当前的比率相关,因此当用户选择不同的画面和图像,会发生变化。然而,如果你的程序目标API级别是12或更低,你的Activity总是会自己处理这个配置变化(这个变化不会引起Activity的重启,甚至在Android3.2或更新的设备上)。在API级别13里加入的。
  • smallestScreenSize:物理屏幕大小的变化。不管方向的变化,仅仅在实际物理屏幕打包变化的时候,如:外接显示器。这个配置项的变化引起在smallestWidth configuration里的变化。然而,如果你的程序目标API级别是12或更低,你的Activity将自己处理这个变化(这个变化不会引起Activity的重启,甚至在Android3.2或更新的设备上)在API级别13里加入的。
  • layoutDirection:布局方向变化。例如书写方式从左向右(LTR)转换为从右向左(RTL)

从上述我们可以看到除了横竖屏,包括语言、网络、键盘和外设等变化都可以被onConfigurationChanged函数监控到。

6、setRequestedOrientation

如果我们是采用上面的configChanges不重启Activity,那么在手动调用setRequestedOrientation之后,假如会引发横竖屏切换(即请求的横竖屏要求与当前的横竖屏情况不一致,就会引发切换),那么会立即调用onConfigurationChanged函数;假如不会引发横竖屏切换(请求前后一致),那么也就不会调用到onConfigurationChanged函数。

这个手动调用setRequestedOrientation的地方可以在Activity中的任何地方,即也可以在onConfigurationChanged中调用,但是一旦指定为横屏或竖屏完成这个变换之后,后面不论屏幕如何进行怎么翻转变化,都不会再触发横竖屏切换了,也即等同于在manifest中设置了android:screenOrientation属性为横屏或竖屏。如果要恢复为响应横竖屏随物理传感器设备变换,那么就需要手动调用类似如下代码进行恢复:

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);

如果是保存数据重启Activity,那么手动调用setRequestedOrientation发出横竖屏设定请求之后,假如需要进行横竖屏切换(即请求前后横竖屏状态不一致),则会对Activity进行销毁并重启;假如不需要需要进行横竖屏切换,则Activity维持现状不变;

手动调用setRequestedOrientation一次,完成变换之后,也跟上面非重启一样,相当于在manifest中设置了android:screenOrientation属性为横屏或竖屏。要想恢复也需要重新调用类似上面非重启的调用。

那么我们可以想想,如果让App启动的时候是横屏的话就横屏显示,纵屏的话就纵屏显示,然后手机怎么让它旋转都不会触发切换横竖屏,这该怎么做呢?

首先在manifest中设置android:screenOrientation=”sensor”。

我们可以在APP启动的时候获得它的宽高,判断是处于横屏还是竖屏,然后用setRequestedOrientation()方法设置为这个值,那么原先在manifest中设置的sensor就没用了。

public void setOrientation() {    Display display = getWindowManager().getDefaultDisplay();    int width = display.getWidth();    int height = display.getHeight();    if (width > height) {        mOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;    } else {        mOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;    }    this.setRequestedOrientation(mOrientation);}

把这个方法放到onCreate中调用即可。不过如果切到别的画面的时候再回到原画面(比如按了Home键),它就仍然是横的或者是纵的。那我们想怎么让它从别的屏幕回来后,又重新横竖屏布局呢?

因为从别的画面中回来调用了onResume()方法,所以我们可以在onResume()方法中调用setRequestedOrientation()方法,将screenOrientation的值设为sensor,因为在启动APP的时候已经修改了这个值,然后如果原先的屏幕方向是竖屏,现在用户把手机置为横屏,物理感应器就会起作用,我们的Activity的方向就会改变了。

这里是否重启Activity也是一个问题,如果我们横竖屏不只有一个布局,那么如果不重启Activity,不执行onCreate()方法,那么就一直是加载那一个布局,转换了方向也是一样。所以如果我们有多个布局,就采用重启Activity的方法。

因为启动APP的时候,onResume方法执行在onCreate之后,所以屏幕方向选择好了后又被设置为sensor,因为前后屏幕状态没变,所以也不会调用onConfigurationChanged()方法。那么我们就设置一个变量来判断是不是在调用onCreate()后第一次执行onResume()。

@Overrideprotected void onResume() {   super.onResume();   if (first == 0) {       this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);   }   first = 0;}

first的初始值为1,这样在要变化屏幕方向销毁Activity的时候也让first的值重置啦。

我发现百度上大家写得基本上都是一样的,我觉得虽然内容比较全面,但有不少不合理的地方,我看到他们的这些博客真是有哭笑不得的感觉,不过这些说明还是很不错的。

结束语:本文仅用来学习记录,参考查阅。

更多相关文章

  1. android仿iPhone滚轮控件实现及源码分析(一)
  2. 【Android(安卓)UI设计与开发】第08期:底部菜单栏(三)Fragment+Frag
  3. Android仿游戏答题
  4. Android(安卓)Service服务
  5. Android中为什么主线程不会因为Looper.loop()方法造成阻塞
  6. Android(安卓)Fragment使用(三) Activity, Fragment, WebView的
  7. Android之自定义View的死亡三部曲之(Measure)
  8. 构建Android自适应布局应用方案解析
  9. AndroidStudio官方的Android(安卓)使用 Layout Editor 构建 UI

随机推荐

  1. android小功能实现之xml文件解析(Pull)
  2. android listView 显示数据 单击 长按
  3. Android file类使用详解-SDcard
  4. Android应用程序核心-应用程序的基本组件
  5. Ubuntu下android源码下载与编译
  6. Android培训班(40)
  7. Android中关于线程使用的几点注意事项
  8. Android实现View隐藏显示渐变动画
  9. Android toolbar与actionbar区别
  10. Android定时任务实现方式归纳总结