Android 为应用添加数字角标
Posted 星辰
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 为应用添加数字角标相关的知识,希望对你有一定的参考价值。
今天在论坛上看到了一个帖子,终于搞清了我很久以来的一个困惑,android到底能不能实现ios的角标效果,QQ是怎么实现的。看了这个帖子顿时终于解除了我的困惑。
先说一个下大概的思路:
大家都知道android系统默认是不支持角标的。但是有时候你又可以在很多系统上看到角标,这些系统包括 小米手机的miui 三星手机的TouchWiz 索尼手机; 这些手机的系统应用都可以显示数字角标。这是应为这些系统进行了定制,使用的是自己的launcher. 所以在这些系统上的实现思路就是使用这些手机的私有api来实现显示角标。但是还有一部分手机不支持 实现方法是通过快捷方式,创建一个带角标的应用的快捷方式。需要更新角标的时候就去通过更新快捷方式来实现。
整个帖子的实现思路就是这样的。最后附上链接:http://www.eoeandroid.com/thread-542394-1-1.html
在小米 三星 索尼 手机 :图标上显示数字(未读消息数):这部分代码,是从QQ5.0.apk中找的。
小米已经测试通过了,
三星和索尼的,由于没有相应的手机,没有测试,有的,可能修改一下代码(判断是什么手机的代码),
测试一下,可以在回复一下测试结果,谢谢
小米已经测试通过了,
三星和索尼的,由于没有相应的手机,没有测试,有的,可能修改一下代码(判断是什么手机的代码),
测试一下,可以在回复一下测试结果,谢谢
1.原生系统(原生的Launcher ),只能修改快捷方式,增加和删除都会有toast提示
2.小米 三星 索尼 手机: 自定义的launcher: 发送显示未读消息数的action已经不同了。具体可以去看代码。。。
2.小米 三星 索尼 手机: 自定义的launcher: 发送显示未读消息数的action已经不同了。具体可以去看代码。。。
判断手机的代码:
/*** * 在应用图标的快捷方式上加数字 * @param clazz 启动的activity * @param isShowNum 是否显示数字 * @param num 显示的数字:整型 * @param isStroke 是否加上边框 * */ public static void addNumShortCut(Context context,Class<?> clazz,boolean isShowNum, String num, boolean isStroke) { Log.e(TAG, "manufacturer="+Build.MANUFACTURER); if (Build.MANUFACTURER.equalsIgnoreCase("Xiaomi")){ //小米 xiaoMiShortCut(context, clazz, num); }else if(Build.MANUFACTURER.equalsIgnoreCase("samsung")){ //三星 samsungShortCut(context, num); }else {//其他原生系统手机 installRawShortCut(context, MainActivity.class, isShowNum, num, isStroke); } }
下面写好的工具类,上面的方法也在其中。
/*** * 应用的快捷方式工具类 * * @author yang * */ public class AppShortCutUtil { private static final String TAG = "AppShortCutUtil"; //默认圆角半径 private static final int DEFAULT_CORNER_RADIUS_DIP = 8; //默认边框宽度 private static final int DEFAULT_STROKE_WIDTH_DIP = 2; //边框的颜色 private static final int DEFAULT_STROKE_COLOR = Color.WHITE; //中间数字的颜色 private static final int DEFAULT_NUM_COLOR = Color.parseColor("#CCFF0000"); /*** * * 生成有数字的图片(没有边框) * @param context * @param icon 图片 * @param isShowNum 是否要绘制数字 * @param num 数字字符串:整型数字 超过99,显示为"99+" * @return */ public static Bitmap generatorNumIcon(Context context, Bitmap icon, boolean isShowNum, String num) { DisplayMetrics dm = context.getResources().getDisplayMetrics(); //基准屏幕密度 float baseDensity = 1.5f;//240dpi float factor = dm.density/baseDensity; Log.e(TAG, "density:"+dm.density); Log.e(TAG, "dpi:"+dm.densityDpi); Log.e(TAG, "factor:"+factor); // 初始化画布 int iconSize = (int) context.getResources().getDimension(android.R.dimen.app_icon_size); Bitmap numIcon = Bitmap.createBitmap(iconSize, iconSize, Config.ARGB_8888); Canvas canvas = new Canvas(numIcon); // 拷贝图片 Paint iconPaint = new Paint(); iconPaint.setDither(true);// 防抖动 iconPaint.setFilterBitmap(true);// 用来对Bitmap进行滤波处理,这样,当你选择Drawable时,会有抗锯齿的效果 Rect src = new Rect(0, 0, icon.getWidth(), icon.getHeight()); Rect dst = new Rect(0, 0, iconSize, iconSize); canvas.drawBitmap(icon, src, dst, iconPaint); if(isShowNum){ if(TextUtils.isEmpty(num)){ num = "0"; } if(!TextUtils.isDigitsOnly(num)){ //非数字 Log.e(TAG, "the num is not digit :"+ num); num = "0"; } int numInt = Integer.valueOf(num); if(numInt > 99){//超过99 num = "99+"; // 启用抗锯齿和使用设备的文本字体大小 Paint numPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG); numPaint.setColor(Color.WHITE); numPaint.setTextSize(20f*factor); numPaint.setTypeface(Typeface.DEFAULT_BOLD); int textWidth=(int)numPaint.measureText(num, 0, num.length()); Log.e(TAG, "text width:"+textWidth); int circleCenter = (int) (15*factor);//中心坐标 int circleRadius = (int) (13*factor);//圆的半径 //绘制左边的圆形 Paint leftCirPaint = new Paint(Paint.ANTI_ALIAS_FLAG); leftCirPaint.setColor(Color.RED); canvas.drawCircle(iconSize-circleRadius-textWidth+(10*factor), circleCenter, circleRadius, leftCirPaint); //绘制右边的圆形 Paint rightCirPaint = new Paint(Paint.ANTI_ALIAS_FLAG); rightCirPaint.setColor(Color.RED); canvas.drawCircle(iconSize-circleRadius, circleCenter, circleRadius, rightCirPaint); //绘制中间的距形 Paint rectPaint = new Paint(Paint.ANTI_ALIAS_FLAG); rectPaint.setColor(Color.RED); RectF oval = new RectF(iconSize-circleRadius-textWidth+(10*factor), 2*factor, iconSize-circleRadius, circleRadius*2+2*factor); canvas.drawRect(oval, rectPaint); //绘制数字 canvas.drawText(num, (float)(iconSize-textWidth/2-(24*factor)), 23*factor, numPaint); }else{//<=99 // 启用抗锯齿和使用设备的文本字体大小 Paint numPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG); numPaint.setColor(Color.WHITE); numPaint.setTextSize(20f*factor); numPaint.setTypeface(Typeface.DEFAULT_BOLD); int textWidth=(int)numPaint.measureText(num, 0, num.length()); Log.e(TAG, "text width:"+textWidth); //绘制外面的圆形 //Paint outCirPaint = new Paint(Paint.ANTI_ALIAS_FLAG); //outCirPaint.setColor(Color.WHITE); //canvas.drawCircle(iconSize - 15, 15, 15, outCirPaint); //绘制内部的圆形 Paint inCirPaint = new Paint(Paint.ANTI_ALIAS_FLAG); inCirPaint.setColor(Color.RED); canvas.drawCircle(iconSize-15*factor, 15*factor, 15*factor, inCirPaint); //绘制数字 canvas.drawText(num, (float)(iconSize-textWidth/2-15*factor), 22*factor, numPaint); } } return numIcon; } /*** * * 生成有数字的图片(没有边框) * @param context * @param icon 图片 * @param isShowNum 是否要绘制数字 * @param num 数字字符串:整型数字 超过99,显示为"99+" * @return */ public static Bitmap generatorNumIcon2(Context context, Bitmap icon, boolean isShowNum, String num) { DisplayMetrics dm = context.getResources().getDisplayMetrics(); //基准屏幕密度 float baseDensity = 1.5f;//240dpi float factor = dm.density/baseDensity; Log.e(TAG, "density:"+dm.density); Log.e(TAG, "dpi:"+dm.densityDpi); Log.e(TAG, "factor:"+factor); // 初始化画布 int iconSize = (int) context.getResources().getDimension(android.R.dimen.app_icon_size); Bitmap numIcon = Bitmap.createBitmap(iconSize, iconSize, Config.ARGB_8888); Canvas canvas = new Canvas(numIcon); // 拷贝图片 Paint iconPaint = new Paint(); iconPaint.setDither(true);// 防抖动 iconPaint.setFilterBitmap(true);// 用来对Bitmap进行滤波处理,这样,当你选择Drawable时,会有抗锯齿的效果 Rect src = new Rect(0, 0, icon.getWidth(), icon.getHeight()); Rect dst = new Rect(0, 0, iconSize, iconSize); canvas.drawBitmap(icon, src, dst, iconPaint); if(isShowNum){ if(TextUtils.isEmpty(num)){ num = "0"; } if(!TextUtils.isDigitsOnly(num)){ //非数字 Log.e(TAG, "the num is not digit :"+ num); num = "0"; } int numInt = Integer.valueOf(num); if(numInt > 99){//超过99 num = "99+"; } //启用抗锯齿和使用设备的文本字体大小 //测量文本占用的宽度 Paint numPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG); numPaint.setColor(Color.WHITE); numPaint.setTextSize(20f*factor); numPaint.setTypeface(Typeface.DEFAULT_BOLD); int textWidth=(int)numPaint.measureText(num, 0, num.length()); Log.e(TAG, "text width:"+textWidth); /**----------------------------------* * TODO 绘制圆角矩形背景 start *------------------------------------*/ //圆角矩形背景的宽度 int backgroundHeight = (int) (2*15*factor); int backgroundWidth = textWidth>backgroundHeight ? (int)(textWidth+10*factor) : backgroundHeight; canvas.save();//保存状态 ShapeDrawable drawable = getDefaultBackground(context); drawable.setIntrinsicHeight(backgroundHeight); drawable.setIntrinsicWidth(backgroundWidth); drawable.setBounds(0, 0, backgroundWidth, backgroundHeight); canvas.translate(iconSize-backgroundWidth, 0); drawable.draw(canvas); canvas.restore();//重置为之前保存的状态 /**----------------------------------* * TODO 绘制圆角矩形背景 end *------------------------------------*/ //绘制数字 canvas.drawText(num, (float)(iconSize-(backgroundWidth + textWidth)/2), 22*factor, numPaint); } return numIcon; } /*** * * 生成有数字的图片(有边框) * @param context * @param icon 图片 * @param isShowNum 是否要绘制数字 * @param num 数字字符串:整型数字 超过99,显示为"99+" * @return */ public static Bitmap generatorNumIcon3(Context context, Bitmap icon, boolean isShowNum, String num) { DisplayMetrics dm = context.getResources().getDisplayMetrics(); //基准屏幕密度 float baseDensity = 1.5f;//240dpi float factor = dm.density/baseDensity; Log.e(TAG, "density:"+dm.density); Log.e(TAG, "dpi:"+dm.densityDpi); Log.e(TAG, "factor:"+factor); // 初始化画布 int iconSize = (int) context.getResources().getDimension(android.R.dimen.app_icon_size); Bitmap numIcon = Bitmap.createBitmap(iconSize, iconSize, Config.ARGB_8888); Canvas canvas = new Canvas(numIcon); // 拷贝图片 Paint iconPaint = new Paint(); iconPaint.setDither(true);// 防抖动 iconPaint.setFilterBitmap(true);// 用来对Bitmap进行滤波处理,这样,当你选择Drawable时,会有抗锯齿的效果 Rect src = new Rect(0, 0, icon.getWidth(), icon.getHeight()); Rect dst = new Rect(0, 0, iconSize, iconSize); canvas.drawBitmap(icon, src, dst, iconPaint); if(isShowNum){ if(TextUtils.isEmpty(num)){ num = "0"; } if(!TextUtils.isDigitsOnly(num)){ //非数字 Log.e(TAG, "the num is not digit :"+ num); num = "0"; } int numInt = Integer.valueOf(num); if(numInt > 99){//超过99 num = "99+"; } //启用抗锯齿和使用设备的文本字体大小 //测量文本占用的宽度 Paint numPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG); numPaint.setColor(Color.WHITE); numPaint.setTextSize(20f*factor); numPaint.setTypeface(Typeface.DEFAULT_BOLD); int textWidth=(int)numPaint.measureText(num, 0, num.length()); Log.e(TAG, "text width:"+textWidth); /**----------------------------------* * TODO 绘制圆角矩形背景:先画边框,再画内部的圆角矩形 start *------------------------------------*/ //圆角矩形背景的宽度 int backgroundHeight = (int) (2*15*factor); int backgroundWidth = textWidth>backgroundHeight ? (int)(textWidth+10*factor) : backgroundHeight; //边框的宽度 int strokeThickness = (int) (2*factor); canvas.save();//保存状态 int strokeHeight = backgroundHeight + strokeThickness*2; int strokeWidth = textWidth>strokeHeight ? (int)(textWidth+ 10*factor + 2*strokeThickness) : strokeHeight; ShapeDrawable outStroke = getDefaultStrokeDrawable(context); outStroke.setIntrinsicHeight(strokeHeight); outStroke.setIntrinsicWidth(strokeWidth); outStroke.setBounds(0, 0, strokeWidth, strokeHeight); canvas.translate(iconSize-strokeWidth-strokeThickness, strokeThickness); outStroke.draw(canvas); canvas.restore();//重置为之前保存的状态 canvas.save();//保存状态 ShapeDrawable drawable = getDefaultBackground(context); drawable.setIntrinsicHeight((int) (backgroundHeight+2*factor)); drawable.setIntrinsicWidth((int) (backgroundWidth+2*factor)); drawable.setBounds(0, 0, backgroundWidth, backgroundHeight); canvas.translate(iconSize-backgroundWidth-2*strokeThickness, 2*strokeThickness); drawable.draw(canvas); canvas.restore();//重置为之前保存的状态 /**----------------------------------* * TODO 绘制圆角矩形背景 end *------------------------------------*/ //绘制数字 canvas.drawText(num, (float)(iconSize-(backgroundWidth + textWidth+4*strokeThickness)/2), (22)*factor+2*strokeThickness, numPaint); } return numIcon; } /*** * * 生成有数字的图片(有边框的) * @param context * @param icon 图片 * @param isShowNum 是否要绘制数字 * @param num 数字字符串:整型数字 超过99,显示为"99+" * @return */ public static Bitmap generatorNumIcon4(Context context, Bitmap icon, boolean isShowNum, String num) { DisplayMetrics dm = context.getResources().getDisplayMetrics(); //基准屏幕密度 float baseDensity = 1.5f;//240dpi float factor = dm.density/baseDensity; Log.e(TAG, "density:"+dm.density); Log.e(TAG, "dpi:"+dm.densityDpi); Log.e(TAG, "factor:"+factor); // 初始化画布 int iconSize = (int) context.getResources().getDimension(android.R.dimen.app_icon_size); Bitmap numIcon = Bitmap.createBitmap(iconSize, iconSize, Config.ARGB_8888); Canvas canvas = new Canvas(numIcon); // 拷贝图片 Paint iconPaint = new Paint(); iconPaint.setDither(true);// 防抖处理 iconPaint.setFilterBitmap(true);// 用来对Bitmap进行滤波处理,这样,当你选择Drawable时,会有抗锯齿的效果 Rect src = new Rect(0, 0, icon.getWidth(), icon.getHeight()); Rect dst = new Rect(0, 0, iconSize, iconSize); canvas.drawBitmap(icon, src, dst, iconPaint); if(isShowNum){ if(TextUtils.isEmpty(num)){ num = "0"; } if(!TextUtils.isDigitsOnly(num)){ //非数字 Log.e(TAG, "the num is not digit :"+ num); num = "0"; } int numInt = Integer.valueOf(num); if(numInt > 99){//超过99 num = "99+"; } //启用抗锯齿和使用设备的文本字体 //测量文本占用的宽度 Paint numPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG); numPaint.setColor(Color.WHITE); numPaint.setTextSize(25f*factor); numPaint.setTypeface(Typeface.DEFAULT_BOLD); int textWidth=(int)numPaint.measureText(num, 0, num.length()); Log.e(TAG, "text width:"+textWidth); /**----------------------------------* * TODO 绘制圆角矩形背景 start *------------------------------------*/ //边框的宽度 int strokeThickness = (int) (DEFAULT_STROKE_WIDTH_DIP*factor); //圆角矩形背景的宽度 float radiusPx = 15*factor; int backgroundHeight = (int) (2*(radiusPx+strokeThickness));//2*(半径+边框宽度) int backgroundWidth = textWidth>backgroundHeight ? (int)(textWidth + 10*factor + 2*strokeThickness) : backgroundHeight; canvas.save();//保存状态 ShapeDrawable drawable = getDefaultBackground2(context); drawable.setIntrinsicHeight(backgroundHeight); drawable.setIntrinsicWidth(backgroundWidth); drawable.setBounds(0, 0, backgroundWidth, backgroundHeight); canvas.translate(iconSize-backgroundWidth-strokeThickness, 2*strokeThickness); drawable.draw(canvas); canvas.restore();//重置为之前保存的状态 /**----------------------------------* * TODO 绘制圆角矩形背景 end *------------------------------------*/ //绘制数字 canvas.drawText(num, (float)(iconSize-(backgroundWidth + textWidth+2*strokeThickness)/2), (float) (25*factor+2.5*strokeThickness), numPaint); } return numIcon; } /*** * 创建原生系统的快捷方式 * @param context * @param clazz 启动的activity * @param isShowNum 是否显示数字 * @param num 显示的数字:整型 * @param isStroke 是否加上边框 */ public static void installRawShortCut(Context context, Class<?> clazz, boolean isShowNum, String num, boolean isStroke) { Log.e(TAG, "installShortCut...."); Intent shortcutIntent = new Intent( "com.android.launcher.action.INSTALL_SHORTCUT"); //名称 shortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, context.getString(R.string.app_name)); // 是否可以有多个快捷方式的副本,参数如果是true就可以生成多个快捷方式,如果是false就不会重复添加 shortcutIntent.putExtra("duplicate", false); //点击快捷方式:打开activity Intent mainIntent = new Intent(Intent.ACTION_MAIN); mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); mainIntent.setClass(context, clazz); shortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, mainIntent); //快捷方式的图标 if(isStroke){ shortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON, generatorNumIcon4( context, ((BitmapDrawable)context.getResources().getDrawable(R.drawable.ic_launcher)).getBitmap(), isShowNum, num)); }else{ shortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON, generatorNumIcon2( context, ((BitmapDrawable)context.getResources().getDrawable(R.drawable.ic_launcher)).getBitmap(), isShowNum, num)); } context.sendBroadcast(shortcutIntent); } /*** * 是否已经创建了快捷方式 * @param context * @return */ public static boolean isAddShortCut(Context context) { Log.e(TAG, "isAddShortCut...."); boolean isInstallShortcut = false; final ContentResolver cr = context.getContentResolver(); //TODO 注释的代码,在有的手机:修改了ROM的系统,不能支持 /*int versionLevel = android.os.Build.VERSION.SDK_INT; String AUTHORITY = "com.android.launcher2.settings"; //2.2以上的系统的文件文件名字是不一样的 if (versionLevel >= 8) { AUTHORITY = "com.android.launcher2.settings"; } else { AUTHORITY = "com.android.launcher.settings"; }*/ String AUTHORITY = getAuthorityFromPermission(context, "com.android.launcher.permission.READ_SETTINGS"); Log.e(TAG, "AUTHORITY : " +AUTHORITY); final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/favorites?notify=true"); Cursor c = cr.query(CONTENT_URI, new String[] { "title" }, "title=?", new String[] { context.getString(R.string.app_name) }, null); if (c != null && c.getCount() > 0) { isInstallShortcut = true; } if(c != null){ c.close(); } Log.e(TAG, "isAddShortCut....isInstallShortcut="+isInstallShortcut); return isInstallShortcut; } /** * 删除快捷方式 * @param context * @param clazz */ public static void deleteShortCut(Context context, Class<?> clazz){ Log.e(TAG, "delShortcut...."); if (Build.MANUFACTURER.equalsIgnoreCase("Xiaomi")){ //小米 //当为""时,不显示数字,相当于隐藏了) xiaoMiShortCut(context, clazz, ""); }else if(Build.MANUFACTURER.equalsIgnoreCase("samsung")){ //三星 samsungShortCut(context, "0"); }else {//其他原生系统手机 //删除显示数字的快捷方式 deleteRawShortCut(context, clazz); //安装不显示数字的快捷方式 //installRawShortCut(context, clazz, false, "0"); } } /*** * 删除原生系统的快捷方式 * @param context * @param clazz 启动的activity */ public static void deleteRawShortCut(Context context, Class<?> clazz) { Intent intent = new Intent("com.android.launcher.action.UNINSTALL_SHORTCUT"); //快捷方式的名称 intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, context.getString(R.string.app_name)); Intent intent2 = new Intent(); intent2.setClass(context, clazz); intent2.setAction(Intent.ACTION_MAIN); intent2.addCategory(Intent.CATEGORY_LAUNCHER); intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT,intent2); context.sendBroadcast(intent); } /*** * 取得权限相应的认证URI * @param context * @param permission * @return */ public static String getAuthorityFromPermission(Context context, String permission) { if (TextUtils.isEmpty(permission)) { return null; } List<PackageInfo> packInfos = context.getPackageManager().getInstalledPackages(PackageManager.GET_PROVIDERS); if (packInfos == null) { return null; } for (PackageInfo info : packInfos) { ProviderInfo[] providers = info.providers; if (providers != null) { for (ProviderInfo provider : providers) { if (permission.equals(provider.readPermission) || permission.equals(provider.writePermission)) { return provider.authority; } } } } return null; } /*** * 在小米应用图标的快捷方式上加数字<br> * * * @param context * @param num 显示的数字:大于99,为"99",当为""时,不显示数字,相当于隐藏了)<br><br> * * 注意点: * context.getPackageName()+"/."+clazz.getSimpleNa以上是关于Android 为应用添加数字角标的主要内容,如果未能解决你的问题,请参考以下文章
Android Icon数字角标(BadgeNumber)的实现方式