社区编辑申请
注册/登录
基于HarmonyOS ArkUI 3.0框架,瀑布式显示HDC2021图片
开发 OpenHarmony
由于前端太菜了,没有在eTS上按照原理实现, 然后就想到了用Flex布局的FlexDirection.Column, 再加上高度设备,效果也出来了。

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

一, 前言

在介绍之前,虽然上篇 基于HarmonyOS ArkUI 3.0 框架,我成功开发了流式布局网络语 吐槽过了, 再吐槽一下为什么文档Ets组件里没有文本输入框,这么基础的组件都不先提供的,好的当时在HDC大会体验Ets实例时,见到有用输入框,名叫TextInput,在开发工具是没有提示的,然后我也尝试用它来为我的Demo提供输入,然而发现个问题,当我把TextInput组件里的text属于绑定@State 定义变量时,每次在文本框输入内容,点击按钮,清空变量值,这时文本输入框也清空了,但当我再次输入内容时,输入文本框会追加上次的内容,估计是因为TextInput有这个Bug,所以还没有在文档上显示出来。这里显示的瀑布式布局效果,我也是在HDC大会上,印象里见到过这样的效果,但现在已经记不清楚在哪里看到过了,然后以为文档里有实例或组件说明,找了一遍, 没有找到,然后就在网上科普了一下瀑布式布局原理,道理都懂了,由于前端太菜了,没有在eTS上按照原理实现, 然后就想到了用Flex布局的FlexDirection.Column, 再加上高度设备,效果也出来了,但觉得不是真正的瀑布式布局,我想下来官网文档开放布局了,一行代码就可以有瀑布式布局了,目前先用着自己写的效果了。

简单介绍一下本文的实现效果,在文本输入框输入图片名称, 模糊搜索出符合条件的图片,点击搜索按钮,把符合条件图片添加到下面瀑布式布局的组件里,图片以x、y轴缩放从0.5变化到1,透明度从0到1 显示出来,点击随机删除按钮时,从下面图片随机删除一个,并且以沿y轴旋转360度消失。

二. 实现效果

开发环境效果: https://www.bilibili.com/video/BV1JQ4y1Q7z2/

远程模拟器效果: https://www.bilibili.com/video/BV1uq4y1R7vB/

基于HarmonyOS ArkUI 3.0 框架,瀑布式显示HDC2021图片-鸿蒙HarmonyOS技术社区

三.创建工程

在这当作你已经安装好最新版本DevEco-Studio开发工具, 点击File -> New -> New Project… 弹出Create HarmonyOS Project窗口, 这里我选择空白eTS模板创建, 下来就跟着一起玩转HarmonyOS ArkUI 3.0 框架声明式开发吧.

基于HarmonyOS ArkUI 3.0 框架,瀑布式显示HDC2021图片-鸿蒙HarmonyOS技术社区
基于HarmonyOS ArkUI 3.0 框架,瀑布式显示HDC2021图片-鸿蒙HarmonyOS技术社区

四. 界面开发

界面有三个组件组合而成,文本输入框和搜索按钮组合成一个自定义组件, 历史记录和随机删除按钮组合成一个自定义组件,滚动组件和多个图片组件组合成一个自定义组件,同时还有Model结构, 初始化数据模型, 下面我们分别从上到下来介绍自定义组件:

  1. import { PictureData } from '../model/PictureData.ets' 
  2. import { initOnStartup } from '../model/PictureDataModels.ets' 
  3.  
  4. @Entry 
  5. @Component 
  6. struct PictureList { 
  7.   @State pictureItems: PictureData[] = initOnStartup() 
  8.  
  9.   build() { 
  10.     Column() { 
  11.       // 文本输入框和搜索按钮组合自定义组件 
  12.       Search_Input({ pictureArr: $pictureItems }) 
  13.       // 历史记录和随机删除按钮组合成自定义组件 
  14.       Operation_Picture({ pictureArr: $pictureItems }) 
  15.       // 滚动组件和多个图片组件组合成自定义组件 
  16.       Flowlayout_Container({ pictureArr: $pictureItems}) 
  17.     } 
  18.     .alignItems(HorizontalAlign.Center) 
  19.   } 

 实现组件内转场动效,通过点击搜索按钮或随机删除按钮来控制图片组件的查找和移除,呈现容器组件子组件过滤和移除时的动效。

这里用到组件转场动画,简单说一下组件转场主要通过transition属性方法配置转场参数,在组件搜索和移除时会执行过渡动效,需要配合animteTo才能生效。动效时长、曲线、延时跟随animateTo中的配置。

文本输入框和搜索按钮组合,在新增按钮的onClick事件中添加animateTo方法,来使下面图片子组件动效生效。

  1. @Component 
  2. struct Search_Input { 
  3.   @State searchInput: string = '' 
  4.   @Link pictureArr: PictureData[] 
  5.  
  6.   build() { 
  7.     Flex({ alignItems: ItemAlign.Center }){ 
  8.       TextInput({ placeholder: '请输入...', text: this.searchInput }) 
  9.         .type(InputType.Normal) 
  10.         .placeholderColor(Color.Gray) 
  11.         .placeholderFont({ size: 50, weight: 2}) 
  12.         .enterKeyType(EnterKeyType.Search) 
  13.         .caretColor(Color.Green) 
  14.         .layoutWeight(8) 
  15.         .height(40) 
  16.         .borderRadius('20px'
  17.         .backgroundColor(Color.White) 
  18.         .onChange((value: string) => { 
  19.           this.searchInput = value 
  20.         }) 
  21.  
  22.       Button({type: ButtonType.Capsule, stateEffect:false}) { 
  23.         Text('查找').fontSize(17).fontColor(Color.Blue) 
  24.       } 
  25.       .layoutWeight(2) 
  26.       .backgroundColor('#00000000'
  27.       .onClick((event: ClickEvent) => { 
  28.         if (this.searchInput != null && this.searchInput.length > 0) { 
  29.           let that = this; 
  30.           animateTo({ duration: 600 }, () => { 
  31.             this.pictureArr = this.pictureArr.filter((item, idx, arr) => item.name.indexOf(that.searchInput) > -1) 
  32.           }) 
  33.           this.searchInput = '' 
  34.         } 
  35.       }) 
  36.     } 
  37.     .height(60) 
  38.     .padding({left: 10}) 
  39.     .backgroundColor('#FFedf2f5'
  40.   } 

历史记录和随机删除按钮组合

  1. @Component 
  2. struct Operation_Picture { 
  3.   @Link pictureArr: PictureData[] 
  4.  
  5.   build() { 
  6.     Flex({ alignItems: ItemAlign.Center }) { 
  7.       if (this.pictureArr.length > 0) { 
  8.         Text('历史记录'
  9.           .fontSize(14) 
  10.           .fontColor(Color.Grey) 
  11.           .layoutWeight(5) 
  12.  
  13.         Text('随机删除'
  14.           .textAlign(TextAlign.End
  15.           .margin({right: 30}) 
  16.           .fontSize(14) 
  17.           .fontColor(Color.Red) 
  18.           .layoutWeight(5) 
  19.           .onClick((event: ClickEvent) => { 
  20.             animateTo({ duration: 600 }, () => { 
  21.               var idx = Math.floor(Math.random()*this.pictureArr.length); 
  22.               this.pictureArr.splice(idx, 1) 
  23.             }) 
  24.           }) 
  25.       } 
  26.     } 
  27.     .height(40) 
  28.     .padding({ left: 20, top: 10 }) 
  29.   } 

滚动组件和多个图片组件组合成, 给图片组件添加两个transition属性,分别用于定义组件的添加动效和移除动效, 同时为实现瀑布式布局,设置好每张图片高度.

  1. @Component 
  2. struct Flowlayout_Container { 
  3. // 链接主入口图片数组 
  4.   @Link pictureArr: PictureData[] 
  5.   private picturesHeight: number 
  6.  
  7.   aboutToAppear() { 
  8.     let tmpHeight: number; 
  9.  
  10.     ForEach(this.pictureArr, 
  11.       (item:PictureData) => { 
  12.         if (item.id % 2 == 0) { 
  13.           tmpHeight = Number(tmpHeight) + 300; 
  14.         } else { 
  15.           tmpHeight = Number(tmpHeight) + 800; 
  16.         } 
  17.       }, 
  18.       (item:PictureData) => item.id.toString() 
  19.     ) 
  20.  
  21.     this.picturesHeight = tmpHeight; 
  22.   } 
  23.  
  24.   build() { 
  25.     // 滚动组件 
  26.     Scroll() { 
  27.       // Flex布局, wrap为FlexWrap.Wrap为流式布局 
  28.       Flex({justifyContent: FlexAlign.Start, direction: FlexDirection.Column, alignContent: FlexAlign.Start, alignItems: ItemAlign.Start, wrap: FlexWrap.Wrap}) { 
  29.         if (this.pictureArr.length > 0) { 
  30.           // 循环显示图片到Image组件 
  31.           ForEach(this.pictureArr, 
  32.             (item:PictureData) => { 
  33.               if (item.id % 2 == 0) { 
  34.                 Image(item.image) 
  35.                   .objectFit(ImageFit.Auto).width(px2vp(530)) 
  36.                   .height(px2vp(300)).margin(2) 
  37.                 // 搜索时的动画 
  38.                   .transition({ type: TransitionType.Insert, scale: { x: 0.5, y: 0.5 }, opacity: 0 }) 
  39.                 // 删除时的动画 
  40.                   .transition({ type: TransitionType.Delete, rotate: { x: 0, y: 1, z: 0, angle: 360 }, scale: { x: 0, y: 0 } }) 
  41.               } else { 
  42.                 Image(item.image) 
  43.                   .objectFit(ImageFit.Auto).width(px2vp(530)) 
  44.                   .height(px2vp(800)).margin(2) 
  45.                 // 搜索时的动画 
  46.                   .transition({ type: TransitionType.Insert, scale: { x: 0.5, y: 0.5 }, opacity: 0 }) 
  47.                 // 删除时的动画 
  48.                   .transition({ type: TransitionType.Delete, rotate: { x: 0, y: 1, z: 0, angle: 360 }, scale: { x: 0, y: 0 } }) 
  49.               } 
  50.  
  51.             }, 
  52.             (item:PictureData) => item.id.toString() 
  53.           ) 
  54.         } 
  55.       } 
  56.       .margin({left: 10, top: 10, right: 10, bottom: 100}) 
  57.       .padding({bottom: 10}).align(Alignment.TopStart).width(px2vp(1024)) 
  58.       .height(px2vp(this.picturesHeight)) 
  59.     } 
  60.   } 

数据Model

  1. export class PictureData { 
  2.   id: number; 
  3.   name: string; 
  4.   image: Resource; 
  5.  
  6.   constructor(id: number, name: string, image: Resource) { 
  7.     this.id = id; 
  8.     this.name = name
  9.     this.image = image; 
  10.   } 

初始化数据方法

  1. import { PictureData } from './PictureData.ets' 
  2.  
  3. const PictureArr: any[] = [ 
  4.   {id: 1, name'aa1', image: $r("app.media.1")}, 
  5.   {id: 2, name'aa2', image: $r("app.media.2")}, 
  6.   {id: 3, name'bb3', image: $r("app.media.3")}, 
  7.   {id: 4, name'bb4', image: $r("app.media.4")}, 
  8.   {id: 5, name'aa1', image: $r("app.media.5")}, 
  9.   {id: 6, name'aa2', image: $r("app.media.6")}, 
  10.   {id: 7, name'aa3', image: $r("app.media.7")}, 
  11.   {id: 8, name'aa4', image: $r("app.media.8")}, 
  12.   {id: 9, name'cc1', image: $r("app.media.9")}, 
  13.   {id: 10, name'cc2', image: $r("app.media.10")}, 
  14.   {id: 11, name'bb3', image: $r("app.media.11")}, 
  15.   {id: 12, name'bb4', image: $r("app.media.12")}, 
  16.   {id: 13, name'aa1', image: $r("app.media.13")}, 
  17.   {id: 14, name'aa2', image: $r("app.media.14")}, 
  18.   {id: 15, name'bb3', image: $r("app.media.15")}, 
  19.   {id: 16, name'bb4', image: $r("app.media.16")}, 
  20.   {id: 17, name'aa1', image: $r("app.media.17")}, 
  21.   {id: 18, name'aa2', image: $r("app.media.18")}, 
  22.   {id: 19, name'aa3', image: $r("app.media.19")} 
  23. ]; 
  24.  
  25. export function initOnStartup(): Array<PictureData> { 
  26.   let PictureDataArray: Array<PictureData> = [] 
  27.  
  28.   PictureArr.forEach(item => { 
  29.     PictureDataArray.push(new PictureData(item.id, item.name, item.image)); 
  30.   }) 
  31.  
  32.   return PictureDataArray; 
  33. }; 

介绍就到此了,声明式开发,是不是简洁了很多,大家一起撸起来吧。

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

 

责任编辑:jianghua 来源: 鸿蒙社区

同话题下的热门内容

Rust难懂?一文解读其“所有权”和“借用”概念太强了!Python 开发桌面小工具,让代码替我们干重复的工作!K8s需要替代品!你离完全理解 SQL 只差这十步分享六个 Vue3 开发必备的 VSCode 插件程序员如何寻找技术突破、体现技术价值?接口性能优化实战:从20s到500ms,三招搞定六七十岁程序员工资暴涨 就因为这门没啥人学的编程语言

编辑推荐

2017年9月编程语言排行榜:Java、C与C++三巨头还能统治排行榜多久?2017年最受欢迎的5个前端框架比较2017年11月编程语言排行榜:脚本语言怎么了?2017年3月编程语言排行榜:Swift首次进入前十最近租房有点烦!技术人如何用Python找到称心如意的“小窝”?
我收藏的内容
点赞
收藏

51CTO技术栈公众号