Android状态栏右侧添加图标并控制其显示状态

2024-08-21 10:08

本文主要是介绍Android状态栏右侧添加图标并控制其显示状态,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

背景介绍

最近接到一个需求。Android机器外接一个21key的键盘。键盘上有一个绿色和黄色按键。其功能就是当按下时,再按其他键会上报不同的键值。我们的外国合作伙伴就发现了一个问题,机器的页面上没有显示出按下绿键或者黄键的状态,就是说用户可能不知道当前键盘是在哪种输入状态。所以要求我们加上一个提示出来。

21key键盘
先看下最终效果,当按下绿色按键后,显示一个g字母,表示green的意思,黄色按键就是y,yellow。

g

思路

介绍完背景了,就讲一下实现过程吧。

其实刚开始我能想到的是直接加一个notification。但是!需求只是给用户个提示而已,notification不止在状态栏会显示在下拉菜单也会有个通知。这就有点杀鸡用牛刀的感觉。所以最好是在StatusBar右侧,电量图标旁边加上一个指示图标。

实现过程

本文是基于android P实现的,开始时候真的小看StatusBar了,以为会很简单,没想到一入源码深似海,进去半天出不来。不知道小伙伴们有没有用Hierarchy view看过StatusBar,它的Tree View就相当复杂。下面是电池图标的位置。是不是头皮发麻。

在这里插入图片描述

看了很多博客,跟了一下流程找到了关键的类–PhoneStatusBarPolicy。
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
PhoneStatusBarPolicy:定义了系统通知图标的设置策略,监听图标改变广播。
看下它的构造方法。

public PhoneStatusBarPolicy(Context context, StatusBarIconController iconController) {mContext = context;mIconController = iconController;//省略...mSlotInputType = context.getString(com.android.internal.R.string.status_bar_input_type);mSlotCast = context.getString(com.android.internal.R.string.status_bar_cast);mSlotHotspot = context.getString(com.android.internal.R.string.status_bar_hotspot);mSlotBluetooth = context.getString(com.android.internal.R.string.status_bar_bluetooth);mSlotTty = context.getString(com.android.internal.R.string.status_bar_tty);mSlotZen = context.getString(com.android.internal.R.string.status_bar_zen);mSlotVolume = context.getString(com.android.internal.R.string.status_bar_volume);mSlotAlarmClock = context.getString(com.android.internal.R.string.status_bar_alarm_clock);mSlotManagedProfile = context.getString(com.android.internal.R.string.status_bar_managed_profile);mSlotRotate = context.getString(com.android.internal.R.string.status_bar_rotate);mSlotHeadset = context.getString(com.android.internal.R.string.status_bar_headset);mSlotDataSaver = context.getString(com.android.internal.R.string.status_bar_data_saver);mSlotLocation = context.getString(com.android.internal.R.string.status_bar_location);// listen for broadcastsIntentFilter filter = new IntentFilter();filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);filter.addAction(AudioManager.ACTION_HEADSET_PLUG);filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);filter.addAction(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED);filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);// listen for user / profile change.try {ActivityManager.getService().registerUserSwitchObserver(mUserSwitchListener, TAG);} catch (RemoteException e) {// Ignore}// TTY statusupdateTTY();// bluetooth statusupdateBluetooth();// Alarm clockmIconController.setIcon(mSlotAlarmClock, R.drawable.stat_sys_alarm, null);mIconController.setIconVisibility(mSlotAlarmClock, false);// zenmIconController.setIcon(mSlotZen, R.drawable.stat_sys_zen_important, null);mIconController.setIconVisibility(mSlotZen, false);// volumemIconController.setIcon(mSlotVolume, R.drawable.stat_sys_ringer_vibrate, null);mIconController.setIconVisibility(mSlotVolume, false);updateVolumeZen();// castmIconController.setIcon(mSlotCast, R.drawable.stat_sys_cast, null);mIconController.setIconVisibility(mSlotCast, false);// hotspotmIconController.setIcon(mSlotHotspot, R.drawable.stat_sys_hotspot,mContext.getString(R.string.accessibility_status_bar_hotspot));mIconController.setIconVisibility(mSlotHotspot, mHotspot.isHotspotEnabled());// managed profilemIconController.setIcon(mSlotManagedProfile, R.drawable.stat_sys_managed_profile_status,mContext.getString(R.string.accessibility_managed_profile));mIconController.setIconVisibility(mSlotManagedProfile, mManagedProfileIconVisible);//省略...}

可以看到在构造方法中注册了广播接收器mIntentReceiver,用来接收各种状态的改变,去更改对应的图标。

private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {String action = intent.getAction();switch (action) {case AudioManager.RINGER_MODE_CHANGED_ACTION:case AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION:updateVolumeZen();break;case TelephonyIntents.ACTION_SIM_STATE_CHANGED:// Avoid rebroadcast because SysUI is direct boot aware.if (intent.getBooleanExtra(TelephonyIntents.EXTRA_REBROADCAST_ON_UNLOCK,false)) {break;}updateSimState(intent);break;case TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED:updateTTY(intent.getIntExtra(TelecomManager.EXTRA_CURRENT_TTY_MODE,TelecomManager.TTY_MODE_OFF));break;case Intent.ACTION_MANAGED_PROFILE_AVAILABLE:case Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE:case Intent.ACTION_MANAGED_PROFILE_REMOVED:updateManagedProfile();break;case AudioManager.ACTION_HEADSET_PLUG:updateHeadsetPlug(intent);break;case ACTION_INPUT_STATUS_CHANGED:updateInputType(intent);break;}}};

接下来看代码猜也能猜出来了。

// Alarm clockmIconController.setIcon(mSlotAlarmClock, R.drawable.stat_sys_alarm, null);mIconController.setIconVisibility(mSlotAlarmClock, false);

很明显就是设置alarm图标的代码,第二行是控制显示还是隐藏。
ok,我们只需要模仿它来加上我们自己的图标就行了。

第一步声明Slot
其实就是个字符串而已。
frameworks/base/core/res/res/values/symbols.xml中加上声明

<java-symbol type="string" name="status_bar_input_type" />

在frameworks/base/core/res/res/values/config.xml的string-array name="config_statusBarIcons"中添加我们自己的slot。比如:

<item><xliff:g id="id">@string/status_bar_input_type</xliff:g></item>

然后继续添加

<string translatable="false" name="status_bar_input_type">input_type</string>

第二步
在PhoneStatusBarPolicy.java中添加图标。
先声明一个slot 和一个广播aciton

private final String mSlotInputType;
private final String ACTION_INPUT_STATUS_CHANGED = "android.intent.action.INPUT_STATUS_CHANGED";

在构造方法中初始化

 mSlotInputType = context.getString(com.android.internal.R.string.status_bar_input_type);
//...省略代码....filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);//添加的actionfilter.addAction(ACTION_INPUT_STATUS_CHANGED);mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);

第三步
在广播接收器中调用按键状态改变后的方法,去改变图标显示。

case ACTION_INPUT_STATUS_CHANGED:updateInputType(intent);break;

第四步
实现updateInputType方法

private int[] inputTypeArr = {R.drawable.y_button,    //yellowR.drawable.g_button     //green};private final void updateInputType(Intent intent) {final int input = intent.getIntExtra(mSlotInputType, 0);if (input >= inputTypeArr.length) return;if(input < 0){mIconController.setIconVisibility(mSlotInputType, false);return;}int iconId = inputTypeArr[input];mIconController.setIcon(mSlotInputType, iconId, null);mIconController.setIconVisibility(mSlotInputType, true);}

相信大家都能看懂,如果intent传过来的参数小于零就隐藏图标,大于零的话根据参数选择显示y图标还是g图标。别忘了把图标加到drawable下面。
packages/SystemUI/res/drawable-hdpi/g_button.png
packages/SystemUI/res/drawable-hdpi/y_button.png
图标可以从阿里巴巴图标库下载。
至此就已经添加完毕。
使用的话只需发广播就可以了

/*** update Input Status*/public void updateInputStatus(int inputState){Intent intent = new Intent(ACTION_INPUT_STATUS_CHANGED);intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);intent.putExtra(mContext.getString(R.string.status_bar_input_type), inputState);mContext.sendBroadcastAsUser(intent, UserHandle.ALL);}

我是放到PhoneWindowManager中的interceptKeyBeforeQueueing方法中调用的。因为这里可以拦截Keyevent事件,根据keycode,scancode判断是黄色还是绿色按键,然后再传入不同的inputState参数,来控制状态栏的图标。

说实话SystemUI代码还是挺复杂的,这里只是提供了添加图标的方法,并不涉及到StatusBar初始化流程,有兴趣的小伙伴可以自己尝试跟一下源码。

这篇关于Android状态栏右侧添加图标并控制其显示状态的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1092858

相关文章

浅析Spring如何控制Bean的加载顺序

《浅析Spring如何控制Bean的加载顺序》在大多数情况下,我们不需要手动控制Bean的加载顺序,因为Spring的IoC容器足够智能,但在某些特殊场景下,这种隐式的依赖关系可能不存在,下面我们就来... 目录核心原则:依赖驱动加载手动控制 Bean 加载顺序的方法方法 1:使用@DependsOn(最直

Android kotlin中 Channel 和 Flow 的区别和选择使用场景分析

《Androidkotlin中Channel和Flow的区别和选择使用场景分析》Kotlin协程中,Flow是冷数据流,按需触发,适合响应式数据处理;Channel是热数据流,持续发送,支持... 目录一、基本概念界定FlowChannel二、核心特性对比数据生产触发条件生产与消费的关系背压处理机制生命周期

Android ClassLoader加载机制详解

《AndroidClassLoader加载机制详解》Android的ClassLoader负责加载.dex文件,基于双亲委派模型,支持热修复和插件化,需注意类冲突、内存泄漏和兼容性问题,本文给大家介... 目录一、ClassLoader概述1.1 类加载的基本概念1.2 android与Java Class

Spring如何使用注解@DependsOn控制Bean加载顺序

《Spring如何使用注解@DependsOn控制Bean加载顺序》:本文主要介绍Spring如何使用注解@DependsOn控制Bean加载顺序,具有很好的参考价值,希望对大家有所帮助,如有错误... 目录1.javascript 前言2. 代码实现总结1. 前言默认情况下,Spring加载Bean的顺

SpringSecurity显示用户账号已被锁定的原因及解决方案

《SpringSecurity显示用户账号已被锁定的原因及解决方案》SpringSecurity中用户账号被锁定问题源于UserDetails接口方法返回值错误,解决方案是修正isAccountNon... 目录SpringSecurity显示用户账号已被锁定的解决方案1.问题出现前的工作2.问题出现原因各

Android DataBinding 与 MVVM使用详解

《AndroidDataBinding与MVVM使用详解》本文介绍AndroidDataBinding库,其通过绑定UI组件与数据源实现自动更新,支持双向绑定和逻辑运算,减少模板代码,结合MV... 目录一、DataBinding 核心概念二、配置与基础使用1. 启用 DataBinding 2. 基础布局

Android ViewBinding使用流程

《AndroidViewBinding使用流程》AndroidViewBinding是Jetpack组件,替代findViewById,提供类型安全、空安全和编译时检查,代码简洁且性能优化,相比Da... 目录一、核心概念二、ViewBinding优点三、使用流程1. 启用 ViewBinding (模块级

基于Python开发Windows屏幕控制工具

《基于Python开发Windows屏幕控制工具》在数字化办公时代,屏幕管理已成为提升工作效率和保护眼睛健康的重要环节,本文将分享一个基于Python和PySide6开发的Windows屏幕控制工具,... 目录概述功能亮点界面展示实现步骤详解1. 环境准备2. 亮度控制模块3. 息屏功能实现4. 息屏时间

苹果macOS 26 Tahoe主题功能大升级:可定制图标/高亮文本/文件夹颜色

《苹果macOS26Tahoe主题功能大升级:可定制图标/高亮文本/文件夹颜色》在整体系统设计方面,macOS26采用了全新的玻璃质感视觉风格,应用于Dock栏、应用图标以及桌面小部件等多个界面... 科技媒体 MACRumors 昨日(6 月 13 日)发布博文,报道称在 macOS 26 Tahoe 中

Python远程控制MySQL的完整指南

《Python远程控制MySQL的完整指南》MySQL是最流行的关系型数据库之一,Python通过多种方式可以与MySQL进行交互,下面小编就为大家详细介绍一下Python操作MySQL的常用方法和最... 目录1. 准备工作2. 连接mysql数据库使用mysql-connector使用PyMySQL3.