• Android 类似未读短信图标显示数字效果的分析


    之前一直以为是应用本身在对图标进行修改,看了源码之后发现其实主要的工作并不是应用自己完成的,主要的工作在是launcher里面完成的.

    关于系统里面类似未读短信的具体处理流程如下,
    原理
    一个应用要实现这个效果,就要在自己有未读的消息的时候发送一个广播告诉系统我有未处理的事件了(例如:短信,电话和邮件等),同时将相关的信息进行保存,比如应用的名称(这里指的是ComponentName)和未处理事件的数量.系统将提升用户有待处理的事件交给Launcher去处理,launcher会接收到对应的广播,不是随便一个应用都有这个待遇的,launcher接到广播之后会先解析数据,看当前的应用有没有这个待遇也就是是否支持显示这个待处理事件的通知.如果通过launcher就会调用相关方法去重绘应用的icon.
    效果图: 实现过程分析
    Intent.java(系统增加3个常量)
    frameworks/base/core/java/android/content/Intent.java
    Java代码  收藏代码
    1. //状态    
    2. public static final String MTK_ACTION_UNREAD_CHANGED = "com.mediatek.action.UNREAD_CHANGED"; 
    3. //应用名称     
    4. public static final String MTK_EXTRA_UNREAD_COMPONENT = "com.mediatek.intent.extra.UNREAD_COMPONENT"; 
    5. //数量 
    6. public static final String MTK_EXTRA_UNREAD_NUMBER = "com.mediatek.intent.extra.UNREAD_NUMBER"; 
    7.   
    系统里面有这个功能几个应用相关的代码路径
    日历 packages/apps/Calendar/src/com/mediatek/calendar/MTKUtils.java
    邮件 :packages/apps/Email/src/com/android/email/NotificationController.java
    电话 :packages/providers/ContactsProvider/src/com/android/providers/contacts/CallLogProvider.java 
    短信 :packages/providers/TelephonyProvider/src/com/android/providers/telephony/MmsSmsProvider.java
     
    这里以电话为例:
    假设当前有人打电话进来,系统会保存改记录到数据库,根据事件是否已经处理来判断是否发送广播
     
    CallLogProvider.java(发送有待处理事件的广播)
    packages/providers/ContactsProvider/src/com/android/providers/contacts/CallLogProvider.java
    Java代码  收藏代码
    1. /** M: send new Calls broadcast to luancher to update unread icon @{ */ 
    2.     public static final void notifyNewCallsCount(SQLiteDatabase db, Context context) { 
    3.         Cursor c = null; 
    4.        ….. 
    5.         //send count=0 to clear the unread icon 
    6.         if (newCallsCount >= 0) { //有新的来电数量 
    7.             Intent newIntent = new Intent(Intent.MTK_ACTION_UNREAD_CHANGED); 
    8.             newIntent.putExtra(Intent.MTK_EXTRA_UNREAD_NUMBER, newCallsCount); 
    9.             newIntent.putExtra(Intent.MTK_EXTRA_UNREAD_COMPONENT, new ComponentName(Constants.CONTACTS_PACKAGE, 
    10.                     Constants.CONTACTS_DIALTACTS_ACTIVITY)); 
    11.             context.sendBroadcast(newIntent); //发送对应的广播 
    12.             android.provider.Settings.System.putInt(context.getContentResolver(), Constants.CONTACTS_UNREAD_KEY, Integer 
    13.                     .valueOf(newCallsCount)); 
    14.         } 
    15.     } 
    MTKUnreadLoader.java(Launcher接收到应用发送的广播,进行判断改应用是否可以显示有未处理事件的图标)
    packages/apps/Launcher2/src/com/android/launcher2/MTKUnreadLoader.java
    Java代码  收藏代码
    1. public void onReceive(final Context context, final Intent intent) { 
    2.         final String action = intent.getAction(); 
    3.             //过滤广播 
    4.         if (Intent.MTK_ACTION_UNREAD_CHANGED.equals(action)) { 
    5.             final ComponentName componentName = (ComponentName) intent.getExtra(Intent.MTK_EXTRA_UNREAD_COMPONENT); 
    6.             final int unreadNum = intent.getIntExtra(Intent.MTK_EXTRA_UNREAD_NUMBER, -1); 
    7.  
    8.             if (mCallbacks != null && componentName != null && unreadNum != -1) { 
    9.                 //判断是否支持该功能 
    10.                 final int index = supportUnreadFeature(componentName); 
    11.                 if (index >= 0) { //支持 
    12.                     boolean ret = setUnreadNumberAt(index, unreadNum); 
    13.                     if (ret) { 
    14.                         final UnreadCallbacks callbacks = mCallbacks.get(); 
    15.                         if (callbacks != null) { 
    16.                             callbacks.bindComponentUnreadChanged(componentName, unreadNum); 
    17.                         } 
    18.                     ......... 
    19.     } 
    LauncherApplication.java(Launcher注册对应的广播接收器)
    packages/apps/Launcher2/src/com/android/launcher2/LauncherApplication.java
    Java代码  收藏代码
    1. public void onCreate() { 
    2.        ........ 
    3.         /// M: register unread broadcast. 
    4.         if (FeatureOption.MTK_LAUNCHER_UNREAD_SUPPORT) { 
    5.             mUnreadLoader = new MTKUnreadLoader(getApplicationContext()); 
    6.             // Register unread change broadcast. 
    7.             filter = new IntentFilter(); 
    8.             filter.addAction(Intent.MTK_ACTION_UNREAD_CHANGED); 
    9.             registerReceiver(mUnreadLoader, filter); //注册对应的广播接收器 
    10.         } 
    11.        .............. 
    12.     } 
    MTKUnreadLoader.java(处理应用的图标显示未处理事件的数字)
    packages/apps/Launcher2/src/com/android/launcher2/MTKUnreadLoader.java
    Java代码  收藏代码
    1. static void drawUnreadEventIfNeed(Canvas canvas, View icon) { 
    2.         ItemInfo info = (ItemInfo)icon.getTag(); 
    3.         if (info != null && info.unreadNum > 0) { //判断未处理事件数量 
    4.             Resources res = icon.getContext().getResources(); 
    5.            .......... 
    6.             if (info.unreadNum > Launcher.MAX_UNREAD_COUNT) { 
    7.                 unreadTextNumber = String.valueOf(Launcher.MAX_UNREAD_COUNT); 
    8.                 unreadTextPlusPaint.getTextBounds(unreadTextPlus, 0, unreadTextPlus.length(), unreadTextPlusBounds); 
    9.             } else { 
    10.                 unreadTextNumber = String.valueOf(info.unreadNum); 
    11.             } 
    12.             unreadTextNumberPaint.getTextBounds(unreadTextNumber, 0, unreadTextNumber.length(), unreadTextNumberBounds); 
    13.             int textHeight = unreadTextNumberBounds.height(); 
    14.             int textWidth = unreadTextNumberBounds.width() + unreadTextPlusBounds.width(); 
    15.  
    16.             // 数字的背景图 
    17.             NinePatchDrawable unreadBgNinePatchDrawable = (NinePatchDrawable)res.getDrawable(R.drawable.ic_newevents_numberindication); 
    18.            ......... 
    19.             Rect unreadBgBounds = new Rect(0, 0, unreadBgWidth, unreadBgHeight); 
    20.             unreadBgNinePatchDrawable.setBounds(unreadBgBounds); 
    21.  
    22.             int unreadMarginTop = 0; 
    23.             int unreadMarginRight = 0; 
    24.             if (info instanceof ShortcutInfo) { //workspace 里面的快捷方式 
    25.                 if (info.container == (long)LauncherSettings.Favorites.CONTAINER_HOTSEAT) { 
    26.                     unreadMarginTop = (int)res.getDimension(R.dimen.hotseat_unread_margin_top); 
    27.                     unreadMarginRight = (int)res.getDimension(R.dimen.hotseat_unread_margin_right); 
    28.                 } else if (info.container == (long)LauncherSettings.Favorites.CONTAINER_DESKTOP) { 
    29.                     unreadMarginTop = (int)res.getDimension(R.dimen.workspace_unread_margin_top); 
    30.                     unreadMarginRight = (int)res.getDimension(R.dimen.workspace_unread_margin_right); 
    31.                 } else { 
    32.                     unreadMarginTop = (int)res.getDimension(R.dimen.folder_unread_margin_top); 
    33.                     unreadMarginRight = (int)res.getDimension(R.dimen.folder_unread_margin_right); 
    34.                 } 
    35.             } else if (info instanceof FolderInfo) { //文件夹 
    36.                 if (info.container == (long)LauncherSettings.Favorites.CONTAINER_HOTSEAT) { 
    37.                     unreadMarginTop = (int)res.getDimension(R.dimen.hotseat_unread_margin_top); 
    38.                     unreadMarginRight = (int)res.getDimension(R.dimen.hotseat_unread_margin_right); 
    39.                 } else if (info.container == (long)LauncherSettings.Favorites.CONTAINER_DESKTOP) { 
    40.                     unreadMarginTop = (int)res.getDimension(R.dimen.workspace_unread_margin_top); 
    41.                     unreadMarginRight = (int)res.getDimension(R.dimen.workspace_unread_margin_right); 
    42.                 } 
    43.             } 
    44.             else if (info instanceof ApplicationInfo) { //all app 里面的应用icon 
    45.                 unreadMarginTop = (int)res.getDimension(R.dimen.app_list_unread_margin_top); 
    46.                 unreadMarginRight = (int)res.getDimension(R.dimen.app_list_unread_margin_right); 
    47.             } 
    48.  
    49.             int unreadBgPosX = icon.getScrollX() + icon.getWidth() - unreadBgWidth - unreadMarginRight; 
    50.             int unreadBgPosY = icon.getScrollY() + unreadMarginTop; 
    51.  
    52.             canvas.save(); 
    53.             canvas.translate(unreadBgPosX, unreadBgPosY); 
    54.  
    55.             unreadBgNinePatchDrawable.draw(canvas); 
    56.  
    57.             /// M: Draw unread text. 
    58.             Paint.FontMetrics fontMetrics = unreadTextNumberPaint.getFontMetrics(); 
    59.             if (info.unreadNum > Launcher.MAX_UNREAD_COUNT) { 
    60.                 canvas.drawText(unreadTextNumber, 
    61.                                 (unreadBgWidth - unreadTextPlusBounds.width()) / 2, 
    62.                                 (unreadBgHeight + textHeight) / 2, 
    63.                                 unreadTextNumberPaint); 
    64.                 canvas.drawText(unreadTextPlus, 
    65.                                 (unreadBgWidth + unreadTextNumberBounds.width()) / 2, 
    66.                                 (unreadBgHeight + textHeight) / 2 + fontMetrics.ascent / 2, 
    67.                                 unreadTextPlusPaint); 
    68.             } else { 
    69.                ..... 
    70.         } 
    71.     } 
    unread_support_shortcuts.xml(配置哪些应用可以显示待处理的事件)
    packages/apps/Launcher2/res/xml/unread_support_shortcuts.xml
    Xml代码  收藏代码
    1. <unreadshortcuts xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
    2. <!--电话--> 
    3.     <shortcut 
    4.         launcher:unreadPackageName="com.android.contacts" 
    5.         launcher:unreadClassName="com.android.contacts.activities.DialtactsActivity" 
    6.         launcher:unreadType="0" 
    7.         launcher:unreadKey="com_android_contacts_mtk_unread" 
    8.      /> 
    9. <!--短信--> 
    10.      <shortcut 
    11.         launcher:unreadPackageName="com.android.mms" 
    12.         launcher:unreadClassName="com.android.mms.ui.BootActivity" 
    13.         launcher:unreadType="0" 
    14.         launcher:unreadKey="com_android_mms_mtk_unread" 
    15.      /> 
    16. <!--邮件--> 
    17.      <shortcut 
    18.         launcher:unreadPackageName="com.android.email" 
    19.         launcher:unreadClassName="com.android.email.activity.Welcome" 
    20.         launcher:unreadType="0" 
    21.         launcher:unreadKey="com_android_email_mtk_unread" 
    22.      /> 
    23.     ................ 
    24. </unreadshortcuts
  • 相关阅读:
    很简单的企业管理器我写程序的方式,几个自定义控件。
    当OO遇到了持久化?!
    [自定义服务器控件] 第一步:文本框。
    [面向过程——老酒换新瓶] (一)开篇:是面向过程还是面向对象?
    个人理财小助手 —— 设计思路、功能说明
    《Head First 设计模式》 终于出中文版了。
    其实添加数据也可以这样简单——表单的第一步抽象(针对数据访问层)《怪怪设计论: 抽象无处不在 》有感
    基类、接口的应用——表单控件:一次添加、修改一条记录,一次修改多条记录。(上)
    其实添加数据也可以这样简单——表单的第三步抽象(针对UI及后置代码)
    转帖:客户端表单通用验证checkForm(oForm) js版
  • 原文地址:https://www.cnblogs.com/Free-Thinker/p/4469108.html
Copyright © 2020-2023  润新知