Android 11 SystemUI(状态/导航栏)-状态栏下拉时图标的隐藏与通知面板的半透黑色背景

简介: Android 11 SystemUI(状态/导航栏)-状态栏下拉时图标的隐藏与通知面板的半透黑色背景

概述


本文续自:Android 11 的状态栏的隐藏


PS

本文虽然同属于SystemUI, 但目前并 没有 打算整理成专橍或撰写一个系列的想法.

仅仅为了记录一些过程, 留下那些容易被遗忘的点滴.


开始下拉时状态栏图标被隐藏


  状态橍的图标在用户开始触摸(ACTION_DOWN)后, 会开始展开, 显示扩展面板, 同时, 隐藏状态橍上的通知和状态图标. 在手机上表现有可能不同, 在android 13上, 在点击没有作用, 只有下拉一定的距离,才会开始隐藏.


image.png

device-2022-12-21-190046


image.png


隐藏从触摸下按开始, 参照下图:

image.png


忽略过程代码, 直接上最后一部分:


frameworks/base/packages/SystemUI//src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java


@Override
    public void disable(int displayId, int state1, int state2, boolean animate) {
        if (displayId != getContext().getDisplayId()) {
            return;
        }
        state1 = adjustDisableFlags(state1);
        final int old1 = mDisabled1;
        final int diff1 = state1 ^ old1;
        mDisabled1 = state1;
        if ((diff1 & DISABLE_SYSTEM_INFO) != 0) {
            if ((state1 & DISABLE_SYSTEM_INFO) != 0) {
                hideSystemIconArea(animate);
                hideOperatorName(animate);
            } else {
                showSystemIconArea(animate);
                showOperatorName(animate);
            }
        }
        if ((diff1 & DISABLE_NOTIFICATION_ICONS) != 0) {
            if ((state1 & DISABLE_NOTIFICATION_ICONS) != 0) {
                hideNotificationIconArea(animate);
            } else {
                showNotificationIconArea(animate);
            }
        }
        // The clock may have already been hidden, but we might want to shift its
        // visibility to GONE from INVISIBLE or vice versa
        if ((diff1 & DISABLE_CLOCK) != 0 || mClockView.getVisibility() != clockHiddenMode()) {
            if ((state1 & DISABLE_CLOCK) != 0) {
                hideClock(animate);
            } else {
                showClock(animate);
            }
        }
    }
    protected int adjustDisableFlags(int state) {
        boolean headsUpVisible = mStatusBarComponent.headsUpShouldBeVisible();
        if (headsUpVisible) {
            state |= DISABLE_CLOCK;
        }
        if (!mKeyguardStateController.isLaunchTransitionFadingAway()
                && !mKeyguardStateController.isKeyguardFadingAway()
                && shouldHideNotificationIcons()
                && !(mStatusBarStateController.getState() == StatusBarState.KEYGUARD
                        && headsUpVisible)) {
            state |= DISABLE_NOTIFICATION_ICONS;
            state |= DISABLE_SYSTEM_INFO;
            state |= DISABLE_CLOCK;
        }
        if (mNetworkController != null && EncryptionHelper.IS_DATA_ENCRYPTED) {
            if (mNetworkController.hasEmergencyCryptKeeperText()) {
                state |= DISABLE_NOTIFICATION_ICONS;
            }
            if (!mNetworkController.isRadioOn()) {
                state |= DISABLE_SYSTEM_INFO;
            }
        }
        // The shelf will be hidden when dozing with a custom clock, we must show notification
        // icons in this occasion.
        if (mStatusBarStateController.isDozing()
                && mStatusBarComponent.getPanelController().hasCustomClock()) {
            state |= DISABLE_CLOCK | DISABLE_SYSTEM_INFO;
        }
        return state;
    }


关于CollapsedStatusBarFragment


   在读到CollapsedStatusBarFragment相关代码的时候, 一时没办法把Fragment与SystemUI串联起来, 这源于传统Fragment的使用习惯: 在Activity中, 获取一个FragmentManager, 创建各种Fragment, 调用replace, show, hide…

   难道SystemUI也吃这一套? 状态栏和导航栏不一直是通过WindowManager.addView()直接往里面丢的么!

   CollapsedStatusBarFragment 本身没什么特别的, 它只是一个android.app.Fragment, 没有特殊的地方, 特殊的是FragmentController, 它与传统的Fragment使用方法完全不同, 参考 frameworks/base/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java

编写一段测试代码, 以便更直观了解它的用法:


public class TestActivity extends Activity{
  void testFragmentController(Context ctx, Handler h, int winAnim){
  if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
    FragmentController fragCtrl = FragmentController.createController(new FragmentHostCallback<Object>(ctx, h, 0) {
    @Override
    public Object onGetHost() {
      return null;
    }
    @Override
    public <T extends View> T onFindViewById(int id) {
      return findViewById(id);
    }
    });
    //java.lang.IllegalStateException: Activity has been destroyed
    //    at android.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1913)
    fragCtrl.attachHost(null);
    fragCtrl.dispatchCreate();
    fragCtrl.dispatchStart();
    fragCtrl.dispatchResume();
    //java.lang.IllegalArgumentException: No view found for id 0x7f08007c (com.android.factorytest:id/flRoot) for fragment SimpleFragment{2b5da47 #0 id=0x7f08007c}
    fragCtrl.getFragmentManager()
      .beginTransaction()
      .replace(R.id.flRoot, new SimpleFragment())
      .commit();
  }
  }
  public static class SimpleFragment extends Fragment{
  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    TextView tv = new TextView(getActivity());
    tv.setLayoutParams(new ViewGroup.LayoutParams(UiTools.MATCH_PARENT, UiTools.MATCH_PARENT));
    tv.setText("FragmentController");
    tv.setTextSize(36);
    return tv;
  }
  }
}


重点关注代码中的createController和FragmentHostCallback, 当创建完成后, 便可以通过 FragmentController的getFragmentManager获取FragmentManager, 接下来就是熟悉的操作, 不多描述.


下拉通知黑色背景


   在下拉通知面板的过程中, 存在两部分的半透明背景, 第一部分(上)比较明显, 第二部分比较隐藏晦.


效果如下图:

0a2653c851af460fa595bd959398a8f1.png

image.png


这是一个自定义的View, 从layout文件中可以找到它: ScrimView


frameworks/base/packages/SystemUI/res/layout/super_notification_shade.xml


<com.android.systemui.statusbar.phone.NotificationShadeWindowView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:sysui="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">
    <!-- 省略代码 -->
    <com.android.systemui.statusbar.ScrimView
        android:id="@+id/scrim_behind"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:importantForAccessibility="no"
        sysui:ignoreRightInset="true"
        />


frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java


diff --git a/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java b/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
index 7f30009cda..907d58c267 100644
--- a/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
+++ b/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
@@ -69,7 +69,7 @@ public class ScrimView extends View {
     @Override
     protected void onDraw(Canvas canvas) {
-        if (mDrawable.getAlpha() > 0) {
+        if (false && mDrawable.getAlpha() > 0) {//强制不进行绘制, 就不会出现半透明背景
             mDrawable.draw(canvas);
         }
     }


第二部分只有在下拉到底部时才会出现(不仔细分辨很难看出来):

image.png

同样, 来自另一个自定义控件:AlphaOptimizedView


frameworks\base\packages\SystemUI\res\layout\status_bar_expanded.xml


<com.android.systemui.statusbar.AlphaOptimizedView
        android:id="@+id/qs_navbar_scrim"
        android:layout_height="66dp"
        android:layout_width="match_parent"
        android:layout_gravity="bottom"
        android:visibility="invisible"
        android:background="@drawable/qs_navbar_scrim" />


扩展


Dagger, 绕得脑壳疼, 记录下out下的路径


out/soong/.intermediates/frameworks/base/packages/SystemUI/SystemUI-core/android_common/kapt/gen/sources/com/android/systemui/dagger/DaggerSystemUIRootComponent.java


public final class DaggerSystemUIRootComponent implements SystemUIRootComponent {
  private Provider<SystemUIRootComponent> systemUIRootComponentProvider;
    this.systemUIRootComponentProvider = InstanceFactory.create((SystemUIRootComponent) this);
  private Provider<FragmentService> fragmentServiceProvider;
 this.fragmentServiceProvider =
        DoubleCheck.provider(
            FragmentService_Factory.create(
                systemUIRootComponentProvider, provideConfigurationControllerProvider));
}


frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java


@NonNull
    @Override
    public Application instantiateApplicationCompat(
            @NonNull ClassLoader cl, @NonNull String className)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        Application app = super.instantiateApplicationCompat(cl, className);
        if (app instanceof ContextInitializer) {
            ((ContextInitializer) app).setContextAvailableCallback(
                    context -> {
                        SystemUIFactory.createFromConfig(context);
                        SystemUIFactory.getInstance().getRootComponent().inject(
                                SystemUIAppComponentFactory.this);
                    }
            );
        }
        return app;
    }


frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java


private void init(Context context) {
        mRootComponent = buildSystemUIRootComponent(context);
        // Every other part of our codebase currently relies on Dependency, so we
        // really need to ensure the Dependency gets initialized early on.
        Dependency dependency = new Dependency();
        mRootComponent.createDependency().createSystemUI(dependency);
        dependency.start();
    }
    public static void createFromConfig(Context context) {
        if (mFactory != null) {
            return;
        }
        final String clsName = context.getString(R.string.config_systemUIFactoryComponent);
        if (clsName == null || clsName.length() == 0) {
            throw new RuntimeException("No SystemUIFactory component configured");
        }
        try {
            Class<?> cls = null;
            cls = context.getClassLoader().loadClass(clsName);
            mFactory = (SystemUIFactory) cls.newInstance();
            mFactory.init(context);
        } catch (Throwable t) {
            Log.w(TAG, "Error creating SystemUIFactory component: " + clsName, t);
            throw new RuntimeException(t);
        }
    }


frameworks/base/packages/SystemUI/src/com/android/systemui/Dependency.java


@Inject Lazy<FragmentService> mFragmentService;
        protected void start() {
          mProviders.put(FragmentService.class, mFragmentService::get);
        }


frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java


FragmentHostManager.get(mPhoneStatusBarWindow)


frameworks/base/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java


public static FragmentHostManager get(View view) {
        try {
            return Dependency.get(FragmentService.class).getFragmentHostManager(view);
        } catch (ClassCastException e) {
            // TODO: Some auto handling here?
            throw e;
        }
    }


frameworks/base/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java


public FragmentHostManager getFragmentHostManager(View view) {
        View root = view.getRootView();
        FragmentHostState state = mHosts.get(root);
        if (state == null) {
            state = new FragmentHostState(root);
            mHosts.put(root, state);
        }
        return state.getFragmentHostManager();
    }


参考


SystemUI源码分析之PhoneStatusBar初始化布局简单分析

Android SystemUI 状态栏网络图标显示分析(Android 11)

SystemUI之状态图标控制

Android 8.0 SystemUI(三):一说顶部 StatusBar

Android 8.0 SystemUI(四):二说顶部 StatusBar

Dagger 基础知识

在 Android 应用中使用 Dagger


相关文章
|
14天前
|
前端开发 Java Shell
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
111 20
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
|
6月前
|
API Android开发 开发者
Android经典实战之用WindowInsetsControllerCompat方便的显示和隐藏状态栏和导航栏
本文介绍 `WindowInsetsControllerCompat` 类,它是 Android 提供的一种现代化工具,用于处理窗口插入如状态栏和导航栏的显示与隐藏。此类位于 `androidx.core.view` 包中,增强了跨不同 Android 版本的兼容性。主要功能包括控制状态栏与导航栏的显示、设置系统窗口行为及调整样式。通过 Kotlin 代码示例展示了如何初始化并使用此类,以及如何设置系统栏的颜色样式。
307 2
|
6月前
|
API Android开发 Kotlin
Android实战经验分享之如何获取状态栏和导航栏的高度
在Android开发中,掌握状态栏和导航栏的高度对于优化UI布局至关重要。本文介绍两种主要方法:一是通过资源名称获取,简单且兼容性好;二是利用WindowInsets,适用于新版Android,准确性高。文中提供了Kotlin代码示例,并对比了两者的优缺点及适用场景。
474 1
|
7月前
|
XML Android开发 数据格式
Android面试题之DialogFragment中隐藏导航栏
在Android中展示全屏`DialogFragment`并隐藏状态栏和导航栏,可通过设置系统UI标志实现。 记得在布局文件中添加内容,并使用`show()`方法显示`DialogFragment`。
89 2
|
8月前
|
API Android开发
31. 【Android教程】状态栏通知:Notification
31. 【Android教程】状态栏通知:Notification
634 1
|
2天前
|
JavaScript 搜索推荐 Android开发
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
21 8
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
|
12天前
|
Dart 前端开发 Android开发
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
35 4
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
|
27天前
|
缓存 前端开发 Android开发
【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
75 12
【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
|
1月前
|
Dart 前端开发 Android开发
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
36 1
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
|
2月前
|
搜索推荐 前端开发 API
探索安卓开发中的自定义视图:打造个性化用户界面
在安卓应用开发的广阔天地中,自定义视图是一块神奇的画布,让开发者能够突破标准控件的限制,绘制出独一无二的用户界面。本文将带你走进自定义视图的世界,从基础概念到实战技巧,逐步揭示如何在安卓平台上创建和运用自定义视图来提升用户体验。无论你是初学者还是有一定经验的开发者,这篇文章都将为你打开新的视野,让你的应用在众多同质化产品中脱颖而出。
77 19

热门文章

最新文章

  • 1
    如何修复 Android 和 Windows 不支持视频编解码器的问题?
  • 2
    【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
  • 3
    当flutter react native 等混开框架-并且用vscode-idea等编译器无法打包apk,打包安卓不成功怎么办-直接用android studio如何打包安卓apk -重要-优雅草卓伊凡
  • 4
    【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
  • 5
    APP-国内主流安卓商店-应用市场-鸿蒙商店上架之必备前提·全国公安安全信息评估报告如何申请-需要安全评估报告的资料是哪些-优雅草卓伊凡全程操作
  • 6
    【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
  • 7
    Android经典面试题之Kotlin中Lambda表达式和匿名函数的区别
  • 8
    【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
  • 9
    【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
  • 10
    Android 13 SystemUI 启动流程
  • 1
    【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
    23
  • 2
    【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
    21
  • 3
    APP-国内主流安卓商店-应用市场-鸿蒙商店上架之必备前提·全国公安安全信息评估报告如何申请-需要安全评估报告的资料是哪些-优雅草卓伊凡全程操作
    52
  • 4
    【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
    35
  • 5
    当flutter react native 等混开框架-并且用vscode-idea等编译器无法打包apk,打包安卓不成功怎么办-直接用android studio如何打包安卓apk -重要-优雅草卓伊凡
    70
  • 6
    【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
    111
  • 7
    Android经典面试题之Kotlin中Lambda表达式和匿名函数的区别
    29
  • 8
    如何修复 Android 和 Windows 不支持视频编解码器的问题?
    262
  • 9
    【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
    75
  • 10
    【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
    36