转载自:https://blog.csdn.net/qqwuy_muzi/article/details/68942118
默认情况下,当用户手机的”屏幕旋转”选项打开后,旋转手机方向,手机屏幕也会跟随旋转的方向进行横竖屏切换.
设置屏幕旋转的方式有两种:
一.在AndroidManifest.xml设置
如果activity默认是某种模式的,直接在AndroidManifest.xml中的对应activity项中,添加相应的代码即可实现
如:
android:screenOrientation=”unspecified” 跟随系统屏幕旋转方向等(默认)
android:screenOrientation=”landscape” 强制横屏
android:screenOrientation=”portrait” 强制竖屏
二.代码动态设置
如果需要动态设置activity的在屏幕上的显示方向,可以调用系统提供的api函数:setRequestedOrientation(@ActivityInfo.ScreenOrientationint requestedOrientation).
requestedOrientation主要对应的值:
ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
三.android:screenOrientation的详细参数
四.android:configChanges设置(自行处理配置变更)
当我们设置的旋转方式是跟随系统的,既android:screenOrientation=”unspecified”,这时如果屏幕方向发生改变,Activity会被销毁,然后重新创建Activity.
(重启行为旨在通过利用与新设备配置匹配的备用资源自动重新加载您的应用,来帮助它适应新配置.)
重启并恢复大量数据不仅成本高昂,影响性能,而且给用户留下糟糕的使用体验.
如果不想Activity被销毁,需要在AndroidManifest.xml中配置android:configChanges
例如:
注意:从 Android 3.2(API级别 13)开始,当设备在纵向和横向之间切换时,“屏幕尺寸”也会发生变化.因此,在开发针对API 级别 13或更高版本(正如 minSdkVersion 和 targetSdkVersion 属性中所声明)的应用时,若要避免由于设备方向改变而导致运行时重启,则除了 “orientation” 值以外,您还必须添加 “screenSize” 值.也就是说,您必须声明android:configChanges=”orientation|screenSize”.但是,如果您的应用面向API 级别 12 或更低版本,则Activity 始终会自行处理此配置变更(即便是在Android 3.2 或更高版本的设备上运行,此配置变更也不会重启Activity).)
加上这段代码后,如果再旋转手机屏幕,Activity就不会再被销毁了,而是会回调activity中的onConfigurationChanged方法,这时就需要在这里处理相关逻辑.
五.android:configChanges的详细参数
六.关于使用横竖屏等多套资源
如上图,我们是可以在Res下同时配置多套资源文件的,layout是默认的资源文件,layout-land是横屏下的资源文件.(value等资源也可以配置多套)
如果没有配置android:configChanges设置,既没有自行处理配置变更,activity被销毁重建后,会自动去加载相应目录下的资源,这个开发者只需要同时准备了多套资源就行,而不用在运行时关注配置的变更(屏幕方向旋转等).
如果配置android:configChanges设置,既自行处理配置变更,activity会在被创建时就去加载相应资源文件下的资源,也就是如果是横屏进来的,就会加载layout-land下的资源文件.后面配置的变更(屏幕方向旋转等),activity不会再去加载资源(即使配置了多套资源),这时就需要在onConfigurationChanged方法中根据Configuration参数去自行刷新相应的资源文件了.
七.获取当前屏幕横竖屏状态
通过getResources().getConfiguration().orientation可以拿到当前屏幕方向的状态值
Configuration.ORIENTATION_LANDSCAPE 表示的是横屏
八.关于onConfigurationChanged方法
onConfigurationChanged回调不仅仅在activity中有,fragment和view,viewgroup等都会有
以下是onConfigurationChanged的回调流程:
1.activity
在ActivityThread中收到配置变化的消息,然后在handleActivityConfigurationChanged和performConfigurationChanged处理,回调到activity.
如果有多个activity,只会回调正在running的activity,其他的activity只有重新激活并且屏幕状态和当前状态不一样时才会回调(官方只说了回调正在running的activity,但是测试证明点击返回后其他的activity也会回调)
final voidhandleActivityConfigurationChanged(ActivityConfigChangeData data) {
ActivityClientRecord r =mActivities.get(data.activityToken);
if (r == null || r.activity == null) {
return;
}
if (DEBUG_CONFIGURATION) Slog.v(TAG,”Handle activity config changed: “
- r.activityInfo.name);
r.tmpConfig.setTo(mCompatConfiguration);
if (data.overrideConfig != null) {
r.overrideConfig =data.overrideConfig;
r.tmpConfig.updateFrom(data.overrideConfig);
}
performConfigurationChanged(r.activity,r.tmpConfig);
freeTextLayoutCachesIfNeeded(r.activity.mCurrentConfig.diff(mCompatConfiguration));
mSomeActivitiesChanged = true;
}
private static voidperformConfigurationChanged(ComponentCallbacks2 cb, Configurationconfig) {
// Only for Activity objects, check thatthey actually call up to their
// superclass implementation. ComponentCallbacks2 is an interface, so
// we check the runtime type and actaccordingly.
Activity activity = (cb instanceofActivity) ? (Activity) cb : null;
if (activity != null) {
activity.mCalled = false;
}
boolean shouldChangeConfig = false;
if ((activity == null) ||(activity.mCurrentConfig == null)) {
shouldChangeConfig = true;
} else {
// If the new config is the same asthe config this Activity
// is already running with then don’tbother calling
// onConfigurationChanged
int diff =activity.mCurrentConfig.diff(config);
if (diff != 0) {
// If this activity doesn’t handleany of the config changes
// then don’t bother callingonConfigurationChanged as we’re
// going to destroy it.
if((~activity.mActivityInfo.getRealConfigChanged() & diff) == 0) {
shouldChangeConfig = true;
}
}
}
if (DEBUG_CONFIGURATION) Slog.v(TAG,”Config callback ” + cb
- “: shouldChangeConfig=”+ shouldChangeConfig);
if (shouldChangeConfig) {
cb.onConfigurationChanged(config);
if (activity != null) {
if (!activity.mCalled) {
throw newSuperNotCalledException(
“Activity “+ activity.getLocalClassName() +
” did not callthrough to super.onConfigurationChanged()”);
}
activity.mConfigChangeFlags = 0;
activity.mCurrentConfig = newConfiguration(config);
}
}
}
2.fragment
(1)Activity中的处理
public voidonConfigurationChanged(Configuration newConfig) {
if (DEBUG_LIFECYCLE) Slog.v(TAG,”onConfigurationChanged ” + this + “: ” +newConfig);
mCalled = true;
mFragments.dispatchConfigurationChanged(newConfig);
if (mWindow != null) {
// Pass the configuration changedevent to the window
mWindow.onConfigurationChanged(newConfig);
}
if (mActionBar != null) {
// Do this last; the action bar willneed to access
// view changes from above.
mActionBar.onConfigurationChanged(newConfig);
}
}
(2)FragmentController中转发
public voiddispatchConfigurationChanged(Configuration newConfig) {
mHost.mFragmentManager.dispatchConfigurationChanged(newConfig);
}
(3)FragmentManager中分发到activity中的所有fragment
public voiddispatchConfigurationChanged(Configuration newConfig) {
if (mAdded != null) {
for (int i=0; i