Android图标上显示数字或加上数字的功能,直到Android4.4也没有实现。所以要修改系统的应用launcher来实现该效果。
之前你可能要参考其他一些博客或资料,对launcher的架构有一个相当的了解。这里不再作过多的分析。
找到src/com/android/launcher2/PagedViewIcon.java
这个类是用于显示所有应用列表里的一个图标,就是TextView然后使用setCompoundDrawablesWithIntrinsicBounds()方法加上icon
图标icon取自ApplicationInfo(注意了这个ApplicationInfo类是在launcher里自定义的,不是SDK提供的ApplicationInfo),对其进行修改,增加一个backupIcon属性用于备份Icon以便以后恢复成原来的状态。
至于加上数字就是在canvas上画个圆和写上数字,如果要有更好的效果就自己画吧。
public void applyFromApplicationInfo(ApplicationInfo info, boolean scaleUp,
HolographicOutlineHelper holoOutlineHelper) {
mHolographicOutlineHelper = holoOutlineHelper;
mIcon = info.iconBitmap;
//modify --start--
Boolean isExistCount = false;
Boolean isRestore = false;
String count = null;
for(Map<String,String> map : LauncherApplication.countIconList){
if(map.get("pkgName").equals( info.componentName.getPackageName() )){
count = map.get("count");
if(count.equals("0")){
isRestore = true;
}else {
isExistCount = true;
}
if(info.backupIcon == null){
info.backupIcon = mIcon.copy(Bitmap.Config.ARGB_8888, true);
}
break;
}
}
if(isExistCount){
setCompoundDrawablesWithIntrinsicBounds(null, generatorNumIcon(count), null, null);
}else if(isRestore){
setCompoundDrawablesWithIntrinsicBounds(null, restoreIcon(info.backupIcon), null, null);
}
else{
setCompoundDrawablesWithIntrinsicBounds(null, new FastBitmapDrawable(mIcon), null, null);
}
//old:setCompoundDrawablesWithIntrinsicBounds(null, new FastBitmapDrawable(mIcon), null, null);
//modify --end--
setText(info.title);
setTag(info);
}
//modify --start--
private Drawable restoreIcon(Bitmap b) {
Drawable mDrawable = new FastBitmapDrawable(b);
Canvas canvas = new Canvas(mIcon);
canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR); //清空画布
mDrawable.draw(canvas); //画原图
return mDrawable;
}
private Drawable generatorNumIcon(String count){
final int X = mIcon.getWidth() - 18;
final int Y = 18;
Drawable mDrawable = new FastBitmapDrawable(mIcon);
Canvas canvas = new Canvas(mIcon);
Paint bgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
bgPaint.setColor(Color.RED);
canvas.drawCircle( X+5, Y-5, 12, bgPaint);
//启用抗锯齿和使用设备的文本字距
Paint countPaint = new Paint(Paint.ANTI_ALIAS_FLAG|Paint.DEV_KERN_TEXT_FLAG);
countPaint.setColor(Color.WHITE);
countPaint.setTextSize(15f);
//countPaint.setTypeface(Typeface.DEFAULT_BOLD);
int c = Integer.parseInt(count);
if(c>99){
canvas.drawText("99", X-3 , Y, countPaint);
}else if(c>9){
canvas.drawText(count, X-3 , Y, countPaint);
}else{
canvas.drawText(count, X+1 , Y, countPaint);
}
mDrawable.draw(canvas);
return mDrawable;
}
//modify --end--
其他应用通过广播来通知修改数字。
新建一个类用于接收广播,当有广播来时将数据保存到LauncherApplication里,并且刷新页面。
public class CountIconReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String pkgName = intent.getStringExtra("pkgName");
if(pkgName == null){
Log.e("CountIconReceiver","pkgName=null");
return;
}
String count = String.valueOf(intent.getIntExtra("count", 0));
for(int i = 0; i < LauncherApplication.countIconList.size(); i++)
{
HashMap<String,String> tempMap = (HashMap<String, String>) LauncherApplication.countIconList.get(i);
if(tempMap.get("pkgName").equals(pkgName)){
tempMap.put("count", count);
Log.d("CountIconReceiver","Receiver exist pkgName:"+pkgName+" count:"+count);
update();
return;
}
}
Map<String,String> map = new HashMap<String,String>();
map.put("pkgName",pkgName);
map.put("count",count);
LauncherApplication.countIconList.add(map);
Log.d("MXY-L","Receiver pkgName:"+pkgName+" count:"+count);
update();
}
private void update() {
AppsCustomizePagedView mPagedView = LauncherApplication.mWeakReference.get();
if(mPagedView != null){
mPagedView.updatePackages();
}
}
}
在AndroidManifest.xml添加广播接受
<receiver android:name="com.android.broadcast.CountIconReceiver">
<intent-filter >
<action android:name="com.android.launcher.COUNT_ICON"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</receiver>
在src/com/android/launcher2/LauncherApplication.java类加上以下属性。AppsCustomizePagedView就是我们PageViewIcon的容器。
public static ArrayList< Map<String,String> > countIconList = new ArrayList< Map<String,String> >();
public static WeakReference<AppsCustomizePagedView> mWeakReference;
在src/com/android/launcher2/Launcher.java类里的onCreate()的最后将AppsCustomizePagedView的对象保存起来。
LauncherApplication.mWeakReference = new WeakReference<AppsCustomizePagedView>(mAppsCustomizeContent);
功能就这样实现完成,使用adb命令测试结果
adb shell am broadcast -a com.android.launcher.COUNT_ICON --es pkgName "com.android.mms" --ei count 11
效果如下:
因为我们修改了Icon里的bitmap,之后包括创建快捷方式和文件夹里的图标都会跟着变化。
如果需要还原只要发送0就好了
adb shell am broadcast -a com.android.launcher.COUNT_ICON --es pkgName "com.android.mms" --ei count 0
思考:
如果要实现更细致的要求,比如不在文件夹里显示数字,那么你就不能直接改ApplicationInfo里的Icon,你可能要将在快捷方式的图标BubbleTextView和应用列表的图标PageViewIcon分开修改。但在快捷方式的容器里Workspace里没有很好的刷新方法。这也是我误打误撞直接修改Icon的原因,这样子代码修改也比较少,不会有严重的不同步的问题。