基于HarmonyOS ArkUI 3.0框架,瀑布式显示HDC2021图片

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

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

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/

三.创建工程

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

四. 界面开发

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

import {  PictureData } from ../model/PictureData.ets import {  initOnStartup } from ../model/PictureDataModels.ets @Entry @Component struct PictureList {    @State pictureItems: PictureData[] = initOnStartup()   build() {      Column() {        // 文本输入框和搜索按钮组合自定义组件       Search_Input({  pictureArr: $pictureItems })       // 历史记录和随机删除按钮组合成自定义组件       Operation_Picture({  pictureArr: $pictureItems })       // 滚动组件和多个图片组件组合成自定义组件       Flowlayout_Container({  pictureArr: $pictureItems})     }     .alignItems(HorizontalAlign.Center)   } } 

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

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

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

@Component struct Search_Input {    @State searchInput: string =    @Link pictureArr: PictureData[]   build() {      Flex({  alignItems: ItemAlign.Center }){        TextInput({  placeholder: 请输入..., text: this.searchInput })         .type(InputType.Normal)         .placeholderColor(Color.Gray)         .placeholderFont({  size: 50, weight: 2})         .enterKeyType(EnterKeyType.Search)         .caretColor(Color.Green)         .layoutWeight(8)         .height(40)         .borderRadius(20px)         .backgroundColor(Color.White)         .onChange((value: string) => {            this.searchInput = value         })       Button({ type: ButtonType.Capsule, stateEffect:false}) {          Text(查找).fontSize(17).fontColor(Color.Blue)       }       .layoutWeight(2)       .backgroundColor(#00000000)       .onClick((event: ClickEvent) => {          if (this.searchInput != null && this.searchInput.length > 0) {            let that = this;           animateTo({  duration: 600 }, () => {              this.pictureArr = this.pictureArr.filter((item, idx, arr) => item.name.indexOf(that.searchInput) > -1)           })           this.searchInput =          }       })     }     .height(60)     .padding({ left: 10})     .backgroundColor(#FFedf2f5)   } } 

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

@Component struct Operation_Picture {    @Link pictureArr: PictureData[]   build() {      Flex({  alignItems: ItemAlign.Center }) {        if (this.pictureArr.length > 0) {          Text(历史记录)           .fontSize(14)           .fontColor(Color.Grey)           .layoutWeight(5)         Text(随机删除)           .textAlign(TextAlign.End)           .margin({ right: 30})           .fontSize(14)           .fontColor(Color.Red)           .layoutWeight(5)           .onClick((event: ClickEvent) => {              animateTo({  duration: 600 }, () => {                var idx = Math.floor(Math.random()*this.pictureArr.length);               this.pictureArr.splice(idx, 1)             })           })       }     }     .height(40)     .padding({  left: 20, top: 10 })   } } 

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

@Component struct Flowlayout_Container {  // 链接主入口图片数组   @Link pictureArr: PictureData[]   private picturesHeight: number   aboutToAppear() {      let tmpHeight: number;     ForEach(this.pictureArr,       (item:PictureData) => {          if (item.id % 2 == 0) {            tmpHeight = Number(tmpHeight) + 300;         } else {            tmpHeight = Number(tmpHeight) + 800;         }       },       (item:PictureData) => item.id.toString()     )     this.picturesHeight = tmpHeight;   }   build() {      // 滚动组件     Scroll() {        // Flex布局, wrap为FlexWrap.Wrap为流式布局       Flex({ justifyContent: FlexAlign.Start, direction: FlexDirection.Column, alignContent: FlexAlign.Start, alignItems: ItemAlign.Start, wrap: FlexWrap.Wrap}) {          if (this.pictureArr.length > 0) {            // 循环显示图片到Image组件           ForEach(this.pictureArr,             (item:PictureData) => {                if (item.id % 2 == 0) {                  Image(item.image)                   .objectFit(ImageFit.Auto).width(px2vp(530))                   .height(px2vp(300)).margin(2)                 // 搜索时的动画                   .transition({  type: TransitionType.Insert, scale: {  x: 0.5, y: 0.5 }, opacity: 0 })                 // 删除时的动画                   .transition({  type: TransitionType.Delete, rotate: {  x: 0, y: 1, z: 0, angle: 360 }, scale: {  x: 0, y: 0 } })               } else {                  Image(item.image)                   .objectFit(ImageFit.Auto).width(px2vp(530))                   .height(px2vp(800)).margin(2)                 // 搜索时的动画                   .transition({  type: TransitionType.Insert, scale: {  x: 0.5, y: 0.5 }, opacity: 0 })                 // 删除时的动画                   .transition({  type: TransitionType.Delete, rotate: {  x: 0, y: 1, z: 0, angle: 360 }, scale: {  x: 0, y: 0 } })               }             },             (item:PictureData) => item.id.toString()           )         }       }       .margin({ left: 10, top: 10, right: 10, bottom: 100})       .padding({ bottom: 10}).align(Alignment.TopStart).width(px2vp(1024))       .height(px2vp(this.picturesHeight))     }   } } 

数据Model

export class PictureData {    id: number;   name: string;   image: Resource;   constructor(id: number, name: string, image: Resource) {      this.id = id;     this.name = name;     this.image = image;   } } 

初始化数据方法

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

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

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

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

https://harmonyos.51cto.com

IT科技
上一篇:扩展Nginx的无限可能:掌握常见扩展模块和第三方插件的使用方法
下一篇:微型数据中心如何实现边缘计算?