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如何使用注解@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.

RedisTemplate默认序列化方式显示中文乱码的解决

《RedisTemplate默认序列化方式显示中文乱码的解决》本文主要介绍了SpringDataRedis默认使用JdkSerializationRedisSerializer导致数据乱码,文中通过示... 目录1. 问题原因2. 解决方案3. 配置类示例4. 配置说明5. 使用示例6. 验证存储结果7.

如何搭建并配置HTTPD文件服务及访问权限控制

《如何搭建并配置HTTPD文件服务及访问权限控制》:本文主要介绍如何搭建并配置HTTPD文件服务及访问权限控制的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、安装HTTPD服务二、HTTPD服务目录结构三、配置修改四、服务启动五、基于用户访问权限控制六、

Android学习总结之Java和kotlin区别超详细分析

《Android学习总结之Java和kotlin区别超详细分析》Java和Kotlin都是用于Android开发的编程语言,它们各自具有独特的特点和优势,:本文主要介绍Android学习总结之Ja... 目录一、空安全机制真题 1:Kotlin 如何解决 Java 的 NullPointerExceptio