切换图标形状,通知小圆点位置异常

时间:2021/04/08
之前公司不允许csdn,笔记写在其它地方。最近整理过来

背景描述:

展讯平台R版本homesettings中可以切换图标形状,多次切换之后,图标小圆点位置异常

问题分析:

切换形状时 , 小圆点需要根据当前图标形状进行调整位置 , 查看相关逻辑是否异常

分析一下图标形状获取 , 设置 , 调整小圆点位置代码:

1、图标形状菜单(获取内容、设置菜单、点击监听和处理)

//porting change icon sharp
static final String PACKAGE_DEVICE_DEFAULT = "package_device_default";
private static final Comparator<OverlayInfo> OVERLAY_INFO_COMPARATOR = Comparator.comparingInt(a -> a.priority);
private static String TAG = "iconsharp";
private final IOverlayManager mOverlayManager;
private final PackageManager mPackageManager;
private String mCategory = "android.theme.customization.adaptive_icon_shape";
private static final String OVERLAY_TARGET_PACKAGE = "android";
private ListPreference mPreference;
private Context mContext;

//获取图标形状
//返回值类型 list OverlayInfo : frameworks/base/core/java/android/content/om/OverlayInfo.java
private List<OverlayInfo> getOverlayInfos() {
    final List<OverlayInfo> filteredInfos = new ArrayList<>();
    try {
        List<OverlayInfo> overlayInfos = mOverlayManager.getOverlayInfosForTarget(OVERLAY_TARGET_PACKAGE, USER_SYSTEM);
        for (OverlayInfo overlayInfo : overlayInfos) {
            if (mCategory.equals(overlayInfo.category)) {
                filteredInfos.add(overlayInfo);
            }
        }
    } catch (RemoteException re) {
        throw re.rethrowFromSystemServer();
    }
    filteredInfos.sort(OVERLAY_INFO_COMPARATOR);
    return filteredInfos;
}
//填充菜单
public void populatePreference(Preference preference) {
    final List<String> pkgs = new ArrayList<>();
    final List<String> labels = new ArrayList<>();
    //添加默认形状的包名和文字显示
    String selectedPkg = PACKAGE_DEVICE_DEFAULT;
    String selectedLabel = mContext.getString(R.string.overlay_option_device_default);

    // Add the default package / label before all of the overlays
    pkgs.add(selectedPkg);
    labels.add(selectedLabel);
    //查询系统支持的图标形状 , 拿到apk的包名和应用名称
    //循环列表 把包名和应用名称添加到列表中
    for (OverlayInfo overlayInfo : getOverlayInfos()) {
        pkgs.add(overlayInfo.packageName);
        try {
            labels.add(mPackageManager.getApplicationInfo(overlayInfo.packageName, 0)
                    .loadLabel(mPackageManager).toString());
        } catch (PackageManager.NameNotFoundException e) {
            labels.add(overlayInfo.packageName);
        }
        if (overlayInfo.isEnabled()) {
            selectedPkg = pkgs.get(pkgs.size() - 1);
            selectedLabel = labels.get(labels.size() - 1);
        }
    }
    //菜单设置内容,ListPreference的使用
    mPreference.setEntries(labels.toArray(new String[labels.size()]));
    mPreference.setEntryValues(pkgs.toArray(new String[pkgs.size()]));
    mPreference.setValue(selectedPkg);
    mPreference.setSummary(selectedLabel);
    //这只item点击监听
    mPreference.setOnPreferenceChangeListener((pref, newValue) -> {
            setOverlay((String) newValue);
            return true;
        });
}
//end

//设置图标形状, 需要传入图标形状apk的包名
private boolean setOverlay(String packageName) {
    final String currentPackageName = getOverlayInfos().stream()
            .filter(info -> info.isEnabled())
            .map(info -> info.packageName)
            .findFirst()
            .orElse(null);
    if (PACKAGE_DEVICE_DEFAULT.equals(packageName) && TextUtils.isEmpty(currentPackageName)
            || TextUtils.equals(packageName, currentPackageName)) {
        // Already set.
        return true;
    }
    new AsyncTask<Void, Void, Boolean>() {
        @Override
        protected Boolean doInBackground(Void... params) {
            try {
                //设置默认形状或者其它包名的图标形状
                if (PACKAGE_DEVICE_DEFAULT.equals(packageName)) {
                    return mOverlayManager.setEnabled(currentPackageName, false, USER_SYSTEM);
                } else {
                    return mOverlayManager.setEnabledExclusiveInCategory(packageName,
                            USER_SYSTEM);
                }
            } catch (RemoteException re) {
                Log.w(TAG, "Error enabling overlay.", re);
                return false;
            }
        }
        @Override
        protected void onPostExecute(Boolean success) {
            populatePreference(mPreference);
            if (!success) {
                Toast.makeText(
                        mContext, R.string.overlay_toast_failed_to_apply, Toast.LENGTH_LONG)
                        .show();
            }
        }
    }.execute();
    return true; // Assume success; toast on failure.
}
2、当修改图标形状后,launcher如何修改图标形状
packages/apps/Launcher3/src/com/android/launcher3/InvariantDeviceProfile.java中
//注册广播接收器监听广播,并调用onConfigChange方法
private class OverlayMonitor extends BroadcastReceiver {
    private final String ACTION_OVERLAY_CHANGED = "android.intent.action.OVERLAY_CHANGED";
    OverlayMonitor(Context context) {
        context.registerReceiver(this, getPackageFilter("android", ACTION_OVERLAY_CHANGED));
    }
    @Override
    public void onReceive(Context context, Intent intent) {
        onConfigChanged(context);
    }
}

private void onConfigChanged(Context context) {
    // Config changes, what shall we do?
    InvariantDeviceProfile oldProfile = new InvariantDeviceProfile(this);
    //初始化桌面网格
    if (mMonitor.getDesktopGridController() != null) {
        // Re-init grid by using the new grid name
        initGrid(context,
                Utilities.getPrefs(context).getString(mIdpGridKey, getDefaultGridName(context)));
    } else {
        // TODO(b/131867841): We pass in null here so that we can calculate the closest profile
        // without the bias of the grid name.
        initGrid(context, null);
    }
    //判断桌面行列数是否有改变
    int changeFlags = 0;
    if (numRows != oldProfile.numRows ||
            numColumns != oldProfile.numColumns ||
            numFolderColumns != oldProfile.numFolderColumns ||
            numFolderRows != oldProfile.numFolderRows ||
            numHotseatIcons != oldProfile.numHotseatIcons) {
        changeFlags |= CHANGE_FLAG_GRID;
    }
    //图标大小对比
    if (iconSize != oldProfile.iconSize || iconBitmapSize != oldProfile.iconBitmapSize ||
            !iconShapePath.equals(oldProfile.iconShapePath)) {
        changeFlags |= CHANGE_FLAG_ICON_PARAMS;
    }
    //图标形状对比
    if (!iconShapePath.equals(oldProfile.iconShapePath)) {
        //图标形状发生变化,初始化选择最合适的轨迹
        IconShape.init(context);
    }

    apply(context, changeFlags);
}
private String initGrid(Context context, String gridName) {
    ...
    //忽略掉其它代码,主要看图标形状相关的初始化
    landscapeProfile = new DeviceProfile(context, this, smallestSize, largestSize,
            largeSide, smallSide, true /* isLandscape */, false /* isMultiWindowMode */);
    portraitProfile = new DeviceProfile(context, this, smallestSize, largestSize,
            smallSide, largeSide, false /* isLandscape */, false /* isMultiWindowMode */);
    ...
    return closestProfile.name;
}

packages/apps/Launcher3/src/com/android/launcher3/DeviceProfile.java的构造方法
public DeviceProfile(Context context, InvariantDeviceProfile inv,
        Point minSize, Point maxSize,
        int width, int height, boolean isLandscape, boolean isMultiWindowMode) {
    ...
    // This is done last, after iconSizePx is calculated above.
    mDotRenderer = new DotRenderer(iconSizePx, IconShape.getShapePath(),
            IconShape.DEFAULT_PATH_SIZE);
}

packages/apps/Launcher3/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java
//这个类负责计算图标形状和小圆点坐标 , 画小圆点
public DotRenderer(int iconSizePx, Path iconShapePath, int pathSize) {
    int size = Math.round(SIZE_PERCENTAGE * iconSizePx);
    ShadowGenerator.Builder builder = new ShadowGenerator.Builder(Color.TRANSPARENT);
    builder.ambientShadowAlpha = 88;
    mBackgroundWithShadow = builder.setupBlurForSize(size).createPill(size, size);
    mCircleRadius = builder.radius;

    mBitmapOffset = -mBackgroundWithShadow.getHeight() * 0.5f; // Same as width.
    //根据传入的图标形状计算左边小圆点和右边小圆点的坐标
    //通俗的讲 , 就是根据图标形状轨迹 计算出 左上角和右上角的点
    //画小圆点的最佳点
    // Find the points on the path that are closest to the top left and right corners.
    mLeftDotPosition = getPathPoint(iconShapePath, pathSize, -1);
    mRightDotPosition = getPathPoint(iconShapePath, pathSize, 1);
}
轨迹转换成点  draw 这里先不讲主要讲一下问题原因

设置图标形状之后 , 计算图形坐标 , 再找到最适合的形状 逻辑顺序不对

DotRenderer的初始化应该在IconShape.init之后
3、修改方案:

为了不影响之前的逻辑,我在IconShape.init之后再次initDotRenderer
修正了角标的位置

packages/apps/Launcher3/src/com/android/launcher3/InvariantDeviceProfile.java中
IconShape.init(context);
+            landscapeProfile.initDotRenderer();
+            portraitProfile.initDotRenderer();
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值