android 手机屏幕旋转机制与使用说明

来源:互联网 发布:淘宝联盟社区 编辑:程序博客网 时间:2024/04/28 03:00

android 屏幕旋转机制与使用说明



一 Overview

在开发android应用的时候,有可能需要让应用程序随着系统设置而进行调整,比如判断系统的屏幕方向、判断系统方向的方向导航设备等。除此之外,还需要让应用程序监听系统设置的更改,对系统设置的更改作出响应。

如果系统需要监听系统设置的更改,则可以考虑重写ActivityonConfigurationChanged()方法,该方法是一个基于回调的事件处理方法;当系统的设置发生更改时,该方法会被自动触发。查阅AndroidAPI可以得知配置信息android:ConfigChanged实际对应的是Activity里的onConfigurationChanged()方法。

在一些特殊的情况中,你可能希望当一种或者多种配置改变时避免重新启动你的activity。你可以通过在manifest中设置android:configChanges属性来实现这点。你可以在这里声明activity可以处理的任何配置改变,当这些配置改变时不会重新启动activity,而会调它的onConfigurationChanged()方法。如果改变的配置中包含了你所无法处理的配置(在android:configChanges并未声明),你的activity仍然要被重新启动,onConfigurationChanged()将不会被调用。

android:configChanges=""中可以用的值,请查询具体的androidAPI

本文主要是站在App的角度,分析屏幕旋转处理的一般使用方法和注意事项。然后简要介绍framework中的实现细节。

二 屏幕旋转状态监测


屏幕旋转处理的关键为获取屏幕旋转的信息,因此系统中就需要有一种机制,可以实时监测当前系统的屏幕角度。所以,首先,我们就关注一下Framework是如何实现实时监控系统中的oritentation状态的。

1.Orientation状态初始化与设置

setting设置

AndroidSettings->Display中有Orientation这一设置项。当选中时,屏幕会随设备旋转。

settings设置是在文件DisplaySettings.java中,该项对应的键字符串为:

private static final String KEY_ACCELEROMETER = "accelerometer";


其默认值保存在xml文件中,默认是EnableUI程序初始化时会根据其值是否在复选框中打勾(代码在onCreate函数中):


public void onCreate(Bundle savedInstanceState){        Accelerometer = (CheckBoxPreference) findPreference(KEY_ACCELEROMETER);        mAccelerometer.setPersistent(false);}

保存setting更改

当用户改变了AndroidSettings-> Display中有Orientation这一设置项时,会保存起来:
 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {        if (preference == mAccelerometer) {            RotationPolicy.setRotationLockForAccessibility(                    getActivity(), !mAccelerometer.isChecked());        } else if (preference == mNotificationPulse) {            boolean value = mNotificationPulse.isChecked();            Settings.System.putInt(getContentResolver(), Settings.System.NOTIFICATION_LIGHT_PULSE,                    value ? 1 : 0);            return true;}

监听setting的变化

在文件frameworks/policies/base/phone/com/android/internal/policy/impl/PhoneWindowManager.java中的mSettingsObserver会随时监控其值,对用户设置做出应:

mSettingsObserver的初始化代码如下:

        mSettingsObserver = new SettingsObserver(mHandler);        mSettingsObserver.observe();

一旦mSettingsObserver监听到AndroidSettings-> Display中有Orientation这一设置项有变化,会马上回调它的onChange( )方法。


onChange( )实现代码如下:
        @Override public void onChange(boolean selfChange) {            updateSettings();            updateRotation(false);        }

2 监听oritentation状态


初始化

2.1.3节所述,也是在文件PhoneWindowManager.java中实现实时监控oritentation的状态的。是通过变量mOrientationListener监听oritentation状态的。

初始化代码如下:

 mOrientationListener = new MyOrientationListener(mContext, mHandler);        try {            mOrientationListener.setCurrentRotation(windowManager.getRotation());        } catch (RemoteException ex) { }


实时监测

一旦oritentation状态发生变化,mOrientationListeneronProposedRotationChanged会被调用。其code如下:

class MyOrientationListener extends WindowOrientationListener{       MyOrientationListener(Context context, Handler handler){           super(context, handler);       }
 
      @Override        public voidonProposedRotationChanged(int rotation) {           if (localLOGV) Slog.v(TAG, "onProposedRotationChanged,rotation=" + rotation);           updateRotation(false);       }    }

我们不难发现,onProposedRotationChanged()函数中会调用updateRotation(false),实现对屏幕旋转的处理。

以上就是android实现的如何实时监测oritentation的状态。至于针对该状态,framework做什么样的处理,后续通过继续分析updateRotation(false)来说明。


屏幕旋转处理

默认情况

如果没有针对性地做任何处理的话,默认情况下,当用户手机的重力感应器打开后,旋转屏幕方向,会导致app的当前activity发生onDestroy->onCreate,会重新构造当前activity和界面布局。其实现机制为:当Configuration改变后,ActivityManagerService将会发送"配置改变"的广播,会要求ActivityThread重新启动当前focusActivity,这是默认情况。

如果想很好地支持屏幕旋转,则建议在res中建立layout-landlayout-port两个文件夹,把横屏和竖屏的布局文件放入对应的layout文件夹中。

如果不申明android:configChanges="",按照Activity的生命周期,都会去执行一次onCreate()方法,而onCreate()方法通常会在显示之前做一些初始化工作。这样就有可能造成重复的初始化,必然降低程序效率,而且更有可能因为重复的初始化而导致数据的丢失。

设置固定的屏幕方向

android:screenOrientation属性的意义为,固定屏幕显示方向

主要的几个属性如下表:


"unspecified"

默认值,由系统来选择方向。它的使用策略,以及由于选择时特定的上下文环境,可能会因为设备的差异而不同。

"user"

使用用户当前首选的方向。

"behind"

使用Activity堆栈中与该Activity之下的那个Activity的相同的方向。

"landscape"

横向显示(宽度比高度要大)

"portrait"

纵向显示(高度比宽度要大)

"reverseLandscape"

与正常的横向方向相反显示,在APILevel 9中被引入

"reversePortrait"

与正常的纵向方向相反显示,在APILevel 9中被引入

"sensor"

显示的方向是由设备的方向传感器来决定的。显示方向依赖与用户怎样持有设备;当用户旋转设备时,显示的方向会改变。但是,默认情况下,有些设备不会在所有的四个方向上都旋转,因此要允许在所有的四个方向上都能旋转,就要使用fullSensor属性值。

"nosensor"

屏幕的显示方向不会参照物理方向传感器。传感器会被忽略,所以显示不会因用户移动设备而旋转。除了这个差别之外,系统会使用与unspecified”设置相同的策略来旋转屏幕的方向。

如上表所示,在AndroidManifest.xml对应的activity属性中,添加:


android:screenOrientation="landscape" //横屏     或者   android:screenOrientation="portrait" //竖屏

那么,应用启动后,会固定为指定的屏幕方向,即使屏幕旋转,Activity也不会出现销毁或者转向等任何反应。这是由于PhoneWindowManager通知WindowManagerService更新Orientation的时候,WindowManagerService在函数updateRotationUnchecked()中会通过判断当前的orientation是否有变化:

public void updateRotationUnchecked(t) {        boolean changed;        synchronized(mWindowMap) {            changed = updateRotationUncheckedLocked(false);            if (!changed || forceRelayout) {                getDefaultDisplayContentLocked().layoutNeeded =true;                performLayoutAndPlaceSurfacesLocked();            }        }    if (changed ||alwaysSendConfiguration) {           sendNewConfiguration();        }    }


由于android:screenOrientation将屏幕设定为固定值,因此函数updateRotationUncheckedLocked永远返回false,此时只是重新布局activity并绘制,而没有通知AMSconfiguration的变化。因此即使屏幕旋转,Activity也不会出现销毁或者转向等任何反应。

强制开启屏幕旋转

如果用户的手机没有开启重力感应器或者在AndroidManifest.xml中设置了android:screenOrientation,默认情况下,该Activity不会响应屏幕旋转事件。如果在这种情况下,依然希望Activity能响应屏幕旋转,则添加如下代码:

// activityonCreate函数中

this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);


此时,尽管没有在manifest中设置android:configChanges,系统仍能够捕获屏幕旋转事件,这是由于函数setRequestedOrientation本质上是重新设置了当前window关于Orientation的配置属性,并更新oritentation状态。setRequestedOrientation核心代码(ActivityManagerService.java)如下:

setRequestedOrientation( ){……//更新Orientation的属性配置mWindowManager.setAppOrientation(r.appToken, requestedOrientation);//更新当前系统的oritentation状态Configurationconfig =mWindowManager.updateOrientationFromAppTokens(                   mConfiguration, r.mayFreezeScreenLocked(r.app) ? r.appToken : null);……}


捕获屏幕旋转事件,并且不希望activity被销毁

当设置android:configChanges="orientation|screenSize"之后,屏幕方向发生改变时,就可以触发onConfigurationChanged事件。其基本的实现原理为:当Configuration改变后,ActivityManagerService会发送"配置改变"的广播,会要求ActivityThread重新启动当前focusActivity。这是我们前面描述的默认情况。

如果我们配置Activityandroid:configChanges信息,那么就可以避免对Activity销毁再重新创建,而是调用activityonConfigurationChanged方法。由于这部分代码量很大,此处仅仅列出framework流程图:



由上面的流程图可以看出,如果希望捕获屏幕旋转事件,并且不希望activity 被销毁,应该采用如下的方法:1)在AndroidManifest.xml对应的activity属性中,添加:android:configChanges="orientation|screenSize"

此时如果屏幕旋转,就会触发orientation状态监听器mOrientationListener的函数onProposedRotationChanged,最终导致调用ActivityonConfigurationChanged方法。

注意:如果你的开发API等级等于或高于13,你还需要设置screenSize,因为screenSize会在屏幕旋转时改变。这与android代码升级有关系,记住即可。2)在对应的activity中,重载函数onConfigurationChanged()即可。

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



五 结束语

至此,我们就分析完了屏幕旋转相关知识,包括旋转信息的捕获以及后续的处理。屏幕旋转是一个android应用开发中,经常遇到的一个问题。看似简单,但是有几点需要注意:


1.注意区分configChangesscreenOrientation的不同,很多同事可能之前没有关注过。


2.配置android:configChanges时,需要设置screenSize,因为screenSize会在屏幕旋转时改变。否则onConfigurationChanged不会被调用。


0 0