Android 11 的状态栏的隐藏

概述

Android 11 的状态栏与导航栏较之前的版本有较大的差异, 在Android 7.0 SystemUI 状态/导航栏的隐藏与显示中所描述的部分内容已不再适用.
比如, 自动隐藏的时间, 隐藏的动画, 较之前的版本已面目全非, 本文将对隐藏状态栏部分的内容进行一些补充.

APP如何隐藏状态栏

  • 参考文档:隐藏状态栏_Android 开发者_Android Developers.pdf)
方法一:
 <application
        ...
        android:theme="@android:style/Theme.Holo.NoActionBar.Fullscreen" >
        ...
    application>

方法二:

    public class MainActivity extends Activity {

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            // If the Android version is lower than Jellybean, use this call to hide
            // the status bar.
            if (Build.VERSION.SDK_INT < 16) {
                getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                        WindowManager.LayoutParams.FLAG_FULLSCREEN);
            }
            setContentView(R.layout.activity_main);
        }
        ...
    }

方法三:(在4.1(SDK 16)及之上)

    View decorView = getWindow().getDecorView();
    // Hide the status bar.
    int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN;
    decorView.setSystemUiVisibility(uiOptions);
    // Remember that you should never show the action bar if the
    // status bar is hidden, so hide that too if necessary.
    ActionBar actionBar = getActionBar();
    actionBar.hide();
    

状态栏是怎么被隐藏的

1. 通过上面的方式主动调用接口设置窗口属性, 由系统判断并完成隐藏
Android 11 的状态栏的隐藏_第1张图片

frameworks/base/core/java/android/view/InsetsController.java

        @Override
        public void onReady(WindowInsetsAnimationController controller, int types) {
            mController = controller;
            if (DEBUG) Log.d(TAG, "default animation onReady types: " + types);

            if (mDisable) {
                onAnimationFinish();
                return;
            }
            mAnimator = ValueAnimator.ofFloat(0f, 1f);
            mAnimator.setDuration(mDurationMs);
            mAnimator.setInterpolator(new LinearInterpolator());
            Insets hiddenInsets = controller.getHiddenStateInsets();
            // IME with zero insets is a special case: it will animate-in from offscreen and end
            // with final insets of zero and vice-versa.
            hiddenInsets = controller.hasZeroInsetsIme()
                    ? Insets.of(hiddenInsets.left, hiddenInsets.top, hiddenInsets.right,
                            mFloatingImeBottomInset)
                    : hiddenInsets;
            Insets start = mShow
                    ? hiddenInsets
                    : controller.getShownStateInsets();
            Insets end = mShow
                    ? controller.getShownStateInsets()
                    : hiddenInsets;
            Interpolator insetsInterpolator = getInterpolator();
            Interpolator alphaInterpolator = getAlphaInterpolator();
            mAnimator.addUpdateListener(animation -> {
                float rawFraction = animation.getAnimatedFraction();
                float alphaFraction = mShow
                        ? rawFraction
                        : 1 - rawFraction;
                float insetsFraction = insetsInterpolator.getInterpolation(rawFraction);
                controller.setInsetsAndAlpha(
                        sEvaluator.evaluate(insetsFraction, start, end),
                        alphaInterpolator.getInterpolation(alphaFraction),
                        rawFraction);
                if (DEBUG) Log.d(TAG, "Default animation setInsetsAndAlpha fraction: "
                        + insetsFraction);
            });
            mAnimator.addListener(new AnimatorListenerAdapter() {

                @Override
                public void onAnimationEnd(Animator animation) {
                    onAnimationFinish();
                }
            });
            if (!mHasAnimationCallbacks) {
                mAnimator.setAnimationHandler(mSfAnimationHandlerThreadLocal.get());
            }
            mAnimator.start();
        }

为定位到这一段代码, 走了相当曲折又漫长的路.

  • 第一个误区: 参照之前的状态栏隐藏流程代码, 在frameworks/base/service中转了很长时间.
  • 第二个误区: 误判隐藏动画的是在SystemUI中实现.

在不断地调试分析后, 最终的突破口在SurfaceControl的子类Transaction
最终输出以下LOG, 最终定位到上面的代码.

2022-12-09 17:56:47.413   	at android.view.SurfaceControl$SurfaceControl.setPosition(SurfaceControl.java:2435)
2022-12-09 17:56:47.413   	at android.view.SurfaceControl$Transaction.setMatrix(SurfaceControl.java:2609)
2022-12-09 17:56:47.413   	at android.view.SyncRtSurfaceTransactionApplier.applyParams(SyncRtSurfaceTransactionApplier.java:97)
2022-12-09 17:56:47.413   	at com.android.server.wm.InsetsPolicy$InsetsPolicyAnimationControlListener$InsetsPolicyAnimationControlCallbacks.applySurfaceParams(InsetsPolicy.java:542)
2022-12-09 17:56:47.413   	at android.view.InsetsAnimationControlImpl.applyChangeInsets(InsetsAnimationControlImpl.java:214)
2022-12-09 17:56:47.413   	at com.android.server.wm.InsetsPolicy$InsetsPolicyAnimationControlListener$InsetsPolicyAnimationControlCallbacks.scheduleApplyChangeInsets(InsetsPolicy.java:510)
2022-12-09 17:56:47.413   	at android.view.InsetsAnimationControlImpl.setInsetsAndAlpha(InsetsAnimationControlImpl.java:186)
2022-12-09 17:56:47.413   	at android.view.InsetsAnimationControlImpl.setInsetsAndAlpha(InsetsAnimationControlImpl.java:170)
2022-12-09 17:56:47.414   	at android.view.InsetsController$InternalAnimationControlListener.lambda$onReady$0$InsetsController$InternalAnimationControlListener(InsetsController.java:332)
2022-12-09 17:56:47.414   	at android.view.-$$Lambda$InsetsController$InternalAnimationControlListener$SInf91MjJKDQFXwrp7C-HBi0xaQ.onAnimationUpdate(Unknown Source:13)
2022-12-09 17:56:47.414   	at android.animation.ValueAnimator.animateValue(ValueAnimator.java:1566)
2022-12-09 17:56:47.414   	at android.animation.ValueAnimator.animateBasedOnTime(ValueAnimator.java:1357)
2022-12-09 17:56:47.414   	at android.animation.ValueAnimator.doAnimationFrame(ValueAnimator.java:1489)
2022-12-09 17:56:47.414   	at android.animation.AnimationHandler.doAnimationFrame(AnimationHandler.java:146)
2022-12-09 17:56:47.414   	at android.animation.AnimationHandler.access$100(AnimationHandler.java:37)
2022-12-09 17:56:47.414   	at android.animation.AnimationHandler$1.doFrame(AnimationHandler.java:54)
2022-12-09 17:56:47.415   	at android.view.Choreographer$CallbackRecord.run(Choreographer.java:970)
2022-12-09 17:56:47.415   	at android.view.Choreographer.doCallbacks(Choreographer.java:796)
2022-12-09 17:56:47.415   	at android.view.Choreographer.doFrame(Choreographer.java:727)
2022-12-09 17:56:47.415   	at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:957)
2022-12-09 17:56:47.415   	at android.os.Handler.handleCallback(Handler.java:938)
2022-12-09 17:56:47.415   	at android.os.Handler.dispatchMessage(Handler.java:99)
2022-12-09 17:56:47.415   	at android.os.Looper.loop(Looper.java:223)
2022-12-09 17:56:47.415   	at android.os.HandlerThread.run(HandlerThread.java:67)
2022-12-09 17:56:47.415   	at com.android.server.ServiceThread.run(ServiceThread.java:44)

状态栏/导航栏 隐藏与显示动画过渡时长:
在定位到代码后, 最先做的事情, 就是把时间增加10倍, 编译看效果.

frameworks/base/core/java/android/view/InsetsController.java

    private static final int ANIMATION_DURATION_SHOW_MS = 275;
    private static final int ANIMATION_DURATION_HIDE_MS = 340;

2. 隐藏后, 用户下拉状态栏, SystemUI定时执行自动隐藏(AutoHideController)

frameworks/base/services/core/java/com/android/server/wm/InsetsPolicy.java

    void hideTransient() {
        if (mShowingTransientTypes.size() == 0) {
            return;
        }
        InsetsState state = new InsetsState(mStateController.getRawInsetsState());
        startAnimation(false /* show */, () -> {
            synchronized (mDisplayContent.mWmService.mGlobalLock) {
                mShowingTransientTypes.clear();
                mStateController.notifyInsetsChanged();
                updateBarControlTarget(mFocusedWin);
            }
        }, state);
        mStateController.onInsetsModified(mDummyControlTarget, state);
    }
    void startAnimation(boolean show, Runnable callback, InsetsState state) {
        int typesReady = 0;
        final SparseArray<InsetsSourceControl> controls = new SparseArray<>();
        final IntArray showingTransientTypes = mShowingTransientTypes;
        for (int i = showingTransientTypes.size() - 1; i >= 0; i--) {
            int type = showingTransientTypes.get(i);
            InsetsSourceProvider provider = mStateController.getSourceProvider(type);
            InsetsSourceControl control = provider.getControl(mDummyControlTarget);
            if (control == null || control.getLeash() == null) {
                continue;
            }
            typesReady |= InsetsState.toPublicType(type);
            controls.put(control.getType(), new InsetsSourceControl(control));
            state.setSourceVisible(type, show);
        }
        controlAnimationUnchecked(typesReady, controls, show, callback);
    }
    private void controlAnimationUnchecked(int typesReady,
            SparseArray<InsetsSourceControl> controls, boolean show, Runnable callback) {
        InsetsPolicyAnimationControlListener listener =
                new InsetsPolicyAnimationControlListener(show, callback, typesReady);
        listener.mControlCallbacks.controlAnimationUnchecked(typesReady, controls, show);
    }

    private class InsetsPolicyAnimationControlListener extends
            InsetsController.InternalAnimationControlListener {
        Runnable mFinishCallback;
        InsetsPolicyAnimationControlCallbacks mControlCallbacks;

        InsetsPolicyAnimationControlListener(boolean show, Runnable finishCallback, int types) {

            super(show, false /* hasCallbacks */, types, false /* disable */,
                    (int) (mDisplayContent.getDisplayMetrics().density * FLOATING_IME_BOTTOM_INSET
                            + 0.5f));
            mFinishCallback = finishCallback;
            mControlCallbacks = new InsetsPolicyAnimationControlCallbacks(this);
        }

        private class InsetsPolicyAnimationControlCallbacks implements
                InsetsAnimationControlCallbacks {
			...
            private void controlAnimationUnchecked(int typesReady,
                    SparseArray<InsetsSourceControl> controls, boolean show) {
                if (typesReady == 0) {
                    // nothing to animate.
                    return;
                }
                mAnimatingShown = show;

                mAnimationControl = new InsetsAnimationControlImpl(controls,
                        mFocusedWin.getDisplayContent().getBounds(), getState(),
                        mListener, typesReady, this, mListener.getDurationMs(),
                        InsetsController.SYSTEM_BARS_INTERPOLATOR,
                        show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE);
                SurfaceAnimationThread.getHandler().post(
                        () -> mListener.onReady(mAnimationControl, typesReady));
            }

一些类图

Android 11 的状态栏的隐藏_第2张图片

一些关键代码.

frameworks/base/core/java/android/view/ViewRootImpl.java

                case MSG_SHOW_INSETS: {
                    if (mView == null) {
                        Log.e(TAG,
                                String.format("Calling showInsets(%d,%b) on window that no longer"
                                        + " has views.", msg.arg1, msg.arg2 == 1));
                    }
                    clearLowProfileModeIfNeeded(msg.arg1, msg.arg2 == 1);
                    mInsetsController.show(msg.arg1, msg.arg2 == 1);
                    break;
                }
    private void controlInsetsForCompatibility(WindowManager.LayoutParams params) {
        if (sNewInsetsMode != NEW_INSETS_MODE_FULL) {
            return;
        }
        final int sysUiVis = params.systemUiVisibility | params.subtreeSystemUiVisibility;
        final int flags = params.flags;
        final boolean matchParent = params.width == MATCH_PARENT && params.height == MATCH_PARENT;
        final boolean nonAttachedAppWindow = params.type >= FIRST_APPLICATION_WINDOW
                && params.type <= LAST_APPLICATION_WINDOW;
        final boolean statusWasHiddenByFlags = (mTypesHiddenByFlags & Type.statusBars()) != 0;
        final boolean statusIsHiddenByFlags = (sysUiVis & SYSTEM_UI_FLAG_FULLSCREEN) != 0
                || ((flags & FLAG_FULLSCREEN) != 0 && matchParent && nonAttachedAppWindow);
        final boolean navWasHiddenByFlags = (mTypesHiddenByFlags & Type.navigationBars()) != 0;
        final boolean navIsHiddenByFlags = (sysUiVis & SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0;

        @InsetsType int typesToHide = 0;
        @InsetsType int typesToShow = 0;
        if (statusIsHiddenByFlags && !statusWasHiddenByFlags) {
            typesToHide |= Type.statusBars();
        } else if (!statusIsHiddenByFlags && statusWasHiddenByFlags) {
            typesToShow |= Type.statusBars();
        }
        if (navIsHiddenByFlags && !navWasHiddenByFlags) {
            typesToHide |= Type.navigationBars();
        } else if (!navIsHiddenByFlags && navWasHiddenByFlags) {
            typesToShow |= Type.navigationBars();
        }
        if (typesToHide != 0) {
            getInsetsController().hide(typesToHide);
        }
        if (typesToShow != 0) {
            getInsetsController().show(typesToShow);
        }
        mTypesHiddenByFlags |= typesToHide;
        mTypesHiddenByFlags &= ~typesToShow;
    }

参考

隐藏状态栏| Android 开发者
Android 显示、隐藏状态栏和导航栏
SurfaceFlinger中Layer的修改 - 安卓R

你可能感兴趣的:(android,android,SystemUI,StatusBar,状态栏,隐藏)