android 应用图标 角标 显示未读消息

Android 桌面图标添加未读消息角标APP角标最佳实践 Android 桌面图标添加未读消息角标APP角标最佳实践 本项目的完整演示代码 https://github.com/benchegnzhou/AndroidDevelomentArtDemo 最近为一个为应用添加角标的是产品需求大为苦恼,不禁感叹Android机型真的多样,由于Android的开源导致各大平台和厂商纷纷定义了自家的rom,这本应是一件利好(对广大的用户),毕竟开源产生了无穷的... 阅读详情
Android桌面角标的适配确实是非常坑爹的需求。原生系统根本就没有这个功能,国内很多厂家效仿ios都自己定义了该功能。Android程序员就很苦逼,要适配很多机型。建议万不得已情况下还是不要进行这项工作。因为google的意思是让大家用notification(提示栏)即可,不过无妨,厉害的android第三方厂商可以通过在自定义的launcher(启动器)中操作来实现添加角标添加角标的原理就是发送一个broadcast(广播),在广播的intent中指定需要被添加角标的应用的packagename(包名),classname(类名),count(角标数目)。当然了,不同厂商的手机的角标操作的intent的action是不一样的。

1.原生系统(原生的Launcher ),只能修改快捷方式,增加和删除都会有toast提示
2.小米 三星  索尼 华为 手机: 自定义的launcher:  发送显示未读消息数的action已经不同了。具体可以去看代码。。

手上没有那么多测试机...只测了vivo的(通过),提供给大家参考一下,还有如果个例方法不成功,你注意下num(未读消息的条数)的类型,建议打印一下manufacturer,然后去对应手机厂商的方法:

/***
 * 在应用图标的快捷方式上加数字
 * @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 if (Build.MANUFACTURER.equalsIgnoreCase("vivo")) {
        //vivo
        vivoShortCut(context, num);
    } else {//其他原生系统手机
        installRawShortCut(context, MainActivity.class, isShowNum, num, isStroke);
    }

}
/***
 * 在vivo应用图标的快捷方式上加数字<br>
 * @param context
 * @param num 显示的数字:大于99,为"99",当为""时,不显示数字,相当于隐藏了)<br><br>
 * 注意点:
 *
 */
public static void vivoShortCut(Context context, String num) {
    Log.e(TAG, "vivoShortCut....");
    Intent localIntent = new Intent("launcher.action.CHANGE_APPLICATION_NOTIFICATION_NUM");
    localIntent.putExtra("packageName", context.getPackageName());
    String launchClassName = context.getPackageManager().getLaunchIntentForPackage(context.getPackageName()).getComponent().getClassName();
    localIntent.putExtra("className", launchClassName);
    if (TextUtils.isEmpty(num)) {
        num = "";
    } else {
        int numInt = Integer.valueOf(num);
        if (numInt > 0) {
            if (numInt > 99) {
                num = "99";
            }
        } else {
            num = "0";
        }
    }
    localIntent.putExtra("notificationNum", Integer.parseInt(num));
    context.sendBroadcast(localIntent);
}
/***
 * 在小米应用图标的快捷方式上加数字<br>
 *
 *
 * @param context
 * @param num 显示的数字:大于99,为"99",当为""时,不显示数字,相当于隐藏了)<br><br>
 *
 * 注意点:
 * context.getPackageName()+"/."+clazz.getSimpleName() (这个是启动activity的路径)中的"/."不能缺少
 *
 */
public static void xiaoMiShortCut(Context context, Class<?> clazz, String num) {
    Log.e(TAG, "xiaoMiShortCut....");
    Intent localIntent = new Intent("android.intent.action.APPLICATION_MESSAGE_UPDATE");
    localIntent.putExtra("android.intent.extra.update_application_component_name", context.getPackageName() + "/." + clazz.getSimpleName());
    if (TextUtils.isEmpty(num)) {
        num = "";
    } else {
        int numInt = Integer.valueOf(num);
        if (numInt > 0) {
            if (numInt > 99) {
                num = "99";
            }
        } else {
            num = "0";
        }
    }
    localIntent.putExtra("android.intent.extra.update_application_message_text", num);
    context.sendBroadcast(localIntent);
}
/***
 * 索尼手机:应用图标的快捷方式上加数字
 * @param context
 * @param num
 */
public static void sonyShortCut(Context context, String num) {
    String activityName = getLaunchActivityName(context);
    if (activityName == null) {
        return;
    }
    Intent localIntent = new Intent();
    int numInt = Integer.valueOf(num);
    boolean isShow = true;
    if (numInt < 1) {
        num = "";
        isShow = false;
    } else if (numInt > 99) {
        num = "99";
    }
    localIntent.putExtra("com.sonyericsson.home.intent.extra.badge.SHOW_MESSAGE", isShow);
    localIntent.setAction("com.sonyericsson.home.action.UPDATE_BADGE");
    localIntent.putExtra("com.sonyericsson.home.intent.extra.badge.ACTIVITY_NAME", activityName);
    localIntent.putExtra("com.sonyericsson.home.intent.extra.badge.MESSAGE", num);
    localIntent.putExtra("com.sonyericsson.home.intent.extra.badge.PACKAGE_NAME", context.getPackageName());
    context.sendBroadcast(localIntent);
}

/***
 * 三星手机:应用图标的快捷方式上加数字
 * @param context
 * @param num
 */
public static void samsungShortCut(Context context, String num) {
    int numInt = Integer.valueOf(num);
    if (numInt < 1) {
        num = "0";
    } else if (numInt > 99) {
        num = "99";
    }
    String activityName = getLaunchActivityName(context);
    Intent localIntent = new Intent("android.intent.action.BADGE_COUNT_UPDATE");
    localIntent.putExtra("badge_count", num);
    localIntent.putExtra("badge_count_package_name", context.getPackageName());
    localIntent.putExtra("badge_count_class_name", activityName);
    context.sendBroadcast(localIntent);
}
下面我把整个类贴出来:

package com.vlvxing.app.utils;

import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.RoundRectShape;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;

import com.vlvxing.app.R;
import com.vlvxing.app.ui.MainActivity;

import java.util.Iterator;
import java.util.List;

/***
 * 应用的快捷方式工具类
 * 目前收录的有
 *
 *
 * vivo vivo
 * 三星 samsung
 * 小米  Xiaomi
 * 中兴  ZTE
 * @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 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 if (Build.MANUFACTURER.equalsIgnoreCase("vivo")) {
            //vivo
            vivoShortCut(context, num);
        } else {//其他原生系统手机
            installRawShortCut(context, MainActivity.class, isShowNum, num, isStroke);
        }

    }


    /***
     * 在vivo应用图标的快捷方式上加数字<br>
     * @param context
     * @param num 显示的数字:大于99,为"99",当为""时,不显示数字,相当于隐藏了)<br><br>
     * 注意点:
     *
     */
    public static void vivoShortCut(Context context, String num) {
        Log.e(TAG, "vivoShortCut....");
        Intent localIntent = new Intent("launcher.action.CHANGE_APPLICATION_NOTIFICATION_NUM");
        localIntent.putExtra("packageName", context.getPackageName());
        String launchClassName = context.getPackageManager().getLaunchIntentForPackage(context.getPackageName()).getComponent().getClassName();
        localIntent.putExtra("className", launchClassName);
        if (TextUtils.isEmpty(num)) {
            num = "";
        } else {
            int numInt = Integer.valueOf(num);
            if (numInt > 0) {
                if (numInt > 99) {
                    num = "99";
                }
            } else {
                num = "0";
            }
        }
        localIntent.putExtra("notificationNum", Integer.parseInt(num));
        context.sendBroadcast(localIntent);
    }


    /***
     * 在小米应用图标的快捷方式上加数字<br>
     *
     *
     * @param context
     * @param num 显示的数字:大于99,为"99",当为""时,不显示数字,相当于隐藏了)<br><br>
     *
     * 注意点:
     * context.getPackageName()+"/."+clazz.getSimpleName() (这个是启动activity的路径)中的"/."不能缺少
     *
     */
    public static void huaWeiShortCut(Context context, Class<?> clazz, String num) {
        Log.e(TAG, "xiaoMiShortCut....");


        String launcherClassName = "com.redfinger.huaweichangebadge.MainActivity";//启动的Activity完整名称
        Bundle localBundle = new Bundle();//需要存储的数据
        localBundle.putString("package", context.getPackageName());//包名
        localBundle.putString("class", launcherClassName);

        if (TextUtils.isEmpty(num)) {
            num = "";
        } else {
            int numInt = Integer.valueOf(num);
            if (numInt > 0) {
                if (numInt > 99) {
                    num = "99";
                }
            } else {
                num = "0";
            }
        }
        localBundle.putInt("badgenumber", Integer.parseInt(num));//未读信息条数
        context.getContentResolver().call(Uri.parse("content://com.huawei.android.launcher.settings/badge/"),
                "change badge", null, localBundle);
    }


    /***
     * 在小米应用图标的快捷方式上加数字<br>
     *
     *
     * @param context
     * @param num 显示的数字:大于99,为"99",当为""时,不显示数字,相当于隐藏了)<br><br>
     *
     * 注意点:
     * context.getPackageName()+"/."+clazz.getSimpleName() (这个是启动activity的路径)中的"/."不能缺少
     *
     */
    public static void xiaoMiShortCut(Context context, Class<?> clazz, String num) {
        Log.e(TAG, "xiaoMiShortCut....");
        Intent localIntent = new Intent("android.intent.action.APPLICATION_MESSAGE_UPDATE");
        localIntent.putExtra("android.intent.extra.update_application_component_name", context.getPackageName() + "/." + clazz.getSimpleName());
        if (TextUtils.isEmpty(num)) {
            num = "";
        } else {
            int numInt = Integer.valueOf(num);
            if (numInt > 0) {
                if (numInt > 99) {
                    num = "99";
                }
            } else {
                num = "0";
            }
        }
        localIntent.putExtra("android.intent.extra.update_application_message_text", num);
        context.sendBroadcast(localIntent);
    }

    /***
     * 索尼手机:应用图标的快捷方式上加数字
     * @param context
     * @param num
     */
    public static void sonyShortCut(Context context, String num) {
        String activityName = getLaunchActivityName(context);
        if (activityName == null) {
            return;
        }
        Intent localIntent = new Intent();
        int numInt = Integer.valueOf(num);
        boolean isShow = true;
        if (numInt < 1) {
            num = "";
            isShow = false;
        } else if (numInt > 99) {
            num = "99";
        }
        localIntent.putExtra("com.sonyericsson.home.intent.extra.badge.SHOW_MESSAGE", isShow);
        localIntent.setAction("com.sonyericsson.home.action.UPDATE_BADGE");
        localIntent.putExtra("com.sonyericsson.home.intent.extra.badge.ACTIVITY_NAME", activityName);
        localIntent.putExtra("com.sonyericsson.home.intent.extra.badge.MESSAGE", num);
        localIntent.putExtra("com.sonyericsson.home.intent.extra.badge.PACKAGE_NAME", context.getPackageName());
        context.sendBroadcast(localIntent);
    }

    /***
     * 三星手机:应用图标的快捷方式上加数字
     * @param context
     * @param num
     */
    public static void samsungShortCut(Context context, String num) {
        int numInt = Integer.valueOf(num);
        if (numInt < 1) {
            num = "0";
        } else if (numInt > 99) {
            num = "99";
        }
        String activityName = getLaunchActivityName(context);
        Intent localIntent = new Intent("android.intent.action.BADGE_COUNT_UPDATE");
        localIntent.putExtra("badge_count", num);
        localIntent.putExtra("badge_count_package_name", context.getPackageName());
        localIntent.putExtra("badge_count_class_name", activityName);
        context.sendBroadcast(localIntent);
    }


    /***
     *
     * &#x751f;&#x6210;&#x6709;&#x6570;&#x5b57;&#x7684;&#x56fe;&#x7247;(&#x6ca1;&#x6709;&#x8fb9;&#x6846;)
     * @param context
     * @param icon &#x56fe;&#x7247;
     * @param isShowNum &#x662f;&#x5426;&#x8981;&#x7ed8;&#x5236;&#x6570;&#x5b57;
     * @param num &#x6570;&#x5b57;&#x5b57;&#x7b26;&#x4e32;&#xff1a;&#x6574;&#x578b;&#x6570;&#x5b57; &#x8d85;&#x8fc7;99&#xff0c;&#x663e;&#x793a;&#x4e3a;"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, Bitmap.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, Bitmap.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, Bitmap.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, Bitmap.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_loading_rotate)).getBitmap(),
                            isShowNum,
                            num));
        } else {
            shortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON,
                    generatorNumIcon2(
                            context,
                            ((BitmapDrawable) context.getResources().getDrawable(R.drawable.ic_loading_rotate)).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;
    }


    /***
     * 取得当前应用的启动activity的名称:
     * mainfest.xml中配置的 android:name:"
     * @param context
     * @return
     */
    public static String getLaunchActivityName(Context context) {
        PackageManager localPackageManager = context.getPackageManager();
        Intent localIntent = new Intent("android.intent.action.MAIN");
        localIntent.addCategory("android.intent.category.LAUNCHER");
        try {
            Iterator<ResolveInfo> localIterator = localPackageManager.queryIntentActivities(localIntent, 0).iterator();
            while (localIterator.hasNext()) {
                ResolveInfo localResolveInfo = localIterator.next();
                if (!localResolveInfo.activityInfo.applicationInfo.packageName.equalsIgnoreCase(context.getPackageName()))
                    continue;
                String str = localResolveInfo.activityInfo.name;
                return str;
            }
        } catch (Exception localException) {
            return null;
        }
        return null;
    }

    /***
     * 得到一个默认的背景:圆角矩形<br><br>
     * 使用代码来生成一个背景:相当于用<shape>的xml的背景
     *
     * @return
     */
    private static ShapeDrawable getDefaultBackground(Context context) {

        //这个是为了应对不同分辨率的手机,屏幕兼容性
        int r = dipToPixels(context, DEFAULT_CORNER_RADIUS_DIP);
        float[] outerR = new float[]{r, r, r, r, r, r, r, r};

        //圆角矩形
        RoundRectShape rr = new RoundRectShape(outerR, null, null);
        ShapeDrawable drawable = new ShapeDrawable(rr);
        drawable.getPaint().setColor(DEFAULT_NUM_COLOR);//设置颜色
        return drawable;

    }

    /***
     * 得到一个默认的背景:圆角矩形<br><br>
     * 使用代码来生成一个背景:相当于用<shape>的xml的背景
     *
     * @return
     */
    private static ShapeDrawable getDefaultBackground2(Context context) {

        //这个是为了应对不同分辨率的手机,屏幕兼容性
        int r = dipToPixels(context, DEFAULT_CORNER_RADIUS_DIP);
        float[] outerR = new float[]{r, r, r, r, r, r, r, r};
        int distance = dipToPixels(context, DEFAULT_STROKE_WIDTH_DIP);

        //圆角矩形
        RoundRectShape rr = new RoundRectShape(outerR, null, null);
        ShapeDrawable drawable = new ShapeDrawable();
//        drawable.getFillpaint().setColor(DEFAULT_NUM_COLOR);//设置填充颜色
//        drawable.getStrokepaint().setColor(DEFAULT_STROKE_COLOR);//设置边框颜色
//        drawable.getStrokepaint().setStrokeWidth(distance);//设置边框宽度
        return drawable;

    }


    /***
     * 得到一个默认的背景:圆角矩形<br><br>
     * 使用代码来生成一个背景:相当于用<shape>的xml的背景
     *
     * @return
     */
    private static ShapeDrawable getDefaultStrokeDrawable(Context context) {

        //这个是为了应对不同分辨率的手机,屏幕兼容性
        int r = dipToPixels(context, DEFAULT_CORNER_RADIUS_DIP);
        int distance = dipToPixels(context, DEFAULT_STROKE_WIDTH_DIP);
        float[] outerR = new float[]{r, r, r, r, r, r, r, r};

        //圆角矩形
        RoundRectShape rr = new RoundRectShape(outerR, null, null);
        ShapeDrawable drawable = new ShapeDrawable(rr);
        drawable.getPaint().setStrokeWidth(distance);
        drawable.getPaint().setStyle(Paint.Style.FILL);
        drawable.getPaint().setColor(DEFAULT_STROKE_COLOR);//设置颜色
        return drawable;
    }

    /***
     * dp to px
     * @param dip
     * @return
     */
    public static int dipToPixels(Context context, int dip) {
        Resources r = context.getResources();
        float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, r.getDisplayMetrics());
        return (int) px;
    }
}
这里在应用图标上添加了角标来展示未读消息条目,隐藏可以复用这些方法未读消息的条目更改为0,就可以去掉角标.


UniApp实战:5分钟搞定安卓应用图标角标显示未读消息(附完整代码) 本文详细介绍了在UniApp中实现安卓应用图标角标显示未读消息的完整实战方案。针对不同安卓厂商的兼容性问题,提供了基于UTS原生插件的多品牌适配方法,涵盖华为、小米、vivo等主流设备的权限配置与核心代码实现,帮助开发者快速集成稳定可靠的消息提醒功能。 阅读详情

相关推荐

Android桌面应用:显示未读短信和未接来电数量

在onUpdate方法中,我们通过RemoteViews找到布局文件widget_layout,并使用setTextViewText方法来设置未读短信和未接来电的数量。Launcher是Android系统默认的桌面应用程序,它负责显示桌面上的应用图标,并提供一些常用的操作,比如打开应用、卸载应用等。注意,上述代码中使用的SMS_UNREAD_COUNT和CALL_UNANSWERED_COUNT是自定义的action,需要确保系统或其他应用已发送相应的广播,并附带未读短信和未接来电的数目信息。

ByteSparkX的博客 503

Android系统 应用图标显示未读消息数(BadgeNumber) 桌面app图标的角标显示 Android系统 应用图标显示未读消息数(BadgeNumber) 桌面app图标的角标显示...

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_34146986的博客 419

IsBadgeWorking:Android角标提示实现指南

本文还有配套的精品资源,点击获取 简介:IsBadgeWorking是一个Android库,用于实现应用图标角标提示功能,显示未读消息或更新数量。该库简化了角标实现,支持动态设置和广泛设备兼容性。开发者可通过源码学习和定制,快速集成角标功能到应用中。本文介绍了如何使用IsBadgeWorking库,包括添加依赖、初始化角标管理器、设置角标以及更新和清除角标的操作。 ...

weixin_42599558的博客 1509

Android系统 应用图标显示未读消息数(BadgeNumber) 桌面app图标的角标显示

参考: http://my.oschina.net/ososchina/blog/352286?p=1#comments  http://www.voidcn.com/blog/kongbaidepao/article/p-62251.html  http://www.eoeandroid.com/thread-557210-1-1.html Android系统 小米,三星,索尼手

llixiangjian的博客 6209

cordova显示角标插件

cordova开发的APP,需要在图标上显示通知数量的插件。

Android ToolBar、TabLayout加角标显示未读消息

Android 使用badgeView让ToolBar、TabLayout加角标显示未读消息或小红点

Android系统实现应用角标未读消息数原理

什么叫应用桌面角标 (BadgeNumber)? 即,未读消息通过在应用图标右上角显示数字提示用户的形式。也可以在通知栏里通知,消息阅读后,角标可以减少和消失。 实现原理: 其实本来 Android 原生系统是不支持应用桌面角标 (BadgeNumber) 显示的。并不是应用本身处理对启动图标进行修改,图标的动态修改的过程主要是在Launcher里面完成的。在应用安装,更新,卸载的时候,都会有广播发出,Launcher在LauncherApplication 中注册广播,在LauncherMode

Jason_Lee155的博客 3241

TabLayout标题右上角加数字标记

Tablayout和ViewPager的结合使用Tablayout标题右上角加标记

shortcutbadger设置在桌面上显示新消息的角标(转载)

原文链接《shortcutbadger设置在桌面上显示新消息的角标》:点击打开链接===============================================================================================shortcutbadger依赖库的介绍先上依赖库下载地址:https://github.com/leolin310148/Sh...

WalterHu1129的博客 2425

Android 桌面图标右上角显示未读消息数字

本文主要介绍了Android 桌面图标右上角显示未读消息数字的方法。具有很好的参考价值。下面跟着小编一起来看下吧

android 桌面APP图标增加角标功能

android 系统桌面logo增加消息角标功能,解决目前android系统只有小米等几类手机有消息角标,华为、三星等手机。。

Android应用角标终极指南:告别未读红点困扰,轻松适配Lawnchair与Smart Launcher

还在为Android应用上的未读消息红点而烦恼吗?🤔 ShortcutBadger是一个强大的Android库,能够让你的应用在三星、LG、索尼和HTC等启动器上显示类似iOS的角标通知,完美解决未读消息提醒问题! ## 🔥 什么是应用角标通知? 应用角标通知是指在应用图标右上角显示的数字或红点,用于提示用户未读消息数量。这种直观的提醒方式让用户一眼就能看到哪些应用有待处理的消息,显著提升

gitblog_00435的博客 460

Unity UGUI高斯模糊背景效果 简易解决方案

UnityUI背景模糊效果简易解决方案1.功能描述简易界面框架搭建合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入 1.功能描述 在我们玩游戏的时候,经常会看到一些游戏会对UI做这样的处理: 可以看到,新开的上层UI界面会以下层UI界面的模糊

SaberZG的博客 1万+

Android系统 应用图标显示未读消息数 BadgeNumber 桌面app图标的角标显示

Android系统 应用图标显示未读消息数 BadgeNumber 桌面app图标的角标显示

qq_44949789的博客 526

跟着团子学SAP CO:结果分析各评估方法计算逻辑概览 RA Valuation Method Overview KKA1/KKA2/KKA3

在大部分企业财务月结时,结果分析是必不可少的一环,通过结果分析系统可按照设定的评估方法及相关配置自动计算在制品(WIP)、主营业务成本、主营业务收入等等。 在结果分析中最重要的是根据业务需求选择合适的评估方法,目前SAP共提供15种评估方法,今天针对每一种方法计算逻辑及常用的业务场景进行介绍: (1)基于收入的评估方式(带利润实现) Revenue-Based Method with Profit Realization 第一种方式的计算是基于实际收入与计划收入的占比推算应结转的主营业务成本,如果当期累计的

Lucas1211的博客 1万+

物联网技术(AIR780E cat1-4G模块+小程序+腾讯云MQTT)驱动的智能井盖系统设计(连载01)

终端设备采用AIR780E模块作为主控器,电子模块有三轴加速度传感器、可燃气体传感器,AIR780E模块(4G模块)通过MQTT通信协议进行数据打包上传至EMQX服务器,在EMQX服务器中,用户可以自行订阅主题和发布主题,小程序通过MQTT通信协议订阅EMQX服务器的主题,服务器通过接收到硬件发布的数据,转发到微信小程序。

leida_3669的博客 2218

Android app图标显示未读消息数

参考:http://dev.xiaomi.com/doc/p=3904/index.html MIUI 6上重新设计了桌面app图标的角标显示,基本规则如下: 基本介绍 默认的情况 当app 向通知栏发送了一条通知 (通知不带进度条并且用户可以删除的),那么桌面app icon角标就会显示1.此时app显示的角标数是和通知栏里app发送的通知数对应的,即向通知栏发送了多少通知就会显示多少角标。 通知可以定义角标数 例如 有5封未读邮件,通知栏里只会显示一条通知,但是想让角标显示5. 可以在发通知时

m0_66070175的博客 2280

USB之ch376s数据手册

         最近师兄交给我一个项目,虽说刚学嵌入式起步,但上手一个项目怎说都是最好的快速学习方式。因为刚了解ch376s管理U盘的数据,所以先是看了ch376s的数据手册。那我就把数据手册直接放上来吧。。。。      1、概述 CH376 是文件管理控制芯片,用于单片机系统读写 U 盘或者 SD 卡中的文件。 CH376 支持 USB 设备方式和 USB 主机方式,并且内置了 USB 通...

iBGG的博客 6004

android 未读短信app,android应用icon右上角未读消息数字提示

1.此功能是指应用图标展示的数字,例如未读短信,邮件,类似iOS应用icon原生Android系统并不支持此功能定制的Samsung,sony支持此功能,有各自对应不同的实现方式,以三星为例1.在AndroidManifest.xml中加上两个权限:2.查看你的APP是否已经注册在BadgeProvider中:Uri uri = Uri.parse("content://com.sec.badge...

weixin_36387104的博客 947

GNSS气象学入门:从ZTD到PWV的水汽反演全流程解析(含精度评估)

本文详细解析了GNSS气象学中从ZTD到PWV的水汽反演全流程,包括基础概念、反演原理、数据处理步骤及精度评估方法。通过PRIDE软件和MATLAB代码示例,展示了如何高效提取ZTD数据并结合ERA5气象数据进行PWV计算,为气象研究和业务应用提供了实用指南。

weixin_28707365的博客 573

uniapp微信小程序 - 解决苹果ios底部输入框被软键盘遮挡的问题,input/textarea底部输入框被弹出的软键盘挡住部分或全部遮住(解决uniApp小程序底部输入框,键盘弹起时页面整体上移

uniapp: IOS微信小程序输入框部分被软键盘遮挡问题,iOS微信小程序键盘弹出时输入框被挡住,uniapp小程序苹果IOS系统软键盘唤起导致底部输入框被遮挡的问题,ios 键盘弹出input输入框被遮挡,uniapp textarea输入框软键盘遮挡问题,苹果手机ios系统,解决uni-app微信小程序底部输入框,键盘弹起时页面整体上移 问题,关于手机端IOS系统微信中虚拟键盘遮挡input输入框问题的解决方案,uniapp使用输入框固定在底部键盘弹起把输入框挡住问题,安卓上没问题就苹果不行,仅ios

🏆 前端领域优质创作者 5594

MASA MAUI Plugin (七)应用通知角标(小红点)Android+iOS

背景MAUI的出现,赋予了广大Net开发者开发多平台应用的能力,MAUI 是Xamarin.Forms演变而来,但是相比Xamarin性能更好,可扩展性更强,结构更简单。但是MAUI对于平台相关的实现并不完整。所以MASA团队开展了一个实验性项目,意在对微软MAUI的补充和扩展项目地址https://github.com/BlazorComponent/MASA.Blazor/tree/featu...

dotNET跨平台 868

DeerFlow 系列教程 第十一篇 | Sandbox 沙箱与 Memory 记忆——安全执行与持久认知

用户请求│▼Lead Agent├── 系统提示词组装│ ├── <memory> 段 ← Memory 系统注入用户记忆│ ├── <skill_system> 段│ └── <subagent_system> 段│├── 工具调用│ ├── read_file ├── Sandbox 系统提供隔离执行│ ├── write_file├── 路径虚拟化 + 安全验证│└── 对话结束│▼│├── 过滤消息(去除工具调用中间步骤)├── 检测纠正/强化信号。

weixin_43674738的博客 161

在小米 三星 索尼 手机 :图标上显示数字(未读消息数)

在小米 三星 索尼 手机 :图标上显示数字(未读消息数):这部分代码,是从QQ5.0.apk中找的。 小米已经测试通过了, 三星和索尼的,由于没有相应的手机,没有测试,有的,可能修改一下代码(判断是什么手机的代码), 测试一下,可以在回复一下测试结果,谢谢 [color=red][b](注:在这里重点说一下Notifyer+此app却可以在android2.2+和以上系统都实现角标,从网...

{丸の子} 310

YOLOv11-seg改进系列 | 引入CVPR2022 MetaFormer,零参数池化Token混合+StarReLU MLP,精度涨不掉速

本文提出了一种改进YOLOv11分割任务的C3k2_PoolingFormer模块,通过引入MetaFormer架构和Pooling Token Mixer,在保持模型轻量化的同时增强全局感受野建模能力。核心创新包括:1)使用零参数的AvgPool3×3差值作为Token Mixer,实现高效邻域信息聚合和局部对比增强;2)采用StarReLU MLP替代传统激活函数,提升非线性表达能力。改进后模型仅增加6.1%参数量,却能显著提升分割精度,特别适用于需要长程依赖建模的实例分割任务。该设计为轻量级视觉模型提

qq_44224801的博客 431

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

上一篇: Android 6.0 运行时权限处理完全解析
下一篇: android String转Class 自定义推送跳转Activity或者接口控制动态跳转相应的Activity
评论 9
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值