# Launcher90 新安装应用title 提示---小圆点
### 前言:
纵观市面上的华为、小米、vivo手机 在桌面或者文件夹中有新安装应用时会在title左边显示一个小圆点(颜色主流为绿色)从而提示用户此应用为新安装的app,可能有以下原因吧: 有一定的导流作用 促使用户去打开此新安装的应用,从UI的角度 页面较为美观吧。
先看下效果图:
### 方案和问题:
在sugar版本上可以看到实现方案为在icon的右上角绘制一个小圆点 表示新安装应用,在Icon上面绘制圆点好处为,iconsize 相同,画的小圆点类似角标可以准确确定位置 基本适用于所有应用。不会有位置偏移变化。
现在改为在新安装的应用title前面添加小圆点 提示,由于每个应用title长短不一,需要动态计算title长度 使小圆点等距离的美观显示在桌面上。看了小米的显示效果为 小圆点距离title的第一个字距离都相同,这样才美观。那么在draw 这个小圆点时 就需要动态计算title首字母的坐标了,这样才能根据不同的title动态等距离的显示小圆点,美观的展示给用户。
先看下所有APP 调试效果图。
会根据每个应用的title前面显示一个小圆点,距离需要等宽。
### 具体实现方案:
在新安装APP的title显示小圆点在BubbleTextView.java 类中实现,需要在桌面添加创建此icon shortcut时 增加判断是否是新安装应用。
首先看下shortcut 实现,
代码位置:Z:\work\Launcher90\src\com\android\launcher3\BubbleTextView.java
由于桌面文件夹和deep shortcut hotset都是继承BubbleTextView.java 所以在创建是需增加判断过滤。
protected void drawRemindIcon(Canvas canvas) {
if (getTag() != null && getTag() instanceof ShortcutInfo) {
ShortcutInfo info = (ShortcutInfo) getTag();
DeviceProfile grid = mActivity.getDeviceProfile();
int paddingTop = mIconSize;
int paddingleft = mOffsetRemindLeft;
int position = 0;
Layout layout = getLayout();
Rect bound = new Rect();
int line = layout.getLineForOffset(position);
layout.getLineBounds(line, bound);
int yAxisTop = bound.top;//字符顶部y坐标
int yAxisBottom = bound.bottom;//字符底部y坐标
float xAxisLeft = layout.getPrimaryHorizontal(position);//字符左边x坐标
float xAxisRight = layout.getSecondaryHorizontal(position);//字符右边x坐标
if (mDisplay == DISPLAY_WORKSPACE) {
paddingleft = (int) xAxisLeft;
paddingTop = mIconSize + (yAxisBottom - yAxisTop) / 2 + 2 * grid.iconDrawablePaddingPx;
} else if (mDisplay == DISPLAY_FOLDER) {
paddingleft = (int) xAxisLeft - mOffsetRemindLeft;
paddingTop = mIconSize + (yAxisBottom - yAxisTop) / 4 + grid.iconDrawablePaddingPx/2;
} else if (mDisplay == DISPLAY_SHORTCUT) {
isShowRemind = false;
} else if (mDisplay == DISPLAY_WIDGET) {
isShowRemind = false;
}
LogUtil.d(TAG, "yAxisTop:" + yAxisTop + "yAxisBottom" + yAxisBottom + "xAxisLeft:" + xAxisLeft + "xAxisRight" + xAxisRight + "grid.iconDrawablePaddingPx" + grid.iconDrawablePaddingPx);
if (info.remind && isShowRemind && shouldTextBeVisible()) {
canvas.save();
canvas.drawBitmap(LauncherAppState.getInstance(mContext).getBitmapAndDrawableCache().getRemindIcon(), paddingleft, paddingTop, null);
canvas.restore();
}
}
}
此段会增加判断只在桌面快捷方式 文件夹内部显示小圆点 ,文件夹前面的需要后面特殊处理。
主要用到 Layout layout = getLayout();
Rect bound = new Rect();
int line = layout.getLineForOffset(position);
layout.getLineBounds(line, bound);
int yAxisTop = bound.top;//字符顶部y坐标
int yAxisBottom = bound.bottom;//字符底部y坐标
float xAxisLeft = layout.getPrimaryHorizontal(position);//字符左边x坐标
float xAxisRight = layout.getSecondaryHorizontal(position);//字符右边x坐标
可以动态计算title最前面字符的左边,有了它就可以canvas.drawBitmap 了。
快捷方式实现了。
接下来看下文件夹的实现方式,文件夹需要特殊处理,要根据folder 内部是否有新安装应用,如果folder内部有新安装应用,在打开文件夹是显示在应用前面,当关闭文件夹时会显示子文件夹title的前面(由于文件夹name 是可以自己定义的需要动态计算显示在前面);
先看下调试效果。
源码位置:
Z:\work\Launcher90\src\com\android\launcher3\folder\FolderIcon.java
在dispatchDraw(Canvas canvas)方法中循环遍历下folder内部是否有新安装应用。
int count = mInfo.contents.size();
boolean needremind = false;
for (int i=0; i ShortcutInfo info = mInfo.contents.get(i); if (CustomConfigManager.getInstance().supportFolderIconUnread) { if (info.unreadNum > 0) { needremind = false; break; } } if (info.remind) { needremind = true; if (CustomConfigManager.getInstance().supportFolderIconUnread) { continue; }else { break; } } } if (needremind) { drawFolderRemind(canvas); } private void drawFolderRemind(Canvas canvas) { canvas.save(); int textureWidth = this.getWidth(); int paddingTop = this.getPaddingTop(); int paddingleft = mOffsetRemindLeft; DeviceProfile grid = mLauncher.getDeviceProfile(); DisplayMetrics dm = mLauncher.getResources().getDisplayMetrics(); int position = 0; if (mFolderName == null) { return; } Layout layout = mFolderName.getLayout(); Rect bound = new Rect(); int line = layout.getLineForOffset(position); layout.getLineBounds(line, bound); int yAxisTop = bound.top;//字符顶部y坐标 int xAxisCenter = bound.centerX();//字符顶部y坐标 int yAxisBottom = bound.bottom;//字符底部y坐标 float xAxisLeft = layout.getPrimaryHorizontal(position);//字符左边x坐标 float xAxisRight = layout.getSecondaryHorizontal(position);//字符右边x坐标 paddingTop = grid.iconSizePx + (yAxisBottom - yAxisTop) / 2 + 2 * grid.iconDrawablePaddingPx; paddingleft = textureWidth/2 - (xAxisCenter - (int) xAxisLeft)-mOffsetRemindLeft; canvas.drawBitmap(LauncherAppState.getInstance(mLauncher).getBitmapAndDrawableCache().getRemindIcon(), paddingleft, paddingTop, null); canvas.restore(); } 和shortcut 创建流程基本类似 需要计算文件夹name左边,在调试过程中 发现 文件夹的name首字母坐标不是left值 需要this.getWidth()/2 减去 name中心坐标和首字母坐标,会随首字母坐标变left变小. 到此显示布局逻辑基本实现了接下来需要控制判断哪些是新安装的应用才显示。 ### 如何判断新安装应用: 源码位置: Z:\work\Launcher90\src\com\android\launcher3\Workspace.java Z:\work\Launcher90\src\com\ape\launcher3\MyosLauncher.java 本次实现方案没有保存在数据库中,在监听新安装应用时 保存此应用的包名到sp, 然后再luncher onresume 或者 public void finishBindingItems() 分别刷新下。 public void refreshRemindIcon() { LogUtil.d(TAG, "refreshRemindIcon()"); Set final HashMap LogUtil.d(TAG, "remindMap"); for (String s : set) { int state = DownloadRemind.isRemindApp(MyosLauncher.this, s) ? 0 : 1; LogUtil.d(TAG, "refreshRemindIcon : packageName = " + s + ", state = " + state); remindMap.put(s, state); } mHandler.post(new Runnable() { public void run() { if (mWorkspace != null) { mWorkspace.updateShortcutsAndFoldersRemind(remindMap); } } }); } 刷新遍历 ShortcutInfo 、FolderInfo、Hotseat是否包含在sp中的包名remind info 信息。 public void updateShortcutsAndFoldersRemind(HashMap final ArrayList int childCount = 0; View view = null; Object tag = null; for (ShortcutAndWidgetContainer layout : childrenLayouts) { childCount = layout.getChildCount(); for (int j = 0; j < childCount; j++) { view = layout.getChildAt(j); tag = view.getTag(); if (tag instanceof ShortcutInfo) { final ShortcutInfo info = (ShortcutInfo) tag; final Intent intent = info.intent; final ComponentName componentName = intent.getComponent(); if (componentName != null && componentName.getPackageName() != null && remindMap.containsKey(componentName.getPackageName())) { ////this is remind app if (remindMap.get(componentName.getPackageName()) == 0) { info.remind = true; } else { info.remind = false; } } ((BubbleTextView)view).invalidate(); //((BubbleTextView) view).mFavorite.invalidate(); } else if (tag instanceof FolderInfo) { ((FolderIcon) view).updateFolderRemind(remindMap); ((FolderIcon) view).invalidate(); } } } mLauncher.getHotseat().updateRemindApps(remindMap); } ### title圆点如何消失: 目前处理 判别应用是否进入新应用,为从桌面点击 startactivity 即认为不是新应用了需要移除标志位。 public boolean startActivitySafely(View v, Intent intent, ItemInfo item) { boolean success = super.startActivitySafely(v, intent, item); if (success && v instanceof BubbleTextView) { // This is set to the view that launched the activity that navigated the user away // from launcher. Since there is no callback for when the activity has finished // launching, enable the press state and keep this reference to reset the press // state when we return to launcher. BubbleTextView btv = (BubbleTextView) v; btv.setStayPressed(true); setOnResumeCallback(btv); if (DownloadRemind.isRemindApp(this, intent.getComponent().getPackageName())) { DownloadRemind.setDownloadAppsRemind(this, intent.getComponent().getPackageName(), false); } LogUtil.d(TAG, "startActivitySafely success:" + success + "PackageName:" + intent.getComponent().getPackageName()); } return success; } 设置对应的包名 value 为false; 以上为新安装应用的title 小圆点draw位置、显示、和消失基本逻辑 方案。方法很多如有好的更优的方法 欢迎指正。