小工具知识网

当前位置: 首页 > 行业知识 > Launcher3图标相关的修改记录【Android13版本】

行业知识

Launcher3图标相关的修改记录【Android13版本】

2026-11-06 10:10:52 来源:互联网转载

文章目录

    • 概要
    • 一、更换图标圆角样式
    • 二、替换图标
      • 2.1 图标被放大
      • 2.2 图标拖拽的时候被打回原型
      • 三、去掉”非自适应“图标默认的白色背景
        • 解决方案一:直接去掉白色背景
        • 方案二:放大前景图标,达到正好盖住白色背景
        • 四、图标增加遥控选中状态

          概要

          本文记录Launcher的图标修改涉及到的一些技术点,如有错误还请指正,欢迎大家一起探讨。

          例如:

          如何替换个别图标?替换后图标被放大怎么处理?

          如何更换图标圆角?

          如何去掉”非自适应“图标默认的白色背景?

          图标增加遥控选中状态

          替换图标(前)

          替换图标后:

          一、更换图标圆角样式

          修改文件:frameworks/base/core/res/res/values/config.xml

          "M50,0 L85,0 C90.42,0 100,4.58 100,15 L100,85 C100,90.42 96.42,100 85,100 L15,100 C10.58,100 0,96.42 0,85 L0,15 C0,10.42 4.42,0 15,0 L50,0 Z"

          找到config_icon_mask属性,修改其中的vector资源(M50,0 L85 …这一串)就能改成任意形状。具体大家可以在Android Studio中制作vector矢量图,或者网络搜一搜。

          二、替换图标

          修改的类:

          \packages\apps\Launcher3\src\com\android\launcher3\BubbleTextView.java

          BubbleTextView就是图标类,applyIconAndLabel方法中直接替换要更换图标。具体怎么调用到这里的直接在Android Studio中点击相关的方法就能找的调用链,这里就不展开了。

          @UiThread    protected void applyIconAndLabel(ItemInfoWithIcon info) {        boolean useTheme = mDisplay == DISPLAY_WORKSPACE || mDisplay == DISPLAY_FOLDER                || mDisplay == DISPLAY_TASKBAR;        int flags = useTheme ? FLAG_THEMED : 0;        if (mHideBadge) {            flags |= FLAG_NO_BADGE;        }        // 改变图标的样式 begin        BitmapInfo bitmapInfo =                 convertBitmapInfoSpecial(info.getTargetComponent().getPackageName());        if (bitmapInfo != null){            info.bitmap = bitmapInfo;        }        // 改变图标的样式 end        FastBitmapDrawable iconDrawable = info.newIcon(getContext(), flags);        mDotParams.appColor = iconDrawable.getIconColor();        mDotParams.dotColor = getContext().getResources()                .getColor(android.R.color.system_accent3_200, getContext().getTheme());        setIcon(iconDrawable);        applyLabel(info);    }/***  根据包名替换app图标*/public BitmapInfo convertBitmapInfoSpecial(String pckName){        if (pckName == null || "".equals(pckName)) return null;        if (customIconMap.containsKey(pckName)){            LauncherIcons li = LauncherIcons.obtain(mContext);            Drawable d = ContextCompat.getDrawable(mContext, customIconMap.get(pckName));            Bitmap bitmap = drawableToBitmap(d);            return li.createIconBitmap(bitmap);        }        return null;}// 静态代码块中初始话一个哈希表。最好写个公共类,创建一个静态的customIconMap 哈希表,这样各个想用的类都可以调用static{// 构造一个包名:资源名的哈希表customIconMap = new HashMap();    customIconMap.put("com.android.settings", com.android.launcher3.icons.R.drawable.ic_launcher_setting);   // 比如我们要替换设置应用的图标,在这里put进去其对应的图标资源}

          经过这样修改后,就能替换掉launcher中的图标

          2.1 图标被放大

          上面的简单替换可能导致图标被放大了,这里再介绍如何解决这个问题。

          核心就是convertBitmapInfoSpecial()方法中,LauncherIcons类是继承BaseIconFactory,因此li.createIconBitmap(bitmap); 其实用的是BaseIconFactory类中的createIconBitmap()

          来看BaseIconFactory#createIconBitmap(),代码如下

          // BaseIconFactory.javapublic BitmapInfo createIconBitmap(Bitmap icon) {        if (this.mIconBitmapSize != icon.getWidth() || this.mIconBitmapSize != icon.getHeight()) {            icon = this.createIconBitmap(new BitmapDrawable(this.mContext.getResources(), icon), 1.0F);    // 这里的1.0f太大了导致的        }       return BitmapInfo.of(icon, this.mColorExtractor.findDominantColorByHue(icon));}

          因此我们在LauncherIcons中重写该方法:

          修改的类:

          \packages\apps\Launcher3\src\com\android\launcher3\icons\LauncherIcons.java

          // LauncherIcons.java@Override    public BitmapInfo createIconBitmap(Bitmap icon) {        if (this.mIconBitmapSize != icon.getWidth() || this.mIconBitmapSize != icon.getHeight()) {            icon = this.createIconBitmap(new BitmapDrawable(this.mContext.getResources(), icon), 0.8F);    // 缩小点,否则换了图标之后会被放大        }        return BitmapInfo.of(icon, this.mColorExtractor.findDominantColorByHue(icon));    }

          经过重写LauncherIcons#createIconBitmap()后,就能解决替换后的图标被放大问题。当然这个不是唯一的方案,可能还有其他的方法。

          2.2 图标拖拽的时候被打回原型

          关于拖拽的简单介绍,大家可以看下这篇文章:Launcher3拖拽分析(一)——Android13版本

          其中拖拽的时候,会创建一个DragView。

          DragView:BubbTextView的平替(他们携带的信息是一样的),因为BubbTextView的父布局是ShortcutAndWidgetContainer,如果拖拽到另一个ShortcutAndWidgetContainer是不允许的(跨布局了)。所以创造了一个DragView来代替BubbTextView

          因此,在上面我们只是单纯的改了BubbleTextView中的图标内容,还要改下DragView的图标。

          具体看DragView#setItemInfo() 方法:

          public void setItemInfo(final ItemInfo info) {        if (info.itemType != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION                && info.itemType != LauncherSettings.Favorites.ITEM_TYPE_SEARCH_ACTION                && info.itemType != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT                && info.itemType != LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {            return;        }        // Load the adaptive icon on a background thread and add the view in ui thread.        MODEL_EXECUTOR.getHandler().postAtFrontOfQueue(() -> {            Object[] outObj = new Object[1];            int w = mWidth;            int h = mHeight;            Drawable dr = Utilities.getFullDrawable(mActivity, info, w, h,                    true /* shouldThemeIcon */, outObj);    // 关键就是这个                                        ......                    

          关键就是看 Drawable dr = Utilities.getFullDrawable()这个方法调用了啥,根据调用链依次追踪,最后发现这个

          Utilities#loadFullDrawableWithoutTheme()

          修改的类:

          \packages\apps\Launcher3\src\com\android\launcher3\Utilities.java

          private static Drawable loadFullDrawableWithoutTheme(Context context, ItemInfo info,            int width, int height, Object[] outObj) {        ActivityContext activity = ActivityContext.lookupContext(context);        LauncherAppState appState = LauncherAppState.getInstance(context);// 如果是应用图标。走这里if代码块        if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {            LauncherActivityInfo activityInfo = context.getSystemService(LauncherApps.class)                    .resolveActivity(info.getIntent(), info.user);            outObj[0] = activityInfo;                       return activityInfo == null ? null : LauncherAppState.getInstance(context)                    .getIconProvider().getIcon(                            activityInfo, activity.getDeviceProfile().inv.fillResIconDpi);         }......}

          因此,我们还要根据特定的包名,重新返回一个新的Drawable对象,上修改后的代码

          private static Drawable loadFullDrawableWithoutTheme(Context context, ItemInfo info,            int width, int height, Object[] outObj) {        ActivityContext activity = ActivityContext.lookupContext(context);        LauncherAppState appState = LauncherAppState.getInstance(context);        if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {            LauncherActivityInfo activityInfo = context.getSystemService(LauncherApps.class)                    .resolveActivity(info.getIntent(), info.user);            outObj[0] = activityInfo;            Drawable drawableSpecial = getDrawableSpecial(info.getTargetComponent().getPackageName());            if (drawableSpecial == null){                return activityInfo == null ? null : LauncherAppState.getInstance(context)                        .getIconProvider().getIcon(                                activityInfo, activity.getDeviceProfile().inv.fillResIconDpi);            }else{                return drawableSpecial;            }        }        ......}public Drawable getDrawableSpecial(String pckName){        if (pckName == null || "".equals(pckName)) return null;        if (customIconMap.containsKey(pckName)){      // customIconMap 最好弄成个静态的放在公共的类中,供大家调用            return ContextCompat.getDrawable(mContext, customIconMap.get(pckName));        }        return null;}

          经过上面的修改后,就能解决拖拽的时候,被替换的图标又打回原型的问题。

          三、去掉”非自适应“图标默认的白色背景

          自适应图标介绍与制作,参看这篇文章:Android应用制作自适应图标

          如果第三方app开发者,并未适配自适应icon图标,则安装后系统会默认添加一个白色背景。

          例如:

          解决方案一:直接去掉白色背景

          修改的类:

          \frameworks\libs\systemui\iconloaderlib\src\com\android\launcher3\icons\BaseIconFactory.java注意这个类是Framework中的源码。

          主要就是normalizeAndWrapToAdaptiveIcon方法中,有对是否自适应图标的判断。如果不是自适应图标就要添加背景,同时修改scale;否则直接取出图标其scale来用。 因此直接屏蔽if、else判断,直接取图标本身的scale即可

          protected Drawable normalizeAndWrapToAdaptiveIcon(@Nullable Drawable icon,            final boolean shrinkNonAdaptiveIcons, @Nullable final RectF outIconBounds,            @NonNull final float[] outScale) {        if (icon == null) {            return null;        }        float scale = 1f;        //  1、原本是if.else.结构 把其注释掉        /* begin 注释 */        if (shrinkNonAdaptiveIcons && !(icon instanceof AdaptiveIconDrawable)) {            if (mWrapperIcon == null) {                mWrapperIcon = mContext.getDrawable(R.drawable.adaptive_icon_drawable_wrapper)                        .mutate();            }            AdaptiveIconDrawable dr = (AdaptiveIconDrawable) mWrapperIcon;            dr.setBounds(0, 0, 1, 1);            boolean[] outShape = new boolean[1];            scale = getNormalizer().getScale(icon, outIconBounds, dr.getIconMask(), outShape);            if (!outShape[0]) {                FixedScaleDrawable fsd = ((FixedScaleDrawable) dr.getForeground());                fsd.setDrawable(icon);                fsd.setScale(scale);                icon = dr;                scale = getNormalizer().getScale(icon, outIconBounds, null, null);                ((ColorDrawable) dr.getBackground()).setColor(mWrapperBackgroundColor);            }        } else {            scale = getNormalizer().getScale(icon, outIconBounds, null, null);        }          /* end 注释 */          // 2、打开这个注释,就能去掉图像的白边        // scale = getNormalizer().getScale(icon, outIconBounds, null, null);        outScale[0] = scale;        return icon;    }

          此方案会误伤不规则图标:即有些icon本身不是方形的,而是不规则图形,不规则图标本来就要有个白色背景。

          方案二:放大前景图标,达到正好盖住白色背景

          既然对于不规则图标,白色背景有存在的必要,且规则图标又不想要白色背景,只有放大前景图标的大小了。

          效果如下:

          修改的类:

          \frameworks\libs\systemui\iconloaderlib\src\com\android\launcher3\icons\FixedScaleDrawable.java

          public class FixedScaleDrawable extends DrawableWrapper {    // TODO b/33553066 use the constant defined in MaskableIconDrawable    // private static final float LEGACY_ICON_SCALE = .7f * .6667f;    private static final float LEGACY_ICON_SCALE = 0.86f;     // 这个静态变量就是控制前景图标缩放比例的,我这里设置成0.86正好能让前景图标盖住白色背景    private float mScaleX, mScaleY;    public FixedScaleDrawable() {        super(new ColorDrawable());        mScaleX = LEGACY_ICON_SCALE;        mScaleY = LEGACY_ICON_SCALE;    }......}

          四、图标增加遥控选中状态

          修改的类:

          \packages\apps\Launcher3\src\com\android\launcher3\BubbleTextView.java

          BubbleTextView就是图标的载体(包含一个icon图片、对应的名字)。增加选中背景就是新建一个背景资源文件,同时在BubbleTextView的构造方法中setBackgroundResource()中设置这个资源文件即可

          public BubbleTextView(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);......setBackgroundResource(R.drawable.select_background);     // 增加这一句}

          新建资源文件 \packages\apps\Launcher3\res\drawable\select_background.xml

                                                                                                          

上一篇:揭秘转转麻将源码,核心机制大解密!

下一篇:小米怎么双开游戏服务器?