ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Harmony os 技术实战|拼豆制图33:响应式卡片末行如何用 Blank 保持等宽

Harmony os 技术实战|拼豆制图33:响应式卡片末行如何用 Blank 保持等宽 图库在手机上每行 2 张、平板上 3 张、宽屏上 4 张。只要图案总数不是列数的整数倍最后一行就会缺卡片。若卡片使用layoutWeight(1)最后一行仅有的一张卡会自动吃掉整行宽度从前几行的小卡突然变成大卡收藏页只有一张内容时这个问题尤其明显。拼豆制图没有引入复杂 Grid而是先把数组切成二维行再为末行补若干个同权重Blank。本篇从toPatternRows()和blankSlots()出发说明这一小段代码如何同时解决等宽、断点切换、收藏列表复用与稳定 Key并讨论何时应该升级为真正的Grid虚拟化。一、问题来自权重分配不是卡片本身卡片根节点设置Column({space:7}){// 封面、统计和标题}.layoutWeight(1).height(202)同一Row里有 4 张卡时每张获得约四分之一可用宽度最后一行若只有 1 张它会获得全部宽度。layoutWeight(1)做的是正确的剩余空间分配只是最后一行参与分配的节点数少了。因此修复点不应放在卡片内部写死宽度。固定宽度会破坏手机、平板、2in1 的自适应也很难同时扣除页面 padding 和列间 gap。正确做法是让每一行始终拥有相同数量的权重项。二、先按当前断点把一维数组切成行列数由页面断点决定privatepatternColumns():number{if(this.isLarge()){return4;}if(this.isMediumUp()){return3;}return2;}然后按列数切片privatetoPatternRows(patterns:Pattern[]):Pattern[][]{constrows:Pattern[][][];constcolumnsthis.patternColumns();for(leti0;ipatterns.length;icolumns){rows.push(patterns.slice(i,icolumns));}returnrows;}10 张图时结果分别是2 列[2, 2, 2, 2, 2] 3 列[3, 3, 3, 1] 4 列[4, 4, 2]断点变化会重新计算行分组无需保存二维数组状态。原始patterns才是事实来源行只是当前视口下的派生结构。三、补位函数返回索引数组而不是数量末行缺几个位置由blankSlots()计算privateblankSlots(rowLength:number,columns:number):number[]{constslots:number[][];for(letirowLength;icolumns;i){slots.push(i);}returnslots;}例如 4 列布局最后一行有 2 张卡结果是[2, 3]。返回数组的好处是可以直接交给ForEachForEach(this.blankSlots(row.length,this.patternColumns()),(slot:number){Blank().layoutWeight(1)},(slot:number)gallery-blank-${row[0].id}-${slot})如果只返回数字2页面还要额外构造可遍历对象数组同时携带了槽位索引适合生成稳定且可读的 Key。四、Blank 必须和真实卡片使用相同权重末行 2 张真实卡 2 个Blank时四个子项都为layoutWeight(1)每个槽位宽度相同[Card 1][gap][Card 2][gap][Blank 2][gap][Blank 3]Blank不需要高度、背景或点击事件只负责参与 Row 的横向剩余空间分配。若给它固定宽度而卡片仍用权重二者会在不同屏幕上产生比例偏差。也不要用一个Blank().layoutWeight(2)替代两个空槽。宽度总量看似相同但 Row 的space只在节点之间产生间距一个双权重 Blank 会少一个 gap真实卡片的边界位置将与上一行不齐。五、Key 要描述“哪一行的哪个空槽”图库和收藏页分别使用类似 Keygallery-blank-${row[0].id}-${slot}favorite-blank-${row[0].id}-${slot}这里包含三个维度页面语义、行锚点和槽位序号。断点切换后行分组会变化Key 随新的首项 ID 变化ArkUI 可以正确重建该行而不会把旧布局的空槽错误复用到新行。不建议只使用slot每一行都会产生2、3等相同值同一父级范围变化后容易失去唯一语义。也不建议用随机数或时间戳否则每次构建都创建新节点失去差量更新价值。六、同一分行函数复用到图库和收藏图库的数据来自分类筛选privatepatternRows():Pattern[][]{returnthis.toPatternRows(this.filteredPatterns());}收藏页的数据先合并用户生成图、当前生成图和内置图再分行privatefavoriteRows():Pattern[][]{returnthis.toPatternRows(this.favoritePatterns());}两页共享patternColumns()、gridGap()、toPatternRows()和blankSlots()因此断点规则是一致的。若收藏页单独写成两列平板上就会与图库产生明显的卡片尺寸跳变。复用的关键不是抽出一个巨大页面 Builder而是共享四个纯布局规则让不同数据源仍能保持同一网格语法。七、分类快捷区也是相同问题分类区宽屏最多 5 列中屏 3 列小屏 2 列privatecategoryColumns():number{if(this.isLarge())return5;if(this.isMediumUp())return3;return2;}其数据固定取五个分类再按列切行。中屏会得到[3,2]最后一行同样需要一个空槽。这里应调用ForEach(this.blankSlots(row.length,this.categoryColumns()),(slot:number){Blank().layoutWeight(1)})注意不能误用patternColumns()。两套列数在小屏都等于 2很容易暂时看不出问题到了中屏一个是 3、另一个也是 3仍然正常只有大屏 4 与 5 的差异才暴露错位。函数名应把内容类型写清楚。八、输入保护防止异常列数制造死循环toPatternRows()的步长来自columns。如果未来断点函数返回 0循环将永远不前进for(leti0;ipatterns.length;icolumns){// columns 0 时 i 不变化}抽成公共函数时应加入口保护functionchunkRowsT(items:T[],columns:number):T[][]{if(columns0){thrownewError(columns must be greater than zero);}constrows:T[][][];for(leti0;iitems.length;icolumns){rows.push(items.slice(i,icolumns));}returnrows;}blankSlots()也可把负数和rowLength columns视为无空槽或明确报错。页面内部当前列数固定为 2—5风险很低一旦函数进入公共布局库保护就值得加入。九、何时应换成 Grid行切片方案适合当前约 50 张内置图和少量收藏因为结构简单、最后一行语义明确也方便在一行内补权重槽位。但当数据达到数百或数千项时一次构建所有卡片的成本会上升。可按数据规模选择几十项、列数少、卡片高度固定Row ForEach Blank 数百项、需要懒加载Grid LazyForEach 卡片高度不固定、瀑布流WaterFlow切换到Grid后列宽交给columnsTemplate或自适应列配置末行通常不会把单项拉满整行不再需要显式 Blank。但收藏、筛选和稳定 ID 的数据规则仍可保留。十、用组合样本验证末行宽度纯函数层至少覆盖以下组合// items 数量 / columns / 行长度 / 空槽// 0 / 4 - []// 1 / 4 - [1] / [1,2,3]// 4 / 4 - [4] / []// 5 / 4 - [4,1] / 最后一行 [1,2,3]// 10 / 3 - [3,3,3,1] / 最后一行 [1,2]页面验收则在 599、600、839、840vp 附近调整窗口宽度观察列数切换。应确认前后行卡片左边界始终对齐。最后一行单卡不会拉伸到整行。断点切换后无短暂重叠或重复卡片。收藏页、图库和分类快捷区遵循各自列数。空数组不会访问row[0].id因为没有行就不会进入行级 ForEach。十一、结语末行卡片突然变宽根因是参与权重分配的节点数不足。拼豆制图先按响应式列数切行再用同权重Blank补足槽位让每行始终拥有相同的布局分母页面前缀、行首 ID 和槽位序号则让补位节点在断点变化时保持清晰身份。这套办法不需要固定卡宽也不依赖复杂组件非常适合中小规模响应式卡片列表。只要记住三个不变量——列数必须大于零、每个槽位权重相同、每个空槽拥有稳定 Key——图库、收藏和分类区就能在手机、平板与宽屏上保持一致节奏。
返回列表