[Android系统开发]Launcher Hotseat图标居中排列
目录
- 背景
- 问题点
- 解决过程
- 完整代码
- 遗留问题
- 1.初始化刷新问题。
- 2.图标覆盖问题。
- 后续思考
之前接手一个和Hotseat自动排列相关的Bug,本身实现方案是参考博文 Hotseat 自动排列 的,但是在实现之后出现将Hotseat当中的图标移除后有一定概率使两个图标重叠的Bug。在此记录一下解决过程。
背景
整体项目基于原生Android 9.0
Launcher Hotseat自动排列方案参考博文 Hotseat 自动排列
竖屏状态Hotseat在底部,横屏状态Hotseat在右边
问题点
将Hotseat的“电话”拖拽至CellLayout当中,再从文件夹当中拖拽“相机”至Hotseat当中时,Hotseat不会提示“相机”应该放在第5个位置,而是直接覆盖在“信息”上面。
解决过程
通过Android Device Monitor DDMS显示两个图标的坐标重叠,确定图标依然存在,并未消失。
了解Launcher图标放置流程。
在Workspace.java->onDrop当中,有着findNearestArea方法的调用,溯源至CellLayout.java->findNearestArea,其中有一段代码:
// First, let's see if this thing fits anywhere for (int i = 0; i < minSpanX; i++) {for (int j = 0; j < minSpanY; j++) {if (mOccupied.cells[x + i][y + j]) {continue inner;}} }
在CellLayout当中正好有着markCellsAsOccupiedForView以及markCellsAsUnoccupiedForView两个方法用来给mOccupied进行cell的占用状态标记。
public void markCellsAsOccupiedForView(View view) {if (view == null || view.getParent() != mShortcutsAndWidgets) return;LayoutParams lp = (LayoutParams) view.getLayoutParams();mOccupied.markCells(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, true); }public void markCellsAsUnoccupiedForView(View view) {if (view == null || view.getParent() != mShortcutsAndWidgets) return;LayoutParams lp = (LayoutParams) view.getLayoutParams();mOccupied.markCells(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, false); }
参考上面两个方法添加对指定单元格的标记方法即可。
// 将单元格标记为占用 public void markCellAsOccupied(int cellX, int cellY, int cellHSpan, int cellVSpan) {mOccupied.markCells(cellX, cellY, cellHSpan, cellVSpan, true); }// 将单元格标记为未占用 public void markCellAsUnoccupied(int cellX, int cellY, int cellHSpan, int cellVSpan) {mOccupied.markCells(cellX, cellY, cellHSpan, cellVSpan, false); }
遍历标记各个位置图标状态即可。
// 标记 Cell 的占用状态 for (int i = 0; i < maxHotseatIconNum; i++) {if (i < curIconPos) {mCellLayout.markCellAsOccupied(i, 0, 1, 1);} else {mCellLayout.markCellAsUnoccupied(i, 0, 1, 1);} }
完整代码
原谅我的水平有限,原有的Margin计算方式没有看懂,只能自己重写了一遍,因此可能与原有的代码不大一样。与此同时,也加上横竖屏的适配。
public void resetLayout() {Hotseat mHotSeat = mLauncher.getHotseat();CellLayout mCellLayout = mHotSeat.getLayout();ShortcutAndWidgetContainer mContainer = mCellLayout.getShortcutsAndWidgets();DisplayMetrics mDisplayMetrics = new DisplayMetrics();mLauncher.getWindowManager().getDefaultDisplay().getMetrics(mDisplayMetrics);int mMargins;int curIconPos; // 当前 Icon 位置int hotseatLength; // Hotseat 长度int maxHotseatIconNum = mLauncher.getDeviceProfile().inv.numHotseatIcons; // Hotseat Icon 最大个数int curOrientation = mLauncher.getResources().getConfiguration().orientation;boolean isOrientationPortrait = curOrientation == Configuration.ORIENTATION_PORTRAIT;if (isOrientationPortrait) { // 竖屏curIconPos = 0;hotseatLength = mDisplayMetrics.widthPixels;// 移除或添加一个图标后,循环将图标重新排序for (int i = 0; i < maxHotseatIconNum; i++) {View child = mContainer.getChildAt(i, 0);if (child != null) {CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();lp.cellX = curIconPos;curIconPos++;// 将重新排序存入数据库之中mLauncher.getModelWriter().modifyItemInDatabase((ItemInfo) child.getTag(),LauncherSettings.Favorites.CONTAINER_HOTSEAT,-1, lp.cellX, lp.cellY, 1, 1);}}// 标记 Cell 的占用状态for (int i = 0; i < maxHotseatIconNum; i++) {if (i < curIconPos) {mCellLayout.markCellAsOccupied(i, 0, 1, 1);} else {mCellLayout.markCellAsUnoccupied(i, 0, 1, 1);}}} else { // 横屏curIconPos = maxHotseatIconNum - 1;hotseatLength = mDisplayMetrics.heightPixels;// 移除或添加一个图标后,循环将图标重新排序for (int i = maxHotseatIconNum - 1; i >= 0; i--) {View child = mCellLayout.getShortcutsAndWidgets().getChildAt(0, i);if (child != null) {CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();lp.cellY = curIconPos;curIconPos--;//将重新排序存入数据库之中mLauncher.getModelWriter().modifyItemInDatabase((ItemInfo) child.getTag(),LauncherSettings.Favorites.CONTAINER_HOTSEAT,-1, lp.cellX, lp.cellY, 1, 1);}}// 标记 Cell 的占用状态for (int i = 0; i < maxHotseatIconNum; i++) {if (i > curIconPos) {mCellLayout.markCellAsOccupied(0, i, 1, 1);} else {mCellLayout.markCellAsUnoccupied(0, i, 1, 1);}}}// Margin计算View child = mContainer.getChildAt(0);if (child == null || mContainer.getChildCount() == maxHotseatIconNum) {mMargins = 0;} else {CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();int iconLength = isOrientationPortrait ? lp.getWidth() : lp.getHeight();int padding = isOrientationPortrait ? mCellLayout.getPaddingLeft() * 2 : mCellLayout.getPaddingTop();int cost = mContainer.getChildCount() * iconLength + padding; // 已占用的长度mMargins = (hotseatLength - cost) / 2;}// 设置Margin,居中显示 HotseatFrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) mHotSeat.getLayoutParams();if (isOrientationPortrait) {layoutParams.setMargins(mMargins, 0, -mMargins, 0);} else {layoutParams.setMargins(0, -mMargins, 0, mMargins);}mHotSeat.setLayoutParams(layoutParams);// 重载 HotseatmHotSeat.requestLayout();
}
挖坑部分
遗留问题
1.初始化刷新问题。
应用启动以及横竖屏切换时,页面需要重新加载,这就导致需要在初始化时便设置Margin值,当前计算方式依赖布局信息,需要在View完成布局之后才能获取到相关信息,为此我将设置Margin的方法放置在Hotseat.java->onLayout中,解决了横竖屏切换的问题,但是启动时依然会有一个Margin值变动的过程。
2.图标覆盖问题。
将Hotseat文件夹中“电话”拖拽至Hotseat中,再将“Paly 商店”拖拽至CellLayout当中,出现图标错位问题。 目前没有找到问题所在。
后续思考
当前通过设置Hotseat的Margin以及对图标重新排序的方法来进行自动居中终究不是一个最优解,甚至可以说是一种“最差解”存在各种不自然的现象(eg. 图标拖动至Hotseat左端远比右端要难)。
对比MIUI12的实现方案:
各图标均匀分布在Hotseat,当需要添加图标时缩小图标间的间距并添加一个预览位置。我认为这是一种要好的多的解决方案。
要对Launcher做出如此大的改动,需要对Launcher有更深的理解,恕我目前无能为力。后续有时间研究的话再继续。如果有更好的相关文章,欢迎留言推荐给我,非常感谢!
[Android系统开发]Launcher Hotseat图标居中排列相关推荐
- Android系统开发:GMS包移植
Android系统开发:GMS包移植 首先,我们拿到一份完整的GMS包应该是如下图所示: 简单介绍每一个目录的作用 1)apps以及apps_go : 这里面就是google释放的apk资源文件,其中 ...
- Android系统开发和性能优化——查漏补缺【建议收藏】
做了这么久性能相关的工作,也接触了不少模块,说实话要做好性能这一块,真心不容易.为什么这么说? 是因为需要接触的知识实在是太多了, Android 是一个整体,牵一发而动全身,不是说只懂一个模块就可以 ...
- Android菜鸟如何学习Android系统开发?
如何做好Android学习前的准备? 如果你已经确定了学习Android的目标,那么,应该提前做好哪些工作.先打下哪些基础呢? 首先,你最好先熟悉一门编程语言,现在大学里面和计算机相关的专业甚至理工类 ...
- Android NDK开发之 Android系统开发中LOG的使用
浅谈Android系统开发中LOG的使用 转自:http://blog.csdn.net/luoshengyang/article/details/6581828
- Android系统开发 ----- 系统服务开发
系列文章目录 Android系统启动 ---- 主要流程类_MrDarly的博客-CSDN博客Android系统启动Zygote关系主要的类https://blog.csdn.net/weixin_6 ...
- Android系统开发:短信的号码拦截
Android系统开发:短信的号码拦截 Code:Android源码 功能要求:针对某号码,短信的接收与发送的监听与拦截. 设备对该号码发送短信的拦截 实现思路 : 应用发送短信(无论是否是默认短信) ...
- 工作感悟之Android系统开发入门
从离开学校到工作快两年了,这期间感悟良多,喜悦.悲伤.希望.失望...总的说来在曲折中不断前进,相信乘风破浪会有时,直挂云帆济沧海. 从事Android有些偶然因素,原始打算做WPF相关的开发,或是C ...
- Android菜鸟如何学习Android系统开发?(
如何做好Android学习前的准备? 如果你已经确定了学习Android的目标,那么,应该提前做好哪些工作.先打下哪些基础呢? 首先,你最好先熟悉一门编程语言,现在大学里面和计算机相关的专业甚至理工类 ...
- Android 系统开发做什么?
题外话 18 年我从 Android 应用开发转 Framework 层开发了,从此开启了 996 幸福生活,博客技术文更新基本停滞了,被工作占据了过多的精力,实在没时间像以前一样拟稿.写作,实践.反 ...
最新文章
- 火热物联网下,中国传感器的冷思考
- 购买使用vps建站(3)
- 西游记里学化学,请收下我的膝盖~ | 今日最佳
- 自定义Spring Data JPA存储库
- 用什么PHP框架最好?框架?还不如用开源系统吧
- 测试需知的TCP3次握手、4次挥手及10道经典面试题
- TableviewController基础
- Swift 团队开源 Collections,提供更多高效数据结构
- Linux下Bash编程之算术运算符详解(三)
- android中bundle的使用
- IKVM.NET_第二篇_应用
- USB3014-应用程序开发(2)
- Milogs客户销售工作日志软件系统简介
- DWM1000 定位操作流程--[蓝点无限]
- 优衣库真的是一家技术驱动型公司?
- 【读后感】薛兆丰经济学讲义
- 实验十四 水下无线传感网协议仿真实验
- 在智慧城市建设中 计算机模拟是一个强大的工具
- skyline R34与R35分类器---第一次尝试
- SSM出租车查询系统毕业设计-附源码220915
热门文章
- Win11系统打开电脑磁盘显示磁盘错误无法打开怎么办?
- linux挂载的硬盘为ro,remount成rw出错问题解决
- python爬取微博博主历史博文存入Excel
- 专访Mockplus用户齐嘉伟 | Mockplus满足做原型的所有需求
- 官场直升机 鸿蒙笔著,担当:老衲笔尖无墨水,要从空处想鸿蒙
- 实现谷歌浏览器打开海康视频等OCX控件
- GPA——平均绩点计算器(5.0分制)
- Kubeadm搭建高可用K8S(四)Dashboard安装配置
- (linux-x86-ARM)麒麟V10安装DBeaver21.3通用的数据库管理工具和 SQL 客户端
- LoRaEdge LR1120 卫星直连通信解读