相信大家都或多或少的为横竖屏烦恼过吧,毕竟我们在开发中用到这方面的知识还是挺多的,比如我上一篇讲VideoView的博客,在播放视频的时候,能够自如的切换横竖屏是很有必要的。所以这篇博客就来解析Android中的横竖屏。
当手机没有关闭横竖屏切换功能时,系统一旦触发横竖屏切换,缺省状态(即系统默认状态)下,当前活动的App的界面就会进行横竖屏切换,由于横竖屏的界面尺寸等参数不同,很多软件在设计和开发中为了避免横竖屏切换时引发不必要的麻烦,通常需要让App禁止掉横竖屏的切换,这就需要通过在AndroidManifest.xml中设置Activity中的android:screenOrientation属性值来实现。
screenOrientation属性,有以下几个参数:
如果android:screenOrientation=”portrait”,则无论手机如何变动,拥有这个属性的Activity都将是竖屏显示。是landscape则为横屏。
上述修改也可以在代码中通过类似如下代码来设置:
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
由上面描述可知,当android:screenOrientation为默认值”unspecified”或”sensor”等时,就会有系统根据设备的旋转情况来触发横竖屏的切换,那么有没有方法我们手动在程序中触发横竖屏的变换呢,显然上面为我们提供的setRequestedOrientation就是系统提供的一个入口。
在Activity中提供了一个方法,会在设置的参数发生变化时被调用,而我们的orientation也属于设置里的参数,所以我们可以用它去监听横竖屏变化。
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
newConfig就是就有现在改变的所有设置参数,我们这样就可以知道是横屏还是竖屏。
String message= newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE ? "屏幕设置为:横屏" : "屏幕设置为:竖屏";
手动调用时,会无视AndroidManifest中关于screenOrientation的设置。
在切换了横竖屏后(包括用setRequestedOrientation调用)都会重新调用一轮onPause-> onStop-> onDestory-> onCreate->onStart->onResume操作,从而销毁原来的Activity对象,创建新的Activity对象,这是因为通常情况下软件在横竖屏之间切换,设置的参数发生了变化,每个参数的变化都会使Activity重启。
因为每次的重启都会导致当前数据的丢失,这对用户体验是非常差的。
要想解决这个问题有两个办法,第一个我们只要设置切换的时候不重启就可以了,改变android:configChanges属性的值即可。
这是比较简单的方法,还有一种就是重启Activity的时候让它保留数据。就是使用onSaveInstanceState(Bundle outState)和onRestoreInstanceState(Bundle savedInstanceState)方法。
横屏切换竖屏实际上是先把当前的横屏的Activity杀掉 然后重新创建一个竖屏的Activity,我们可以使用onSaveInstanceState()方法保存数据,它是在横屏Activity将杀死前调用,可以将须要保存的数据放入Bundle封装在系统中,切换竖屏后这个Activity又重新被创建 这样可以在onCreate(Bundle)或者onRestoreInstanceState(Bundle)方法中来回复之前保存在Bundle中的数据,这样就可以实现横竖屏界面切换数据的保存与读取,当然前提是只能保存Bundle类型的数据,也就是说大量的对象数据的话就要想其它办法来恢复。
我们拿上篇博客中的VideoVIew来做说明。像VideoView,也就是在播放视频的时候重启了Activity,那么我们需要保存的数据就应该是视频播放的进度。
@Override
protected void onSaveInstanceState(Bundle outState) {
int progress = mVideoView.getCurrentPosition();
outState.putInt("progress", progress);
super.onSaveInstanceState(outState);
}
首先我们在切换屏幕之前将数据保存在Bundle中。
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
int progress = savedInstanceState.getInt("progress");
mVideoView.seekTo(progress);
mVideoView.start();
super.onRestoreInstanceState(savedInstanceState);
}
然后在切换屏幕之后在Bundle中把数据取出来,那么我们就可以让VideoView跳转到取出的进度那儿,是播放还是暂停都是由我们自己决定,既然是重启,默认是暂停的。
我们在切换横竖屏的时候,界面的宽高会发生转换,这样在有的时候可能会发生适配不恰当的情况,就会给用户带来不好的体验,我们的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);
}
经过上面的演示,我们可以看到具体实现涉及到了Manifest工程配置里面具体Activity的screenOrientation和configChanges两个参数,这两个参数screenOrientation的优先级要高于configChanges,即假如screenOrientation设置为固定横竖屏时,那么configChanges参数无论怎么设都没有办法引发横竖屏切换,除非在代码中手动调用setRequestedOrientation函数进行修改。
让我们来看看configChanges的配置属性都有什么:
从上述我们可以看到除了横竖屏,包括语言、网络、键盘和外设等变化都可以被onConfigurationChanged函数监控到。
如果我们是采用上面的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()。
@Override
protected void onResume() {
super.onResume();
if (first == 0) {
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
}
first = 0;
}
first的初始值为1,这样在要变化屏幕方向销毁Activity的时候也让first的值重置啦。
我发现百度上大家写得基本上都是一样的,我觉得虽然内容比较全面,但有不少不合理的地方,我看到他们的这些博客真是有哭笑不得的感觉,不过这些说明还是很不错的。
结束语:本文仅用来学习记录,参考查阅。