
文章目录flexBasis 百分比的工作原理动态列数切换完整代码flexBasis 和 width 的区别间距的处理方式小结做过应用桌面类布局的人大概都遇过这个问题产品说普通窗口显示 5 列最大化窗口显示 7 列然后你翻了一圈文档发现 Grid 的列数要写死只能靠监听窗口宽度变化手动算。用flexBasis百分比有更优雅的解法不关心窗口多宽只告诉每个格子你占父容器的1/N超出一行的自动换行。想换列数改一个数字就行。flexBasis 百分比的工作原理flexBasis设置的是 Flex 子项在分配剩余空间之前的基准尺寸。配合FlexWrap.Wrap每行放不下的子项自动换到下一行。想让每行固定 N 列// 每行 5 列.flexBasis(20%)// 100 / 5 20%// 每行 4 列.flexBasis(25%)// 100 / 4 25%// 每行 6 列.flexBasis(16.66%)// 100 / 6 ≈ 16.67%注意如果子项有padding或margin实际宽度会超出百分比导致一行排不下 N 个。要么用calc(20% - 16px)减去间距要么把间距做在 padding 里而不是 margin 里。动态列数切换把列数作为State变量用户可以在 4/5/6 列之间切换Statecolumns:number5getbasisPercent():string{return${(100/this.columns).toFixed(2)}%}切换列数时只改this.columnsbasisPercent自动更新所有格子的宽度跟着变。完整代码interfaceAppItem{id:numbername:stringcategory:stringversion:stringicon:stringcolor:stringhasUpdate:booleanrating:number}EntryComponentstruct PcAppGridPage{Statecolumns:number5StateactiveCategory:string全部StatecategoryList:string[][全部]StatedisplayApps:AppItem[][]apps:AppItem[][{id:1,name:浏览器,category:工具,version:1.2.1,icon:,color:#3B82F6,hasUpdate:false,rating:4.8},{id:2,name:文件管理,category:系统,version:2.0.0,icon:,color:#10B981,hasUpdate:true,rating:4.6},{id:3,name:备忘录,category:效率,version:3.1.0,icon:,color:#F59E0B,hasUpdate:false,rating:4.7},{id:4,name:日历,category:效率,version:1.8.2,icon:,color:#8B5CF6,hasUpdate:true,rating:4.5},{id:5,name:计算器,category:工具,version:1.0.5,icon:,color:#EF4444,hasUpdate:false,rating:4.9},{id:6,name:相册,category:媒体,version:4.2.1,icon:️,color:#EC4899,hasUpdate:false,rating:4.8},{id:7,name:音乐,category:媒体,version:5.0.1,icon:,color:#6366F1,hasUpdate:true,rating:4.7},{id:8,name:视频,category:媒体,version:3.3.0,icon:,color:#F43F5E,hasUpdate:false,rating:4.6},{id:9,name:设置,category:系统,version:1.5.0,icon:⚙️,color:#6B7280,hasUpdate:false,rating:4.4},{id:10,name:AppGallery,category:系统,version:12.0,icon:,color:#CF1322,hasUpdate:true,rating:4.8},{id:11,name:邮件,category:通讯,version:2.4.0,icon:,color:#0EA5E9,hasUpdate:false,rating:4.3},{id:12,name:地图,category:工具,version:6.1.0,icon:️,color:#22C55E,hasUpdate:false,rating:4.7},{id:13,name:天气,category:工具,version:1.9.0,icon:️,color:#06B6D4,hasUpdate:true,rating:4.6},{id:14,name:代码编辑器,category:效率,version:2.0.0,icon:,color:#1D4ED8,hasUpdate:false,rating:4.9},{id:15,name:终端,category:系统,version:1.1.0,icon:⬛,color:#111827,hasUpdate:false,rating:4.8},]aboutToAppear():void{this.initData()}initData():void{constcats:string[][全部]this.apps.forEach((app:AppItem){if(cats.indexOf(app.category)-1){cats.push(app.category)}})this.categoryListcatsthis.updateDisplayApps()}updateDisplayApps():void{if(this.activeCategory全部){this.displayAppsthis.appsreturn}this.displayAppsthis.apps.filter((app:AppItem)app.categorythis.activeCategory)}getbasisPercent():string{return${(100/this.columns).toFixed(2)}%}BuilderappIcon(app:AppItem){Column({space:8}){Stack({alignContent:Alignment.TopEnd}){// 应用图标Column(){Text(app.icon).fontSize(32)}.width(64).height(64).borderRadius(16).backgroundColor(app.color).justifyContent(FlexAlign.Center).shadow({radius:8,color:${app.color}30,offsetY:4})// 更新角标if(app.hasUpdate){Circle({width:12,height:12}).fill(#EF4444).border({width:2,color:Color.White}).offset({x:4,y:-4})}}Text(app.name).fontSize(11).fontColor(#374151).maxLines(1).textOverflow({overflow:TextOverflow.Ellipsis}).width(72).textAlign(TextAlign.Center)}.alignItems(HorizontalAlign.Center).padding({top:12,bottom:12}).onClick((){})}BuildercolumnSelector(){Row({space:4}){Text(列数).fontSize(13).fontColor(#6B7280)ForEach([4,5,6,7],(col:number){Text(${col}).fontSize(13).fontColor(this.columnscol?Color.White:#374151).padding({left:12,right:12,top:6,bottom:6}).backgroundColor(this.columnscol?#3B82F6:#F3F4F6).borderRadius(8).onClick((){this.columnscol})},(col:number)col.toString())}}build(){Column({space:0}){// 顶部工具栏Row({space:16}){Text(应用桌面).fontSize(20).fontWeight(FontWeight.Bold).fontColor(#111827).layoutWeight(1)this.columnSelector()}.padding({left:24,right:24,top:20,bottom:16}).backgroundColor(Color.White).width(100%)// 分类筛选Scroll(){Row({space:4}){ForEach(this.categoryList,(cat:string){Text(cat).fontSize(13).fontColor(this.activeCategorycat?#3B82F6:#6B7280).fontWeight(this.activeCategorycat?FontWeight.Medium:FontWeight.Normal).padding({left:16,right:16,top:8,bottom:8}).backgroundColor(this.activeCategorycat?#EFF6FF:Color.Transparent).borderRadius(20).onClick((){this.activeCategorycatthis.updateDisplayApps()})},(cat:string)cat)}.padding({left:24,right:24})}.scrollable(ScrollDirection.Horizontal).width(100%).height(44).backgroundColor(Color.White)Divider().strokeWidth(1).color(#F3F4F6)// 应用网格Scroll(){Flex({wrap:FlexWrap.Wrap,alignContent:FlexAlign.Start}){ForEach(this.displayApps,(app:AppItem){Column(){this.appIcon(app)}.flexBasis(this.basisPercent).alignItems(HorizontalAlign.Center)},(app:AppItem)app.id.toString())}.width(100%).padding({left:12,right:12,top:16,bottom:24})}.layoutWeight(1).backgroundColor(#F9FAFB)}.width(100%).height(100%).constraintSize({minWidth:640})}}flexBasis 和 width 的区别width是最终宽度不参与 Flex 伸缩计算。flexBasis是分配剩余空间前的初始尺寸如果同时有flexGrow子项还会继续伸长。对于应用网格这种场景我们想要的是精确按百分比切割不希望伸长.flexBasis(this.basisPercent)// 不设 flexGrow默认是 0不会伸长如果写成.width(this.basisPercent)在没有 Flex 上下文的 Column 里是一样的效果但在 Flex 容器里语义不同。间距的处理方式格子之间不用margin会破坏百分比宽度的计算改成在内容区域内设paddingColumn(){this.appIcon(app)}.flexBasis(20%).padding({left:4,right:4})// 内部 padding不影响 flexBasis 计算或者用gap属性需要 Flex 容器支持视版本而定。小结flexBasis百分比 FlexWrap.Wrap是实现响应式网格最简洁的方式。改列数只改一个数字不需要重新计算像素值也不需要监听窗口宽度事件。