Android 11 的状态栏的隐藏

简介: 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. 通过上面的方式主动调用接口设置窗口属性, 由系统判断并完成隐藏

image.png


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));
            }


一些类图


0a2653c851af460fa595bd959398a8f1.png


一些关键代码.


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


相关文章
|
Java API Android开发
Android 最新实现沉浸式状态栏的效果
Android 最新实现沉浸式状态栏的效果
238 0
|
Android开发
android Compose中沉浸式设计、导航栏、状态栏的处理
android Compose中沉浸式设计、导航栏、状态栏的处理
2182 0
android Compose中沉浸式设计、导航栏、状态栏的处理
|
7月前
|
Android开发
Android 状态栏WiFi图标的显示逻辑
Android 状态栏WiFi图标的显示逻辑
186 0
|
4月前
|
API Android开发 开发者
Android经典实战之用WindowInsetsControllerCompat方便的显示和隐藏状态栏和导航栏
本文介绍 `WindowInsetsControllerCompat` 类,它是 Android 提供的一种现代化工具,用于处理窗口插入如状态栏和导航栏的显示与隐藏。此类位于 `androidx.core.view` 包中,增强了跨不同 Android 版本的兼容性。主要功能包括控制状态栏与导航栏的显示、设置系统窗口行为及调整样式。通过 Kotlin 代码示例展示了如何初始化并使用此类,以及如何设置系统栏的颜色样式。
239 2
|
4月前
|
API Android开发 Kotlin
Android实战经验分享之如何获取状态栏和导航栏的高度
在Android开发中,掌握状态栏和导航栏的高度对于优化UI布局至关重要。本文介绍两种主要方法:一是通过资源名称获取,简单且兼容性好;二是利用WindowInsets,适用于新版Android,准确性高。文中提供了Kotlin代码示例,并对比了两者的优缺点及适用场景。
382 1
|
6月前
|
API Android开发
31. 【Android教程】状态栏通知:Notification
31. 【Android教程】状态栏通知:Notification
593 1
|
7月前
|
XML 存储 测试技术
Android系统 添加动态控制SystemUI状态栏、导航栏和下拉菜单
Android系统 添加动态控制SystemUI状态栏、导航栏和下拉菜单
1307 1
|
XML Java API
Android 沉浸式状态栏必知必会
Android 沉浸式状态栏追根究底
821 0
|
Java 测试技术 API
Android透明状态栏和导航栏方案最终版
Android透明状态栏和导航栏方案最终版
880 0
|
Android开发
Android 11 SystemUI(状态/导航栏)-状态栏下拉时图标的隐藏与通知面板的半透黑色背景
Android 11 SystemUI(状态/导航栏)-状态栏下拉时图标的隐藏与通知面板的半透黑色背景
881 0
Android 11 SystemUI(状态/导航栏)-状态栏下拉时图标的隐藏与通知面板的半透黑色背景