鸿蒙Next实现瀑布流布局

鸿蒙Next实现瀑布流布局 #鸿蒙影音娱乐类应用 #拍摄美化 #HarmonyOS

一、环境准备与项目创建

在开始实现瀑布流布局前,需确保已安装好 DevEco Studio,且已配置好鸿蒙开发环境。打开 DevEco Studio,新建一个鸿蒙应用项目,选择合适的模板(如 Empty Feature Ability),设置项目名称、包名等信息,完成项目创建。

二、布局设计思路

鸿蒙 Next 的瀑布流布局可以通过自定义组件结合 Column、Row 等容器组件实现。其核心思路是将数据分成若干列,每列独立滚动展示,且根据数据项高度动态调整布局,以达到类似瀑布自然流动的效果。

三、基础实现

创建一个自定义组件 MasonryLayout,接收图片数据数组作为参数,并根据列数将数据分配到不同列中展示:

@Component
export struct  MasonryLayout {
  @Prop data: string[];

  @State cols: number[] = Array.from<number>({ length: 2 }).fill(0);

  build() {
    Row({}) {
      ForEach(this.cols, (_col: number, cIndex) => {
        Column({  }) {
          ForEach(this.data, (item: string, i) => {
            if(i % this.cols.length === cIndex) {
              Image(item).width(`${100 / this.cols.length}%`);
            }
          })
        }
      })
    }.alignItems(VerticalAlign.Top)
  }
}

四、引用 MasonryLayout 瀑布流组件

build() {
  MasonryLayout({
    data: ["img1.png", "img2.png", "img3.png", "img4.png", "img5.png"],
  });
}

五、优化与扩展

1. 响应式布局

通过 MediaQuery 组件根据屏幕宽度动态调整瀑布流的列数,以适配不同设备:

在 UIAbility 的 onWindowStageCreate 生命周期回调中,通过窗口对象获取启动时的应用窗口宽度并注册回调函数监听窗口尺寸变化。将窗口尺寸的长度单位由 px 换算为 vp 后,即可基于前文中介绍的规则得到当前断点值,此时可以使用状态变量记录当前的断点值方便后续使用

  • MainAbility.ts
import { window, display } from "@kit.ArkUI";
import { UIAbility } from "@kit.AbilityKit";

export default class MainAbility extends UIAbility {
  private windowObj?: window.Window;
  private col: number = 2;
  //...
  // 根据当前窗口尺寸更新断点
  private updateBreakpoint(windowWidth: number): void {
    // 将长度的单位由px换算为vp
    let windowWidthVp = windowWidth / display.getDefaultDisplaySync().densityPixels;
    let col: number = this.col;
    if (windowWidthVp < 320) {
      // "xs";
      col = 1;
    } else if (windowWidthVp < 600) {
      // "sm";
      col = 2;
    } else if (windowWidthVp < 840) {
      // "md";
      col = 3;
    } else {
      // "lg";
      col = 4;
    }
    if (this.col !== col) {
      this.col = col;
    }
  }

  onWindowStageCreate(windowStage: window.WindowStage): void {
    windowStage.getMainWindow().then((windowObj) => {
      this.windowObj = windowObj;
      // 获取应用启动时的窗口尺寸
      this.updateBreakpoint(windowObj.getWindowProperties().windowRect.width);
      // 注册回调函数,监听窗口尺寸变化
      windowObj.on("windowSizeChange", (windowSize) => {
        this.updateBreakpoint(windowSize.width);
      });
    });
    // ...
  }

  //...
}

  • MasonryLayout.ets
interface IBpMapCol {
  xs: number;
  sm: number;
  md: number;
  lg: number;
}

const bpMapCol = new Map<string, number>();

bpMapCol.set('xs', 1)
bpMapCol.set('sm', 2)
bpMapCol.set('md', 3)
bpMapCol.set('lg', 4)

@Component
export struct  MasonryLayout {
  @StorageProp('currentBreakpoint') curBp: keyof IBpMapCol = 'sm';
  @Prop data: string[];

  @State cols: number[] = Array.from<number>({ length: bpMapCol.get(this.curBp) || 2 }).fill(0);

  build() {
    Row({}) {
      ForEach(this.cols, (_col: number, cIndex) => {
        Column({  }) {
          ForEach(this.data, (item: string, i) => {
            if(i % this.cols.length === cIndex) {
              Image(item).width(`${100 / this.cols.length}%`);
              Text(this.curBp)
            }
          })
        }
      })
    }.alignItems(VerticalAlign.Top)
  }
}

注:鸿蒙 next 中无法使用索引访问对象属性,如 const obj = { a: 1 } 无法使用 obj[a],这种情况可以用 Map

2. 动态加载数据

为了实现类似真实瀑布流不断加载新数据的效果,可以结合鸿蒙的 LazyForEach 组件,在滚动到列表底部时触发数据加载逻辑

六、网络权限

// config.json
{
  "module": {
    "reqPermissions": [
      {
        "name": "ohos.permission.INTERNET",
        "reason": "需要网络权限来加载图片"
      }
    ]
  }
}

七、常见问题与解决方案

1. 图片加载后布局跳动

  • 解决方案:使用预估高度占位,图片加载完成后更新高度

2. 大数据量性能问题

  • 解决方案:实现虚拟列表,只渲染可视区域内的元素

3. 滚动卡顿

  • 解决方案: 使用防抖/节流处理滚动事件避免在滚动回调中执行复杂计算使用鸿蒙的 Canvas 组件替代部分布局组件

4. 不同设备适配问题

  • 解决方案: 使用响应式布局动态调整列数基于设备类型设置不同的默认列数

八、最佳实践总结

  1. 优先使用固定高度:如果业务场景允许,尽量使用固定高度或宽高比,减少动态测量开销
  2. 合理实现懒加载:对于非首屏内容或图片资源,一定要实现懒加载
  3. 渐进式增强体验:先确保基础功能可用,再添加动画和交互效果
  4. 测试与优化:在不同设备上测试性能表现,针对卡顿问题进行专项优化
  5. 遵循鸿蒙设计规范:保持与鸿蒙系统一致的视觉风格和交互体验
全部评论

相关推荐

昨天 22:46
已编辑
东北大学 Java
鼠鼠其实大三的时候,拿过字节客户端开发的实习,但是当时疫情,把&nbsp;offer&nbsp;拖没了,遗憾至今。后面专心备战考研,打算上岸之后从客户端转后端或者算法,当然现在看来,算法的想法有些天真,而且也感觉自己对算法岗没什么兴趣,看到深度学习什么的头就疼,所以就一头扎进后端里。研一上的时候,鼠鼠寻思先刷一段客户端的实习,到时候万一后端找的不理想,可以拿客户端秋招当保底。在探探实习4个多月后,提出离职专心学习后端技术栈。其实,有了客户端开发经验,学习后端像是水到渠成,没遇到什么特别的困难和瓶颈,Java基础牢牢掌握(因为安卓开发也是Java)。看了一周多&nbsp;SSM&nbsp;就直接上手微服务项目了,这里着重推荐一下知识星球的“小哈书”项目,敲了将近一个月后,中间遇到的各种中间件,比如&nbsp;Redis,&nbsp;MQ&nbsp;等都是边做边学,有了大致的一个理解和应用。项目大体完成后,我就开始着手项目更深层次的理解,用到的各种中间件的底层,为什么这么用等等,并且针对具体地中间件买了书籍来看,不得不说看了书就是比背八股文理解的深入,面试的时候真的有的说,体现自己对技术的理解。从三月份学习后端到五月份开始投递,只从官网投递了大厂(字节,腾讯,快手,滴滴,百度,美团,小米),这里吐槽一下这个时间点找日常实习太难了,只有字节百度腾讯约了面,腾讯还是&nbsp;kpi。当时百度一面挂,字节搜索二面挂还是挺挫败的,但好在鼠鼠越挫越勇,第二次面字节的时候就侃侃而谈了,最终也是如愿以偿拿到了offer。总的来说,学习软件开发掌握学习方法很重要,打的就是一个信息差和自学能力,同时也需要代码量的堆积产生对各种业务的理解。而且如果想冲大厂,真的需要好好沉淀,对项目的细节要牢牢掌握,面试官是很抠细节的。最后祝愿各位牛友都有满意的offer~
投递快手等公司10个岗位 我的成功项目解析 牛客创作赏金赛
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务