当前位置: 首页 > news >正文

Launcher3中的CellLayout 和ShortcutAndWidgetContainer 的联系和各自职责

1. CellLayout(网格布局容器)

/*** 网格布局的核心容器,负责划分单元格和管理占用状态*/
public class CellLayout extends ViewGroup {private int mCellWidth = 100;  // 单元格宽度(像素)private int mCellHeight = 100; // 单元格高度private int mCountX = 4;      // 列数private int mCountY = 4;      // 行数!!!一个二维的boolean类型数据的数组,里面的每个数据都是boolean类型!!!private boolean[][] mOccupied = new boolean[mCountX][mCountY]; // 占用状态表private ShortcutsAndWidgetsContainer mContainer; // 子容器public CellLayout(Context context) {super(context);mContainer = new ShortcutsAndWidgetsContainer(context);addView(mContainer); // 添加唯一子容器}/*** 添加子View到指定单元格* @return 是否添加成功*/public boolean addViewToCell(View child, int cellX, int cellY, int spanX, int spanY) {// 1. 检查边界和占用状态if (!isRegionVacant(cellX, cellY, spanX, spanY)) {return false; // 区域被占用或越界}// 2. 标记单元格为已占用markCells(cellX, cellY, spanX, spanY, true);// 3. 生成布局参数并添加ViewLayoutParams lp = new LayoutParams(cellX, cellY, spanX, spanY);!!!	addView() 内部会调用 requestLayout() + invalidate(),强制容器更新 UI!!!这里的view ,以下的这些都可以添加进去!!!--->(基础控件	✅	TextViewButtonImageView 等可直接添加并显示。自定义 View	✅	继承自 View 或现有控件,需正确实现 onDraw()onMeasure()ViewGroup 容器	✅	如 LinearLayoutFrameLayout,可作为子容器嵌套。)mContainer.addView(child, lp);return true;}/** 检查目标区域是否全部空闲 */private boolean isRegionVacant(int cellX, int cellY, int spanX, int spanY) {for (int x = cellX; x < cellX + spanX; x++) {for (int y = cellY; y < cellY + spanY; y++) {if (x >= mCountX || y >= mCountY || mOccupied[x][y]) {return false; // 越界或已被占用}}}return true;}/** 标记/清除单元格占用状态 */private void markCells(int cellX, int cellY, int spanX, int spanY, boolean occupied) {for (int x = cellX; x < cellX + spanX; x++) {for (int y = cellY; y < cellY + spanY; y++) {mOccupied[x][y] = occupied;}}}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {// 计算总宽高(单元格尺寸 × 数量)int width = mCellWidth * mCountX;int height = mCellHeight * mCountY;setMeasuredDimension(width, height);mContainer.measure(widthMeasureSpec, heightMeasureSpec);}@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {mContainer.layout(0, 0, r - l, b - t); // 子容器填满父布局}/** 自定义LayoutParams,携带单元格信息 */public static class LayoutParams extends ViewGroup.LayoutParams {public int cellX, cellY; // 起始单元格坐标public int spanX, spanY; // 跨单元格数public LayoutParams(int cellX, int cellY, int spanX, int spanY) {super(0, 0);this.cellX = cellX;this.cellY = cellY;this.spanX = spanX;this.spanY = spanY;}}
}

2. ShortcutsAndWidgetsContainer(子View容器)

/*** 实际承载所有子View的容器,负责具体布局*/
public class ShortcutsAndWidgetsContainer extends ViewGroup {private int mCellWidth, mCellHeight; // 从CellLayout接收的单元格尺寸public ShortcutsAndWidgetsContainer(Context context) {super(context);}/** 接收CellLayout传递的网格参数 */public void setCellDimensions(int cellWidth, int cellHeight) {mCellWidth = cellWidth;mCellHeight = cellHeight;}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {// 测量所有子View(根据跨单元格数计算实际尺寸)for (int i = 0; i < getChildCount(); i++) {View child = getChildAt(i);CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();int childWidth = lp.spanX * mCellWidth;int childHeight = lp.spanY * mCellHeight;child.measure(MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY),MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY));}super.onMeasure(widthMeasureSpec, heightMeasureSpec);}@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {// 遍历所有子View,根据单元格坐标计算实际位置for (int i = 0; i < getChildCount(); i++) {View child = getChildAt(i);CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();int left = lp.cellX * mCellWidth;int top = lp.cellY * mCellHeight;child.layout(left,top,left + child.getMeasuredWidth(),top + child.getMeasuredHeight());}}@Overrideprotected boolean checkLayoutParams(ViewGroup.LayoutParams p) {return p instanceof CellLayout.LayoutParams; // 只接受自定义LayoutParams}
}

3. 使用示例

// 初始化CellLayout(4x4网格)
CellLayout cellLayout = new CellLayout(context);
setContentView(cellLayout);// 添加一个1x1的图标到(1,2)位置
ImageView icon = new ImageView(context);
icon.setImageResource(R.drawable.ic_launcher);
cellLayout.addViewToCell(icon, 1, 2, 1, 1);// 添加一个2x2的小部件到(0,0)位置
View widget = new View(context);
widget.setBackgroundColor(Color.BLUE);
cellLayout.addViewToCell(widget, 0, 0, 2, 2);

关键交互流程图

User CellLayout ShortcutsAndWidgetsContainer icon 调用addViewToCell(icon, 1, 2, 1, 1) 检查mOccupied[1][2]是否空闲 标记mOccupied[1][2]=true addView(icon, LayoutParams) 计算实际像素位置(x=100,y=200) layout(100,200,200,300) User CellLayout ShortcutsAndWidgetsContainer icon

总结

组件职责
CellLayout管理网格参数、占用状态、处理高层的交互逻辑(如拖拽)
ShortcutsAndWidgetsContainer实际承载子View,负责具体的测量和布局(像素级计算)
mOccupied二维数组快速记录单元格占用状态,避免遍历所有子View

相关文章:

  • 华为云镜像仓库下载 selenium/standalone-chrome 镜像
  • SQL关键字三分钟入门:ROW_NUMBER() —— 窗口函数为每一行编号
  • 深度学习-分类
  • Sensodrive SensoJoint机器人力控关节模组抗振动+Sensodrive力反馈系统精准对接
  • web3 docs
  • 力扣第73题-矩阵置零
  • Java面向对象(一)
  • 对话式数据分析与Text2SQL Agent产品可行性分析思考
  • Python 数据分析:numpy,抽提,整数数组索引
  • 从单体架构到微服务:微服务架构演进与实践
  • 如何解决电脑windows蓝屏问题
  • 叉车考试真题(含答案)pdf下载
  • Rust宏和普通函数的区别
  • 心理测评app在线预约系统框架设计
  • 【HarmonyOS Next之旅】DevEco Studio使用指南(三十八) -> 构建HAR
  • ByteMD+CozeAPI+Coze平台Agent+Next搭建AI辅助博客撰写平台(逻辑清楚,推荐!)
  • 如何修改discuz文章标题字数限制 修改成255
  • Spring MVC参数解析:深入剖析415异常与@RequestBody处理机制问题场景
  • 创客匠人:创始人 IP 打造引领知识变现新路径​
  • 【HarmonyOS NEXT】跳转到华为应用市场进行应用下载并更新