前言
桌面圖示的角標,看著是個很簡單的功能,就是在應用的右上角顯示當前有幾個未讀訊息;在網上查了資料之后,發現很多同行說,Android原生是沒有此功能,平時使用的手機都有該功能,其實是國內手機廠商自己定制的桌面圖示角標,且不同廠商之間,方案還不盡相同;但是此次我還是要看Android源生代碼究竟是怎么顯示應用有新的未讀訊息,此篇文章是以為記,

一
很直觀,它是顯示在應用的右上角的,那么我們要查看它,得先在代碼中找到單個應用顯示的類,根據上篇文章介紹,應用是在CellLayout平均分配的矩形中顯示,可以先查看CellLayout的原始碼,在CellLayout的原始碼中,找到了下面的方法:
public boolean addViewToCellLayout(View child, int index, int childId, LayoutParams params,
boolean markCells) {
final LayoutParams lp = params;
// Hotseat icons - remove text
if (child instanceof BubbleTextView) {
BubbleTextView bubbleChild = (BubbleTextView) child;
bubbleChild.setTextVisibility(mContainerType != HOTSEAT);
}
child.setScaleX(mChildScale);
child.setScaleY(mChildScale);
// Generate an id for each view, this assumes we have at most 256x256 cells
// per workspace screen
if (lp.cellX >= 0 && lp.cellX <= mCountX - 1 && lp.cellY >= 0 && lp.cellY <= mCountY - 1) {
// If the horizontal or vertical span is set to -1, it is taken to
// mean that it spans the extent of the CellLayout
if (lp.cellHSpan < 0) lp.cellHSpan = mCountX;
if (lp.cellVSpan < 0) lp.cellVSpan = mCountY;
child.setId(childId);
if (LOGD) {
Log.d(TAG, "Adding view to ShortcutsAndWidgetsContainer: " + child);
}
mShortcutsAndWidgets.addView(child, index, lp);
if (markCells) markCellsAsOccupiedForView(child);
return true;
}
return false;
}
該方法是將子View添加到CellLayout中;而我們知道CellLayout中要么顯示應用的快捷方式,要不顯示桌面小部件,如果是Hotseat里么的CellLayout,那么它就只能顯示應用的快捷方式,且不顯示它的名稱,從代碼得知應用的快捷方式的類就是BubbleTextView,
// Hotseat icons - remove text
if (child instanceof BubbleTextView) {
BubbleTextView bubbleChild = (BubbleTextView) child;
bubbleChild.setTextVisibility(mContainerType != HOTSEAT);
}
在BubbleTextView原始碼中,確實有看到在該View的右上角畫標記,但是沒有發現有設定未讀訊息的代碼,經過修改代碼運行得知,此標記僅表示有新訊息,但是沒有訊息的條數,具體修改代碼如下:
protected void drawBadgeIfNecessary(Canvas canvas) {
//設定mBadgeScale以滿足條件,畫出右上角標記資訊
mBadgeScale = 1.0f;
if (!mForceHideBadge && (hasBadge() || mBadgeScale > 0)) {
getIconBounds(mTempIconBounds);
mTempSpaceForBadgeOffset.set((getWidth() - mIconSize) / 2, getPaddingTop());
final int scrollX = getScrollX();
final int scrollY = getScrollY();
canvas.translate(scrollX, scrollY);
mBadgeRenderer.draw(canvas, mBadgeColor, mTempIconBounds, mBadgeScale,
mTempSpaceForBadgeOffset);
canvas.translate(-scrollX, -scrollY);
}
}
運行結果如下:
所以得出結論,原生圖示角標與國內手機的圖示角標有出入,
二
接下來要學習一下國內手機的圖示角標都是怎么實作的,
小米
本人有部紅米手機,所以就以小米手機為例,進行學習,通過查找,在小米開發平臺找到相關資料;小米桌面角標
- 默認邏輯
當應用向通知欄發送了一條通知 (除了進度條樣式和常駐通知外),應用圖示的右上角就會顯示「1」,角標的數字代表應用的通知數,即應用發送了「x」條通知,角標就會顯示為「x」,
- 開發者如何設定桌面角標
2.1 MIUI6-MIUI11桌面應用角標適配方法
可通過反射呼叫設定桌面角標,參考代碼如下:
try {
Field field = notification.getClass().getDeclaredField(“extraNotification”);
Object extraNotification = field.get(notification);
Method method = extraNotification.getClass().getDeclaredMethod(“setMessageCount”, int.class);
method.invoke(extraNotification, mCount);
} catch (Exception e) {
e.printStackTrace();
}
2.2 MIUI12及以后桌面應用角標適配方法
由于Google屏蔽了hideAPI的反射呼叫,因此MIUI12及以后可以使用notification.number,可參照Android開發者檔案https://developer.android.google.cn/reference/android/app/Notification#number,參考代碼如下:
Notification notification = new Notification.Builder(context, CHANNEL_ID)
.setSmallIcon(R.drawable.notification_icon)
.setContentTitle(textTitle)
.setContentText(textContent)
.setNumber(int number)
.build();
- 如何判斷MIUI版本
可參考檔案https://dev.mi.com/console/doc/detail?pId=1312,其中6.1節有具體方法說明,
由于本人的手機版本是MIUI12.5.6,所以只能參考上述檔案中2.2的適配方法,寫了如下測驗代碼:
private void createShortcutBadger() {
//獲取通知管理類
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
//通知在Android8.0之后需要創建通道,才能彈出來
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel("1", "name", NotificationManager.IMPORTANCE_HIGH);
mNotificationManager.createNotificationChannel(channel);
}
Notification notification = new Notification.Builder(this, "1")
.setSmallIcon(R.drawable.ic_launcher_background)
.setContentTitle("通知標題")
.setContentText("通知內容")
.setNumber(1)
.build();
//告訴系統要出現哪個通知
mNotificationManager.notify(0, notification);
}
通知可以創建成功,可應用的角標卻沒有,在網上查閱資料后,得知原因是,當我們創建通知時,已經在應用內部了,所以創建的通知,應用認為是已讀狀態,所以并不會在應用圖示的右上角顯示未讀訊息個數,所以對代碼做了改動如下:
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
createShortcutBadger();
}
},3000);
經過測驗,發現此時圖示的角標還是未能顯示,后想是否是因為手機里的某些權限未開啟,經過查找,找到了設定->通知與控制中心->桌面角標,發現自己寫的應用的角標權限確實未開啟,開啟之后,再次驗證,角標正常顯示,
但是此代碼只適用于MIUI12及以后桌面的圖示角標顯示,為了能夠適配所有小米手機的角標顯示,根據檔案對原始碼做了如下改動;
private void createShortcutBadger() {
String code = PropUtils.get("ro.miui.ui.version.code", "7");
Integer intCode = Integer.valueOf(code);
/**
* 當前手機code為11,但是不清楚MIUI12的code是多少,所以就以11為臨界點,
*/
if (intCode >= 11){
createHighShortcutBadger();
} else {
createLowShortcutBadger();
}
}
private void createLowShortcutBadger() {
//獲取通知管理類
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
//通知在Android8.0之后需要創建通道,才能彈出來
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel("1", "name", NotificationManager.IMPORTANCE_HIGH);
mNotificationManager.createNotificationChannel(channel);
}
Notification notification = new Notification.Builder(this, "1")
.setSmallIcon(R.drawable.ic_launcher_background)
.setContentTitle("通知標題")
.setContentText("通知內容")
.build();
try {
Field field = notification.getClass().getDeclaredField("extraNotification");
Object extraNotification = field.get(notification);
Method method = extraNotification.getClass().getDeclaredMethod("setMessageCount", int.class);
method.invoke(extraNotification, 1);
} catch (Exception e) {
e.printStackTrace();
}
//告訴系統要出現哪個通知
mNotificationManager.notify(0, notification);
}
private void createHighShortcutBadger() {
//獲取通知管理類
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
//通知在Android8.0之后需要創建通道,才能彈出來
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel("1", "name", NotificationManager.IMPORTANCE_HIGH);
mNotificationManager.createNotificationChannel(channel);
}
Notification notification = new Notification.Builder(this, "1")
.setSmallIcon(R.drawable.ic_launcher_background)
.setContentTitle("通知標題")
.setContentText("通知內容")
.setNumber(1)
.build();
//告訴系統要出現哪個通知
mNotificationManager.notify(0, notification);
}
package com.yangshuangyue.myshortcutbadger;
import java.lang.reflect.Method;
class PropUtils {
/**
* 設定屬性值
*
* @param key 長度不能超過31,key.length <= 30
* @param value 長度不能超過91,value.length<=90
*/
public static void set(String key, String value) {
// android.os.SystemProperties
// public static void set(String key, String val)
try {
Class<?> cls = Class.forName("android.os.SystemProperties");
Method method = cls.getMethod("set", String.class, String.class);
method.invoke(null, key, value);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 獲取屬性值
*
* @param key 長度不能超過31,key.length <= 30
* @param defValue
* @return
*/
public static String get(String key, String defValue) {
// android.os.SystemProperties
// public static String get(String key, String def)
try {
Class<?> cls = Class.forName("android.os.SystemProperties");
Method method = cls.getMethod("get", String.class, String.class);
return (String) method.invoke(null, key, defValue);
} catch (Exception e) {
e.printStackTrace();
}
return defValue;
}
}
沒有MIUI12以下的手機,未能驗證,
參考資料:
Android開發:史上最全Android應用角標適配方法
MIUI6&7桌面角標開源代碼簡介
桌面應用角標適配說明
關于小米 角標不顯示問題
使用SystemProperties的幾種方式
華為
1、宣告權限
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="com.huawei.android.launcher.permission.CHANGE_BADGE"/>
2、在需要進行角標顯示地方,采用如下方法傳遞資料給華為桌面應用,
Bundle extra = new Bundle();
extra.putString("package", "xxxxxx");
extra.putString("class", "yyyyyyy");
extra.putInt("badgenumber", i);
context.getContentResolver().call(Uri.parse("content://com.huawei.android.launcher.settings/badge/"), "change_badge", null, extra);
關鍵引數說明:
- package:應用包名
- class:桌面圖示對應的應用入口Activity類
- badgenumber:角標數字
示例
boolean mIsSupportedBade = true;
if (mIsSupportedBade) {
setBadgeNum(num);
}
/** set badge number*/
public void setBadgeNum(int num) {
try {
Bundle bunlde = new Bundle();
bunlde.putString("package", "com.test.badge"); // com.test.badge is your package name
bunlde.putString("class", "com.test. badge.MainActivity"); // com.test. badge.MainActivity is your apk main activity
bunlde.putInt("badgenumber", num);
this.getContentResolver().call(Uri.parse("content://com.huawei.android.launcher.settings/badge/"), "change_badge", null, bunlde);
} catch (Exception e) {
mIsSupportedBade = false;
}
}
參考資料
Android開發:史上最全Android應用角標適配方法
華為桌面角標開發指導書
三星
可以通過廣播機制直接設定應用角標,且應用在前臺和被殺掉后仍可顯示
private static boolean setSamsungBadge(int count, Context context) {
try {
String launcherClassName = getLauncherClassName(context);
if (TextUtils.isEmpty(launcherClassName)) {
return false;
}
Intent intent = new Intent("android.intent.action.BADGE_COUNT_UPDATE");
intent.putExtra("badge_count", count);
intent.putExtra("badge_count_package_name", context.getPackageName());
intent.putExtra("badge_count_class_name", launcherClassName);
context.sendBroadcast(intent);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
參考資料
Android開發:史上最全Android應用角標適配方法
聯想
private static boolean setZukBadge(int count, Context context) {
try {
Bundle extra = new Bundle();
ArrayList<String> ids = new ArrayList<>();
// 以串列形式傳遞快捷方式id,可以添加多個快捷方式id
// ids.add("custom_id_1");
// ids.add("custom_id_2");
extra.putStringArrayList("app_shortcut_custom_id", ids);
extra.putInt("app_badge_count", count);
Uri contentUri = Uri.parse("content://com.android.badge/badge");
Bundle bundle = context.getContentResolver().call(contentUri, "setAppBadgeCount", null,
extra);
return bundle != null;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
參考資料
Android開發:史上最全Android應用角標適配方法
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/413484.html
標籤:其他
