Android 横竖屏处理

Android 手机一般都支持横竖屏旋转,系统也会提供一个设置,控制是否可以旋转。

界面旋转方向的决定因素

决定一个界面显示为横屏/竖屏的因素有几个:

  • 系统的设置项,一般可以设置为只允许横竖屏或可旋转切换。
  • 设备的物理传感器感应到的设备方向。
  • 不同 App 里的代码对横竖屏的设置。
    需要注意的一点是,这三个因素没有固定的优先级。所以即使系统的设置项中设置了固定为竖屏,App 里的代码也可以将界面设置为横屏,当然影响的范围仅限于 App 内部界面。

代码中设置横竖屏的方法

我们有2种方法设置横竖屏:

  • 在 AndroidManifest.xml 文件中,对 <activity> 标签设置android:screenOrientation 属性,具体的值和作用见官网
    参数 |说明
    ----|------
    unspecified|默认值,不做任何指定,由系统来决定显示方向。这将由上一个 activity 来决定,或者当 activity 处于栈底时由用户设置的方向来决定。
    landscape|保持横屏。
    portrait|保持竖屏。
    user|用户当前设置的 orientation 值。
    behide|保持和上一个 Activity 的方向一致。
    sensor|完全根据物理传感器的方向来决定。注意用这个值时会忽略用户在系统设置中的旋转开关状态。(注意一般机器即使用这个值也不会支持竖屏旋转180度)
    nosensor|忽略物理传感器的方向。这将导致用户旋转手机时不会切换横竖屏。
    sensorLandscape|保持横屏,但可以根据物理传感器来决定横屏的方向。
    sensorPortrait|保持竖屏,但可以根据物理传感器来决定竖屏的方向。
    reverseLandscape|保持横屏,但方向与使用 landscape 时相反。
    reversePortrait|保持竖屏,但方向与使用 portrait 时相反。
    fullSensor|与 sensor 大致相同,区别在于这个属性会允许4个方向都可以旋转。
    userLandscape|保持横屏,但可以在用户允许旋转的情况下,根据物理传感器来决定横屏的方向。(注意与 sensorLandscape 对比)
    userPortrait|保持竖屏,但可以在用户允许旋转的情况下,根据物理传感器来决定竖屏的方向。(注意与 sensorPortrait 对比)
    fullUser|与 user 大致相同,区别在于如果用户允许旋转,这个属性允许4个方向都可以旋转。
    locked|屏幕方向会锁定在当前方向,不能再旋转。

该属性的值有很多,但一般比较常用的几个值也就几个:unspecified(允许旋转)、landscape(固定为横屏)、portrait(固定为竖屏)。而且一般我们只需要在 AndroidManifest.xml 中指定好固定的值就可以了。除非你对横竖屏切换有自己的一套逻辑,才需要在不同的逻辑下进行不同的设置。

  • 在 Java 中调用 Activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) //执行此代码变为横屏
    Activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) //执行此代码变为竖屏
    如果在 xml 中设置,则在初始化界面时就已经确定好其方向。而在如果 Java 中设置,则可以做到根据不同情况动态设置。

横竖屏切换时引起的 Activity 重新创建

默认情况下,横竖屏切换会引起当前 Activity 销毁然后重新创建。所以我们可以在 Activity 重新创建时根据当前横竖屏状态做一些差异化。最常见的就是根据横竖屏采用不同的 layout.xml

  • 在 res 目录下建立 layout-land 和 layout-port 目录,在不同目录下分别放一个同名的 layout.xml 文件,然后在代码里正常使用这个 layout.xml 文件,系统就会在横竖屏切换并重新创建 Activity 时自动使用对应的 layout 文件。如:
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_layout);
}
  • 如果不想通过创建多个 layout 文件来区分横竖屏布局,也可以用 Java 代码来做一些差异化,只要使用以下判断即可获取当前横竖屏的信息。
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_layout);
    // 获取横竖屏信息
    int orientation = getResources().getConfiguration().orientation;
    if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
        // 针对横屏做一些处理
    } else if (orientation == Configuration.ORIENTATION_PORTRAIT) {
        // 针对竖屏做一些处理
    }
}

有一个点需要注意,屏幕旋转 Activity会被销毁重新创建

阻止横竖屏切换时 Activity 重启

虽然我们可以用上面的方法对横竖屏做不同的布局,但是横竖屏切换导致的 Activity 重新创建还会引起用户当前状态、数据的丢失。例如 EditText 中输入到一半的文字在 Activity 重新创建后会丢失。所以如果有办法能在旋转屏幕时不重新创建 Activity,应该是更好的一种选择。
我们可以在 AndroidManifest.xml 中为相应的 <activity> 设置android:configChanges 属性,让它不在横竖屏切换时引起 Activity 的重新创建。
注意:

  • 如果targetSdkVersion的值小于13(Android 3.2),则只要配置:
    android:configChanges="orientation"
    如果 targetSdkVersion 的值大于等于13,则只要配置
    android:configChanges="orientation|screenSize"

扩展:当接入一个外设键盘时,默认软键盘会自动隐藏,系统自动使用外设键盘。这个过程Activity的销毁和隐藏执行了两次。并且 onConfigurationChanged() 不会调用。
但是在配置文件中设置 android:configChanges="keyboardHidden|keyboard"。当接入外设键盘或者拔出外设键盘时,调用的周期是先调用onConfigurationChanged()然后在进行一次销毁重建。
在这里有一个疑点,为什么有两次的销毁重建?
其中一次的销毁重建可以肯定是因为外设键盘的插入和拔出。当设置 android:configChanges="keyboardHidden|keyboard" 之后,就不会销毁重建,而是调用onConfigurationChanged()方法。
但是还有一次销毁重建一直存在。
经过测试,当接入外设键盘时,除了键盘类型的改变,触摸屏也发生了变化。因为使用外设键盘,触摸屏不能使用了。这里,我接入的是键盘,所以触摸屏不能使用了。
总结:如果是键盘类型发生了改变,则 configChanges属性 配置如下Activity才不会销毁重建,且回调 onConfigurationChanged方法:


注:这里的外置物理键盘可以是游戏手柄、扫描枪、键盘等等。
configChanges属性具体查看官方文档

横竖屏切换时的回调

我们知道一旦我们按照上述方法设置了 android:configChanges 属性,那么横竖屏切换时 Activity 将不会重新创建,而是在 Activity、View(ViewGroup)、Fragment、Service、Content Provider 等等在设备的配置发生变化的时候,会回调 onConfigurationChanged 的方法。实质上主要是 Activity 中收到 AMS(ActivityManagerService) 的通知,回调,然后把事件分发到 Window、Fragment、ActionBar 等。
注意:在设备配置发生变化的时候,会回调 onConfigurationChanged,但是前提条件是当你的 Activity(组件)还在运行的时候。这就很明显了,说明一旦你的界面暂停以后就不会回调这个方法了。但是这样会导致一个问题,就是你的界面跳转到其他界面的时候(当前界面暂停),然后发生了一次屏幕旋转,再返回的时候,你的界面虽然旋转了,但是并没有回调 onConfigurationChanged 方法,并没有执行你的UI适配代码或者你的一些逻辑.

  • 解决办法就是我们自定义一个广播接受者专门用来接受onConfigurationChanged这个广播,这样子就可以确保,无论什么情况下,系统ConfigurationChanged的时候你的代码都会被执行。

自定义的ConfigurationChangeReceiver如下,这里提供一个静态方法方便注册。

public abstract class ConfigurationChangeReceiver extends BroadcastReceiver {

    private static final String TAG = ConfigurationChangeReceiver.class.getSimpleName();

    public static IntentFilter getIntentFilter() {
    IntentFilter filter = new IntentFilter();
    filter.addAction("android.intent.action.CONFIGURATION_CHANGED");
    return filter;
    }
}

接下类在Activity或者Fragment中的正确位置注册

private void initConfigurationChangeReceiver() {
    mConfigurationChangeReceiver = new ConfigurationChangeReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            //你的屏幕适配代码
        }
    };
    this.registerReceiver(mConfigurationChangeReceiver, ConfigurationChangeReceiver.getIntentFilter());
}

记得反注册,防止内存泄漏哦。

private void unRegisterConfigurationChangeReceiver() {
    if (mConfigurationChangeReceiver != null) {
        this.unregisterReceiver(mConfigurationChangeReceiver);
    }
}

注意:注意这个广播只能够在Java代码中注册才会有效果。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,047评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,807评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,501评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,839评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,951评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,117评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,188评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,929评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,372评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,679评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,837评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,536评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,168评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,886评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,129评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,665评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,739评论 2 351

推荐阅读更多精彩内容