目录

  • 背景
  • 问题点
  • 解决过程
  • 完整代码
  • 遗留问题
    • 1.初始化刷新问题。
    • 2.图标覆盖问题。
  • 后续思考

​之前接手一个和Hotseat自动排列相关的Bug,本身实现方案是参考博文 Hotseat 自动排列 的,但是在实现之后出现将Hotseat当中的图标移除后有一定概率使两个图标重叠的Bug。在此记录一下解决过程。

背景

  1. 整体项目基于原生Android 9.0

  2. Launcher Hotseat自动排列方案参考博文 Hotseat 自动排列

  3. 竖屏状态Hotseat在底部,横屏状态Hotseat在右边

问题点

​将Hotseat的“电话”拖拽至CellLayout当中,再从文件夹当中拖拽“相机”至Hotseat当中时,Hotseat不会提示“相机”应该放在第5个位置,而是直接覆盖在“信息”上面。

解决过程

  1. 通过Android Device Monitor DDMS显示两个图标的坐标重叠,确定图标依然存在,并未消失。

  2. 了解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);
    }
    
  3. 参考上面两个方法添加对指定单元格的标记方法即可。

    // 将单元格标记为占用
    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);
    }
    
  4. 遍历标记各个位置图标状态即可。

    //  标记 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图标居中排列相关推荐

  1. Android系统开发:GMS包移植

    Android系统开发:GMS包移植 首先,我们拿到一份完整的GMS包应该是如下图所示: 简单介绍每一个目录的作用 1)apps以及apps_go : 这里面就是google释放的apk资源文件,其中 ...

  2. Android系统开发和性能优化——查漏补缺【建议收藏】

    做了这么久性能相关的工作,也接触了不少模块,说实话要做好性能这一块,真心不容易.为什么这么说? 是因为需要接触的知识实在是太多了, Android 是一个整体,牵一发而动全身,不是说只懂一个模块就可以 ...

  3. Android菜鸟如何学习Android系统开发?

    如何做好Android学习前的准备? 如果你已经确定了学习Android的目标,那么,应该提前做好哪些工作.先打下哪些基础呢? 首先,你最好先熟悉一门编程语言,现在大学里面和计算机相关的专业甚至理工类 ...

  4. Android NDK开发之 Android系统开发中LOG的使用

    浅谈Android系统开发中LOG的使用 转自:http://blog.csdn.net/luoshengyang/article/details/6581828

  5. Android系统开发 ----- 系统服务开发

    系列文章目录 Android系统启动 ---- 主要流程类_MrDarly的博客-CSDN博客Android系统启动Zygote关系主要的类https://blog.csdn.net/weixin_6 ...

  6. Android系统开发:短信的号码拦截

    Android系统开发:短信的号码拦截 Code:Android源码 功能要求:针对某号码,短信的接收与发送的监听与拦截. 设备对该号码发送短信的拦截 实现思路 : 应用发送短信(无论是否是默认短信) ...

  7. 工作感悟之Android系统开发入门

    从离开学校到工作快两年了,这期间感悟良多,喜悦.悲伤.希望.失望...总的说来在曲折中不断前进,相信乘风破浪会有时,直挂云帆济沧海. 从事Android有些偶然因素,原始打算做WPF相关的开发,或是C ...

  8. Android菜鸟如何学习Android系统开发?(

    如何做好Android学习前的准备? 如果你已经确定了学习Android的目标,那么,应该提前做好哪些工作.先打下哪些基础呢? 首先,你最好先熟悉一门编程语言,现在大学里面和计算机相关的专业甚至理工类 ...

  9. Android 系统开发做什么?

    题外话 18 年我从 Android 应用开发转 Framework 层开发了,从此开启了 996 幸福生活,博客技术文更新基本停滞了,被工作占据了过多的精力,实在没时间像以前一样拟稿.写作,实践.反 ...

最新文章

  1. 火热物联网下,中国传感器的冷思考
  2. 购买使用vps建站(3)
  3. 西游记里学化学,请收下我的膝盖~ | 今日最佳
  4. 自定义Spring Data JPA存储库
  5. 用什么PHP框架最好?框架?还不如用开源系统吧
  6. 测试需知的TCP3次握手、4次挥手及10道经典面试题
  7. TableviewController基础
  8. Swift 团队开源 Collections,提供更多高效数据结构
  9. Linux下Bash编程之算术运算符详解(三)
  10. android中bundle的使用
  11. IKVM.NET_第二篇_应用
  12. USB3014-应用程序开发(2)
  13. Milogs客户销售工作日志软件系统简介
  14. DWM1000 定位操作流程--[蓝点无限]
  15. 优衣库真的是一家技术驱动型公司?
  16. 【读后感】薛兆丰经济学讲义
  17. 实验十四 水下无线传感网协议仿真实验
  18. 在智慧城市建设中 计算机模拟是一个强大的工具
  19. skyline R34与R35分类器---第一次尝试
  20. SSM出租车查询系统毕业设计-附源码220915

热门文章

  1. Win11系统打开电脑磁盘显示磁盘错误无法打开怎么办?
  2. linux挂载的硬盘为ro,remount成rw出错问题解决
  3. python爬取微博博主历史博文存入Excel
  4. 专访Mockplus用户齐嘉伟 | Mockplus满足做原型的所有需求
  5. 官场直升机 鸿蒙笔著,担当:老衲笔尖无墨水,要从空处想鸿蒙
  6. 实现谷歌浏览器打开海康视频等OCX控件
  7. GPA——平均绩点计算器(5.0分制)
  8. Kubeadm搭建高可用K8S(四)Dashboard安装配置
  9. (linux-x86-ARM)麒麟V10安装DBeaver21.3通用的数据库管理工具和 SQL 客户端
  10. LoRaEdge LR1120 卫星直连通信解读