android 横竖屏切换 源码剖析

以通知栏中下拉菜单里面的"自动旋转"按钮为例子,说下横竖屏切换的原理;

点击状态栏中的"自动旋转"按钮,会走下面代码:

frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java

protected void handleClick() {
    //"自动旋转"的点击事件
    if (mController == null) return;
    final boolean newState = !mState.value;
    //设置横竖屏,newState表示是否保存当前旋转方向
    mController.setRotationLocked(!newState);
    //刷新界面
    refreshState(newState);
}

 我们主要看的是,横竖屏是如何设置的,mController的实现类是RotationLockControllerImpl:

public void setRotationLocked(boolean locked) {
    RotationPolicy.setRotationLock(mContext, locked);
}

进入到了RotationPolicy里面,继续看:

public static void setRotationLock(Context context, final boolean enabled) {
    Settings.System.putIntForUser(context.getContentResolver(),
            Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, 0,
            UserHandle.USER_CURRENT);

    //屏幕旋转度数
    final int rotation = areAllRotationsAllowed(context) ? CURRENT_ROTATION : NATURAL_ROTATION;
    //设置旋转
    setRotationLock(enabled, rotation);
}

private static void setRotationLock(final boolean enabled, final int rotation) {
    AsyncTask.execute(new Runnable() {
        @Override
        public void run() {
            try {
                IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
                if (enabled) {
                    //旋转屏幕,并保持旋转后的方向
                    wm.freezeRotation(rotation);
                } else {
                    //可以自由旋转方向,也就是横竖屏自由切换
                    wm.thawRotation();
                }
            } catch (RemoteException exc) {
                Log.w(TAG, "Unable to save auto-rotate setting");
            }
        }
    });
}

可以看到,wm就是WMS,wm.freezeRotation(rotation)方法可以固定屏幕的旋转方向,rotation就是屏幕的旋转度数;

rotation只有两个值,CURRENT_ROTATION和NATURAL_ROTATION,分别是-1和0;

0我们知道是竖屏的意思,那这个-1呢,它表示的是固定当前屏幕的方向。假如我们在横屏时想固定横屏方向,就可以使用RotationPolicy.setRotationLock(context,true)函数;

private static final int CURRENT_ROTATION = -1;
private static final int NATURAL_ROTATION = Surface.ROTATION_0;

当然你想让屏幕旋转到其他位置也是可以的,Surface中设置了4种旋转角度:

/**
 * Rotation constant: 0 degree rotation (natural orientation)
 */
public static final int ROTATION_0 = 0;

/**
 * Rotation constant: 90 degree rotation.
 */
public static final int ROTATION_90 = 1;

/**
 * Rotation constant: 180 degree rotation.
 */
public static final int ROTATION_180 = 2;

/**
 * Rotation constant: 270 degree rotation.
 */
public static final int ROTATION_270 = 3;

但是,RotationPolicy并没有提供设置旋转角度的api,如果有这方面的需求,可以调用WMS.freezeRotation()函数;

接下来就看屏幕旋转是如何实现的;


public void freezeRotation(int rotation) {
    // TODO(multi-display): Track which display is rotated.
    if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION,
            "freezeRotation()")) {
        throw new SecurityException("Requires SET_ORIENTATION permission");
    }
    if (rotation < -1 || rotation > Surface.ROTATION_270) {
        throw new IllegalArgumentException("Rotation argument must be -1 or a valid "
                + "rotation constant.");
    }

    //得到当前的屏幕旋转方向
    final int defaultDisplayRotation = getDefaultDisplayRotation();
    if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "freezeRotation: mRotation="
            + defaultDisplayRotation);

    long origId = Binder.clearCallingIdentity();
    try {
        //保存新的屏幕方向,
        //rotation == -1表示锁定当前的屏幕旋转方向,比如屏幕自由旋转中,旋转到了横屏,那就可以锁定横屏,一直保持横屏状态
        mPolicy.setUserRotationMode(WindowManagerPolicy.USER_ROTATION_LOCKED,
                rotation == -1 ? defaultDisplayRotation : rotation);
    } finally {
        Binder.restoreCallingIdentity(origId);
    }

    //更新屏幕状态,这个才是核心
    updateRotationUnchecked(false, false);
}

private void updateRotationUnchecked(boolean alwaysSendConfiguration, boolean forceRelayout) {
    if(DEBUG_ORIENTATION) Slog.v(TAG_WM, "updateRotationUnchecked:"
            + " alwaysSendConfiguration=" + alwaysSendConfiguration
            + " forceRelayout=" + forceRelayout);

    Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateRotation");

    long origId = Binder.clearCallingIdentity();

    try {
        // TODO(multi-display): Update rotation for different displays separately.
        final boolean rotationChanged;
        final int displayId;
        synchronized (mWindowMap) {
            final DisplayContent displayContent = getDefaultDisplayContentLocked();
            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateRotation: display");
            //更新屏幕旋转状态,关键代码
            rotationChanged = displayContent.updateRotationUnchecked(
                    false /* inTransaction */);
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
            if (!rotationChanged || forceRelayout) {
                displayContent.setLayoutNeeded();
                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
                        "updateRotation: performSurfacePlacement");
                mWindowPlacerLocked.performSurfacePlacement();
                Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
            }
            displayId = displayContent.getDisplayId();
        }

        if (rotationChanged || alwaysSendConfiguration) {
            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateRotation: sendNewConfiguration");
            sendNewConfiguration(displayId);
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        }
    } finally {
        Binder.restoreCallingIdentity(origId);
        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
    }
}


boolean updateRotationUnchecked(boolean inTransaction) {
    if (mService.mDeferredRotationPauseCount > 0) {
        // Rotation updates have been paused temporarily.  Defer the update until
        // updates have been resumed.
        if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Deferring rotation, rotation is paused.");
        return false;
    }
    //旋转动画,mService.mAnimator持有DimSurface,这个就是最终要使用的Surface
    ScreenRotationAnimation screenRotationAnimation =
            mService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
    ......

    final int oldRotation = mRotation;
    final int lastOrientation = mLastOrientation;
    final boolean oldAltOrientation = mAltOrientation;
    //后续的计算都基于这个值
    int rotation = mService.mPolicy.rotationForOrientationLw(lastOrientation, oldRotation);
    boolean mayRotateSeamlessly = mService.mPolicy.shouldRotateSeamlessly(oldRotation,
            rotation);

    ......
    
    mRotation = rotation;
    mAltOrientation = altOrientation;
    if (isDefaultDisplay) {
        //更新旋转状态,同步到WindowOrientationListener中
        mService.mPolicy.setRotationLw(rotation);
    }

    ......

    return true;
}

其中mPolicy是PhoneWindowManager;

关于这个代码暂时就先不分析了,有机会再来继续看,今天的主要目的还是介绍下如何设置屏幕旋转状态;

完结,散花!

 

你可能感兴趣的:(android开发)