Launcher3图标替换不生效问题

问题

  • 屏幕为720p,改系统设置应用源码,替换了 mipmap-xhdpi 目录下的 ic_launcher_settings.png,发现在应用信息中图标变成新的了,但是Launcher3中却没有任何变化。

尝试

  • 首先认为 Launcher3 中对设置应用进行了特殊图标替换,因此在res目录下查找,发现没有原设置图标。
  • 是否不是 Launcher3 的特殊设置呢?安装 Launcher2 应用验证一下,发现Launcher2显示了新图标,因此问题还是在 Launcher3中。
  • 只替换了 mipmap-xhdpi 目录的图标,是否Launcher3采用了别的分辨率的图标?查看了源码, 最终确认了问题所在。

原因

  • 首先,看LauncherModel的初始化
// LauncherModel.java
private void loadAllApps() {
  for (int i = 0; i < apps.size(); i++) {
    LauncherActivityInfoCompat app = apps.get(i);
    // 这里创建了icon信息
    mBgAllAppsList.add(new AppInfo(mContext, app, user, mIconCache));
  }
}
// AppInfo.java
public AppInfo(Context context, LauncherActivityInfoCompat info, UserHandleCompat user, IconCache iconCache) {
  this.componentName = info.getComponentName();
  this.container = ItemInfo.NO_ID;

  flags = initFlags(info);
  firstInstallTime = info.getFirstInstallTime();
  // 采用图标缓存器获取应用名和图标
  iconCache.getTitleAndIcon(this, info, true /* useLowResIcon */);
  intent = makeLaunchIntent(context, info, user);
  this.user = user;
}
  • 再看图标缓存器
// IconCache.java
public synchronized void getTitleAndIcon(AppInfo application,
               LauncherActivityInfoCompat info, boolean useLowResIcon) {
  UserHandleCompat user = info == null ? application.user : info.getUser();
  // 很熟悉的cacheLocked方法
  CacheEntry entry = cacheLocked(application.componentName, info, user,
                                 false, useLowResIcon, application.unreadNum);
  application.title = Utilities.trim(entry.title);
  application.iconBitmap = getNonNullIcon(entry, user);
  application.contentDescription = entry.contentDescription;
  application.usingLowResIcon = entry.isLowResIcon;
}

private CacheEntry cacheLocked(ComponentName componentName, LauncherActivityInfoCompat info,
               UserHandleCompat user, boolean usePackageIcon, boolean useLowResIcon, int unreadNum) {
  if (entry == null || (entry.isLowResIcon && !useLowResIcon)) {
    entry = new CacheEntry();
    mCache.put(cacheKey, entry);
    
    if (!getEntryFromDB(cacheKey, entry, useLowResIcon)) {
      if (info != null) {
        // 这里通过LauncherActivityInfoCompat的getBadgedIcon方法创建bitmap
        // 注意传入的mIconDpi是图标的dpi
        entry.icon = Utilities.createIconBitmap(info.getBadgedIcon(mIconDpi), mContext);
      } else {
      
      }
    }
  }
}
// LauncherActivityInfoCompatVL.java
public class LauncherActivityInfoCompatVL extends LauncherActivityInfoCompat {
  public Drawable getBadgedIcon(int density) {
    // 这里调用了框架层的LauncherActivityInfo.java的getBadgedIcon方法
    return mLauncherActivityInfo.getBadgedIcon(density);
  }
}
  • 到此,我们可以知道关键点在这个图标的dpi上
// IconCache.java
public IconCache(Context context, InvariantDeviceProfile inv) {
  iconSize = interpolatedDeviceProfileOut.iconSize;
  iconBitmapSize = Utilities.pxFromDp(iconSize, dm);
  // InvariantDeviceProfile的fillResIconDpi成员
  mIconDpi = inv.fillResIconDpi;
}
// InvariantDeviceProfile.java
InvariantDeviceProfile(Context context) {
  fillResIconDpi = getLauncherIconDensity(iconBitmapSize);
}
// 最终核心方法,根据Launcher3需要的图标大小来计算需要取用哪个dpi的图标
private int getLauncherIconDensity(int requiredSize) {
  int[] densityBuckets = new int[] {
    DisplayMetrics.DENSITY_LOW,
      DisplayMetrics.DENSITY_MEDIUM,
      DisplayMetrics.DENSITY_TV,
      DisplayMetrics.DENSITY_HIGH,
      DisplayMetrics.DENSITY_XHIGH,
      DisplayMetrics.DENSITY_XXHIGH,
      DisplayMetrics.DENSITY_XXXHIGH
  };

  int density = DisplayMetrics.DENSITY_XXXHIGH;
  for (int i = densityBuckets.length - 1; i >= 0; i--) {
    float expectedSize = ICON_SIZE_DEFINED_IN_APP_DP * densityBuckets[i]
      / DisplayMetrics.DENSITY_DEFAULT;
    if (expectedSize >= requiredSize) {
      density = densityBuckets[i];
    }
  }

  return density;
}

结论

  • 我们可以得出 Launcher3 会根据需求,取用不同dpi的图标,意在节约资源吧。
  • 因此我们在替换图标的时候,有条件的话,UI出图标各个分辨率的都出,全部替换;没有这个条件,就留下1个我们需要dpi图标,余下的删掉就好了。

下面这个随便看看

石器时代

你可能感兴趣的:(android)