Android探索之旅 | 為應(yīng)用添加角標(biāo)(Badge)

-- 作者 謝恩銘 轉(zhuǎn)載請(qǐng)注明出處

內(nèi)容簡(jiǎn)介


  1. 需求簡(jiǎn)介
  2. Android角標(biāo)起源
  3. 不錯(cuò)的Github項(xiàng)目
  4. 清除角標(biāo)
  5. 小問題糾錯(cuò)
  6. 總結(jié)

1.需求簡(jiǎn)介


角標(biāo)是什么意思呢?

看下圖即可明了:

可以看到圖中的樂購(gòu)這個(gè)app右上角的紅色的圓圈,里面有10這個(gè)數(shù)字的涛菠,就是一種角標(biāo)赐稽。

角標(biāo)瞧哟,英語(yǔ)是badge并闲,也就是“徽章倚舀,像章叹哭,獎(jiǎng)?wù)拢?象征,標(biāo)記”的意思痕貌。

一般來說风罩,應(yīng)用的角標(biāo)是用來標(biāo)記有多少條提醒(Notification)沒讀(unread),一旦點(diǎn)擊提示進(jìn)應(yīng)用閱讀了舵稠,角標(biāo)也會(huì)消失超升。

2. Android角標(biāo)起源


角標(biāo)原本是蘋果的iOS中的東西入宦,Android原生并不支持角標(biāo),因?yàn)镚oogle的意思是讓大家用Notification(提示欄)即可室琢,角標(biāo)實(shí)在大有讓處女座“跳崖”的風(fēng)險(xiǎn)乾闰。幸好我不是...

最近公司的項(xiàng)目中,客戶的一個(gè)新需求是在我們的加密信息應(yīng)用上加上角標(biāo)功能盈滴,因?yàn)槲覀兊暮献骰锇槭侨牵梢詤⒖次业倪@篇文章:程序員在法國(guó) | 我被法國(guó)國(guó)防部盯上了!)涯肩,因此我就去網(wǎng)上找相關(guān)資料。

找的時(shí)候巢钓,才知道上面所說的Android原生不支持角標(biāo)一事病苗。不過無妨,厲害的Android第三方廠商可以通過在自定義的Launcher(啟動(dòng)器)中操作來實(shí)現(xiàn)添加角標(biāo)症汹。

我在第一時(shí)間當(dāng)然是去找三星的移動(dòng)設(shè)備如何添加角標(biāo)硫朦,不過卻有幸找到了Github上的比較普適的項(xiàng)目。

把我導(dǎo)向Github的自然是Stack Overflow背镇,而把我導(dǎo)向Stack Overflow的就是Google咬展,因此我會(huì)說:為什么程序員一定要會(huì)用Google和Stack Overflow? 芽世。

3. 不錯(cuò)的Github項(xiàng)目


  • 一般來說,現(xiàn)在被引用最多的Android添加和去除角標(biāo)的Github項(xiàng)目是這位中國(guó)人寫的:https://github.com/leolin310148/ShortcutBadger

    這個(gè)項(xiàng)目挺不錯(cuò)诡壁,雖然更新不是特別勤快济瓢,但最近一次更新是在2016年10月31日,也就是兩個(gè)月前妹卿,還可以接受旺矾。

  • 《Android群英傳》和《Android群英傳:神兵利器》的作者 徐宜生 也在自己的Github上建了一個(gè)項(xiàng)目:https://github.com/xuyisheng/ShortcutHelper ,挺有意思夺克,里面還有號(hào)稱“瘋狂模式”的為所有在手機(jī)桌面上的應(yīng)用加上99的角標(biāo)數(shù)的功能箕宙,當(dāng)然了,去除的代碼也有铺纽,不然處女座豈不是要暈了~

添加角標(biāo)的原理就是發(fā)送一個(gè)Broadcast(廣播)柬帕,在廣播的Intent中指定需要被添加角標(biāo)的應(yīng)用的packageName(包名),className(類名)狡门,count(角標(biāo)數(shù)目)陷寝。當(dāng)然了,不同廠商的手機(jī)的角標(biāo)操作的Intent的action是不一樣的其馏。

因此凤跑,我們?nèi)绻o自己的手機(jī)里的應(yīng)用添加角標(biāo),只需要簡(jiǎn)單的利用上面兩個(gè)項(xiàng)目中的代碼即可叛复,一般不需要把全部項(xiàng)目搬過來仔引。當(dāng)然了扔仓,如果你要適配所有手機(jī),那么可以全盤引用項(xiàng)目咖耘。

比如我要給三星的手機(jī)的應(yīng)用添加角標(biāo)翘簇,那么我只需要做以下的幾步即可:

  • 在AndroidManifest.xml中添加讀取和寫入角標(biāo)的權(quán)限:
<uses-permission android:name="com.sec.android.provider.badge.permission.READ" />
<uses-permission android:name="com.sec.android.provider.badge.permission.WRITE" />
  • 自己寫一個(gè)類,隨便取名字鲤看,比如叫做 BadgeUtils缘揪,在類中添加如下內(nèi)容:
public class BadgeUtils {
  private static final String INTENT_ACTION = "android.intent.action.BADGE_COUNT_UPDATE";
  private static final String INTENT_EXTRA_BADGE_COUNT = "badge_count";
  private static final String INTENT_EXTRA_PACKAGENAME = "badge_count_package_name";
  private static final String INTENT_EXTRA_ACTIVITY_NAME = "badge_count_class_name";

  public static void setBadgeCount(Context context, ComponentName componentName, int badgeCount) {
    Intent intent = new Intent(INTENT_ACTION);     
    intent.putExtra(INTENT_EXTRA_BADGE_COUNT, badgeCount);    
    intent.putExtra(INTENT_EXTRA_PACKAGENAME, componentName.getPackageName());    
    intent.putExtra(INTENT_EXTRA_ACTIVITY_NAME, componentName.getClassName());
    context.sendBroadcast(intent);  
  }
}
  • 使用上面的代碼時(shí),只需要傳入三個(gè)參數(shù)义桂,也就是:

    1. Context : 應(yīng)用的Context找筝。簡(jiǎn)單。
    2. ComponentName :組件名慷吊,略有點(diǎn)麻煩袖裕。可以這樣來獲雀绕俊(applicationContext就是應(yīng)用的Context) :

applicationContext.getPackageManager()
.getLaunchIntentForPackage(applicationContext.getPackageName())
.getComponent()
```

  1. badgeCount :角標(biāo)的數(shù)目急鳄,例如10。簡(jiǎn)單堰酿。

當(dāng)然了疾宏,如果你不想要傳入三個(gè)參數(shù)這么麻煩,你也可以再寫一個(gè)方法getLauncherClassName触创,就只需要傳入兩個(gè)參數(shù)即可坎藐。BadgeUtils中的代碼變?yōu)椋?/p>

public class BadgeUtils {
  private static final String INTENT_ACTION = "android.intent.action.BADGE_COUNT_UPDATE";
  private static final String INTENT_EXTRA_BADGE_COUNT = "badge_count";
  private static final String INTENT_EXTRA_PACKAGENAME = "badge_count_package_name";
  private static final String INTENT_EXTRA_ACTIVITY_NAME = "badge_count_class_name";

  public static void setBadgeCount(Context context, int badgeCount) {
    String launcherClassName = getLauncherClassName(context);
    if (launcherClassName == null) {
      return;
    }

    Intent intent = new Intent(INTENT_ACTION);     
    intent.putExtra(INTENT_EXTRA_BADGE_COUNT, badgeCount);    
    intent.putExtra(INTENT_EXTRA_PACKAGENAME, context.getPackageName());    
    intent.putExtra(INTENT_EXTRA_ACTIVITY_NAME, launcherClassName);

    context.sendBroadcast(intent);  
  }

  private static String getLauncherClassName(Context context) {
    PackageManager pm = context.getPackageManager();

    Intent intent = new Intent(Intent.ACTION_MAIN); 
    intent.addCategory(Intent.CATEGORY_LAUNCHER);

    List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent, 0);
    for (ResolveInfo resolveInfo : resolveInfos) {
      String pkgName = resolveInfo.activityInfo.applicationInfo.packageName;
      if (pkgName.equalsIgnoreCase(context.getPackageName())) {
        String className = resolveInfo.activityInfo.name;
        return className;
      }
    }
    return null; 
  }
}

使用時(shí)傳入兩個(gè)參數(shù)即可:

  1. Context : 應(yīng)用的Context。
  2. badgeCount :角標(biāo)的數(shù)目哼绑,例如10岩馍。

4. 清除角標(biāo)


要清除應(yīng)用的角標(biāo)就很簡(jiǎn)單了,給badgeCount傳入0即可抖韩。

BadgeUtils.setBadgeCount(context,       
context.getPackageManager()               
.getLaunchIntentForPackage(context.getPackageName())                
.getComponent(),        
0);

或者

BadgeUtils.setBadgeCount(context, 0);

5. 小問題糾錯(cuò)


上面的 https://github.com/leolin310148/ShortcutBadger 這個(gè)項(xiàng)目中蛀恩,基本已經(jīng)包含了大多數(shù)可以定制角標(biāo)的Android生產(chǎn)廠商的添加角標(biāo)的代碼實(shí)現(xiàn),不過它也提到:

三星和LG(這兩個(gè)難兄難弟)的代碼有很多類似茂浮,連角標(biāo)處理的廣播的Intent中的action也是一樣的双谆,都是:

"android.intent.action.BADGE_COUNT_UPDATE"

但是作者在三星和LG的兩個(gè)角標(biāo)操作實(shí)現(xiàn)類中寫了注釋:

// Deprecated, Samsung devices will use DefaultBadger

// Deprecated, LG devices will use DefaultBadger

意思是“三星和LG的實(shí)現(xiàn)代碼已經(jīng)Deprecated(失效了),請(qǐng)用DefaultBadger類”席揽。

因此佃乘,這兩個(gè)需要用 https://github.com/leolin310148/ShortcutBadger/blob/master/ShortcutBadger/src/main/java/me/leolin/shortcutbadger/impl/DefaultBadger.java 中的實(shí)現(xiàn):

private static final String INTENT_ACTION = "android.intent.action.BADGE_COUNT_UPDATE";
private static final String INTENT_EXTRA_BADGE_COUNT = "badge_count";
private static final String INTENT_EXTRA_PACKAGENAME = "badge_count_package_name";
private static final String INTENT_EXTRA_ACTIVITY_NAME = "badge_count_class_name";

@Override
public void executeBadge(Context context, ComponentName componentName, int badgeCount) throws ShortcutBadgeException {
  Intent intent = new Intent(INTENT_ACTION);
  intent.putExtra(INTENT_EXTRA_BADGE_COUNT, badgeCount);
  intent.putExtra(INTENT_EXTRA_PACKAGENAME, componentName.getPackageName());
  intent.putExtra(INTENT_EXTRA_ACTIVITY_NAME, componentName.getClassName());

  if (BroadcastHelper.canResolveBroadcast(context, intent)) {
    context.sendBroadcast(intent);
  } else {
    throw new ShortcutBadgeException("unable to resolve intent: " + intent.toString());
  }
}

不過上面的代碼有一個(gè)小問題,就是那句

 if (BroadcastHelper.canResolveBroadcast(context, intent)) {

在有些設(shè)備(比如Samsung Galaxy S5)上會(huì)拋出異常(Exception)驹尼,找不到處理"android.intent.action.BADGE_COUNT_UPDATE"這個(gè)Intent的BroadcastReceiver趣避,很奇怪。

但有些設(shè)備(比如Samsung Galaxy A5)上又運(yùn)行正常新翎,沒有拋出異常程帕。

解決辦法是去除這一個(gè)檢測(cè)住练,把

if (BroadcastHelper.canResolveBroadcast(context, intent)) {
  context.sendBroadcast(intent);
} else {
  throw new ShortcutBadgeException("unable to resolve intent: " + intent.toString());
}

替換為簡(jiǎn)單的

context.sendBroadcast(intent);

就可以了。

也就是我上面自己實(shí)作時(shí)的代碼愁拭。

6. 總結(jié)


  1. Android的角標(biāo)添加和移除畢竟是基于各大手機(jī)廠商的Launcher的定制讲逛,因此不是正統(tǒng)的Android技巧,隨著廠商的Launcher的改變岭埠,也許你的代碼未來就不一定有用了盏混,因此需要不斷修改,“推陳出新”惜论。

  2. 不過正所謂“生命在于折騰”许赃,而這也是我們喜歡Android系統(tǒng)的原因。這個(gè)萌萌的機(jī)器人可以經(jīng)得起我們隨意折騰馆类,在嵌入式領(lǐng)域的應(yīng)用前途也是很不錯(cuò)的混聊。

  3. 大家在平時(shí)學(xué)習(xí)編程的時(shí)候,也可以把自己的代碼或經(jīng)驗(yàn)匯總到Github項(xiàng)目乾巧,一來惠己利人句喜,二來提高自己的業(yè)界知名度。


我是謝恩銘咳胃,在巴黎奮斗的軟件工程師。
熱愛生活旷太,喜歡游泳展懈,略懂烹飪。
人生格言:「向著標(biāo)桿直跑」

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末泳秀,一起剝皮案震驚了整個(gè)濱河市标沪,隨后出現(xiàn)的幾起案子榄攀,更是在濱河造成了極大的恐慌嗜傅,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,602評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件檩赢,死亡現(xiàn)場(chǎng)離奇詭異吕嘀,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)贞瞒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門偶房,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人军浆,你說我怎么就攤上這事棕洋。” “怎么了乒融?”我有些...
    開封第一講書人閱讀 152,878評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵掰盘,是天一觀的道長(zhǎng)摄悯。 經(jīng)常有香客問我,道長(zhǎng)愧捕,這世上最難降的妖魔是什么奢驯? 我笑而不...
    開封第一講書人閱讀 55,306評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮次绘,結(jié)果婚禮上瘪阁,老公的妹妹穿的比我還像新娘。我一直安慰自己邮偎,他們只是感情好管跺,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,330評(píng)論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著钢猛,像睡著了一般伙菜。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上命迈,一...
    開封第一講書人閱讀 49,071評(píng)論 1 285
  • 那天贩绕,我揣著相機(jī)與錄音,去河邊找鬼壶愤。 笑死淑倾,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的征椒。 我是一名探鬼主播娇哆,決...
    沈念sama閱讀 38,382評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼勃救!你這毒婦竟也來了碍讨?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,006評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤蒙秒,失蹤者是張志新(化名)和其女友劉穎勃黍,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體晕讲,經(jīng)...
    沈念sama閱讀 43,512評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡覆获,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,965評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了瓢省。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片弄息。...
    茶點(diǎn)故事閱讀 38,094評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖勤婚,靈堂內(nèi)的尸體忽然破棺而出摹量,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 33,732評(píng)論 4 323
  • 正文 年R本政府宣布缨称,位于F島的核電站废亭,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏具钥。R本人自食惡果不足惜豆村,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,283評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望骂删。 院中可真熱鬧掌动,春花似錦、人聲如沸宁玫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)欧瘪。三九已至眷射,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間佛掖,已是汗流浹背妖碉。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留芥被,地道東北人欧宜。 一個(gè)月前我還...
    沈念sama閱讀 45,536評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像拴魄,于是被迫代替她去往敵國(guó)和親冗茸。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,828評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容