鸿蒙OS开发实例:【瀑布流式图片浏览】

 介绍

瀑布流式展示图片文字,在当前产品设计中已非常常见,本篇将介绍关于WaterFlow的图片浏览场景,顺便集成Video控件,以提高实践的趣味性

准备

  1. 请参照[官方指导],创建一个Demo工程,选择Stage模型
  2. 熟读HarmonyOS 官方指导“https://gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md”

搜狗高速浏览器截图20240326151547.png

效果

竖屏

image.png

横屏

数据源

鸿蒙OS开发更多内容↓点击HarmonyOS与OpenHarmony技术
鸿蒙技术文档开发知识更新库gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md在这。

功能介绍

  1. 瀑布流式图片展示
  2. 横竖屏图片/视频展示

核心代码

布局

整体结构为:瀑布流 + 加载进度条

每条数据结构: 图片 + 文字 【由于没有设定图片宽高比,因此通过文字长度来自然生成瀑布流效果】

由于有点数据量,按照官方指导,采用LazyForEach懒加载方式

Stack() {WaterFlow({ scroller: this.scroller }) {LazyForEach(dataSource, item => {FlowItem() {Column({ space: 10 }) {Image(item.coverUrl).objectFit(ImageFit.Cover).width('100%').height(this.imageHeight)Text(item.title).fontSize(px2fp(50)).fontColor(Color.Black).width('100%')}.onClick(() => {router.pushUrl({ url: 'custompages/waterflow/Detail', params: item })})}}, item => item)}.columnsTemplate(this.columnsTemplate).columnsGap(5).rowsGap(5).onReachStart(() => {console.info("onReachStart")}).onReachEnd(() => {console.info("onReachEnd")if (!this.running) {if ((this.pageNo + 1) * 15 < this.total) {this.pageNo++this.running = truesetTimeout(() => {this.requestData()}, 2000)}}}).width('100%').height('100%').layoutDirection(FlexDirection.Column)if (this.running) {this.loadDataFooter()}}

横竖屏感知

横竖屏感知整体有两个场景:1. 当前页面发生变化 2.初次进入页面
这里介绍几种监听方式:

当前页面监听

import mediaquery from '@ohos.mediaquery';//这里你也可以使用"orientation: portrait" 参数
listener = mediaquery.matchMediaSync('(orientation: landscape)');
this.listener.on('change', 回调方法)

外部传参

通过UIAbility, 一直传到Page文件

事件传递

采用EeventHub机制,在UIAbility把横竖屏切换事件发出来,Page文件注册监听事件

this.context.eventHub.on('onConfigurationUpdate', (data) => {console.log(JSON.stringify(data))let config = data as Configurationthis.screenDirection = config.directionthis.configureParamsByScreenDirection()
});

API数据请求

这里需要设置Android 或者 iOS 特征UA

requestData() {let url = `https://api.apiopen.top/api/getHaoKanVideo?page=${this.pageNo}&size=15`let httpRequest = http.createHttp()httpRequest.request(url,{header: {"User-Agent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G955U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Mobile Safari/537.36"}}).then((value: http.HttpResponse) => {if (value.responseCode == 200) {let searchResult: SearchResult = JSON.parse(value.result as string)if (searchResult) {this.total = searchResult.result.totalsearchResult.result.list.forEach(ItemModel => {dataSource.addData(ItemModel)})}} else {console.error(JSON.stringify(value))}}).catch(e => {Logger.d(JSON.stringify(e))promptAction.showToast({message: '网络异常: ' + JSON.stringify(e),duration: 2000})}).finally(() => {this.running = false})}

横竖屏布局调整

因为要适应横竖屏,所以需要在原有布局的基础上做一点改造, 让瀑布流的列参数改造为@State 变量 , 让图片高度的参数改造为@State 变量

WaterFlow({ scroller: this.scroller }) {LazyForEach(dataSource, item => {FlowItem() {Column({ space: 10 }) {Image(item.coverUrl).objectFit(ImageFit.Cover).width('100%').height(this.imageHeight)Text(item.title).fontSize(px2fp(50)).fontColor(Color.Black).width('100%')}.onClick(() => {router.pushUrl({ url: 'custompages/waterflow/Detail', params: item })})}}, item => item)
}
.columnsTemplate(this.columnsTemplate)

瀑布流完整代码

API返回的数据结构

import { ItemModel } from './ItemModel'export default class SearchResult{public code: numberpublic message: stringpublic result: childResult
}class childResult {public total: numberpublic list: ItemModel[]
};

Item Model

export class ItemModel{public id: numberpublic tilte: stringpublic userName: stringpublic userPic: stringpublic coverUrl: stringpublic playUrl: stringpublic duration: string
}

WaterFlow数据源接口

import List from '@ohos.util.List';
import { ItemModel } from './ItemModel';export class PicData implements IDataSource {private data: List<ItemModel> = new List<ItemModel>()addData(item: ItemModel){this.data.add(item)}unregisterDataChangeListener(listener: DataChangeListener): void {}registerDataChangeListener(listener: DataChangeListener): void {}getData(index: number): ItemModel {return this.data.get(index)}totalCount(): number {return this.data.length}}

布局

import http from '@ohos.net.http';
import { CommonConstants } from '../../common/CommonConstants';
import Logger from '../../common/Logger';
import { PicData } from './PicData';
import SearchResult from './Result';
import promptAction from '@ohos.promptAction'
import router from '@ohos.router';
import common from '@ohos.app.ability.common';
import { Configuration } from '@ohos.app.ability.Configuration';
import mediaquery from '@ohos.mediaquery';let dataSource = new PicData()/*** 问题: 横竖屏切换,间距会发生偶发性变化* 解决方案:延迟300毫秒改变参数**/
@Entry
@Component
struct GridLayoutIndex {private context = getContext(this) as common.UIAbilityContext;@State pageNo: number = 0total: number = 0@State running: boolean = true@State screenDirection: number = this.context.config.direction@State columnsTemplate: string = '1fr 1fr'@State imageHeight: string = '20%'scroller: Scroller = new Scroller()// 当设备横屏时条件成立listener = mediaquery.matchMediaSync('(orientation: landscape)');onPortrait(mediaQueryResult) {if (mediaQueryResult.matches) {//横屏this.screenDirection = 1} else {//竖屏this.screenDirection = 0}setTimeout(()=>{this.configureParamsByScreenDirection()}, 300)}onBackPress(){this.context.eventHub.off('onConfigurationUpdate')}aboutToAppear() {console.log('已进入瀑布流页面')console.log('当前屏幕方向:' + this.context.config.direction)if (AppStorage.Get('screenDirection') != 'undefined') {this.screenDirection = AppStorage.Get(CommonConstants.ScreenDirection)}this.configureParamsByScreenDirection()this.eventHubFunc()let portraitFunc = this.onPortrait.bind(this)this.listener.on('change', portraitFunc)this.requestData()}@Builder loadDataFooter() {LoadingProgress().width(px2vp(150)).height(px2vp(150)).color(Color.Orange)}build() {Stack() {WaterFlow({ scroller: this.scroller }) {LazyForEach(dataSource, item => {FlowItem() {Column({ space: 10 }) {Image(item.coverUrl).objectFit(ImageFit.Cover).width('100%').height(this.imageHeight)Text(item.title).fontSize(px2fp(50)).fontColor(Color.Black).width('100%')}.onClick(() => {router.pushUrl({ url: 'custompages/waterflow/Detail', params: item })})}}, item => item)}.columnsTemplate(this.columnsTemplate).columnsGap(5).rowsGap(5).onReachStart(() => {console.info("onReachStart")}).onReachEnd(() => {console.info("onReachEnd")if (!this.running) {if ((this.pageNo + 1) * 15 < this.total) {this.pageNo++this.running = truesetTimeout(() => {this.requestData()}, 2000)}}}).width('100%').height('100%').layoutDirection(FlexDirection.Column)if (this.running) {this.loadDataFooter()}}}requestData() {let url = `https://api.apiopen.top/api/getHaoKanVideo?page=${this.pageNo}&size=15`let httpRequest = http.createHttp()httpRequest.request(url,{header: {"User-Agent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G955U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Mobile Safari/537.36"}}).then((value: http.HttpResponse) => {if (value.responseCode == 200) {let searchResult: SearchResult = JSON.parse(value.result as string)if (searchResult) {this.total = searchResult.result.totalsearchResult.result.list.forEach(ItemModel => {dataSource.addData(ItemModel)})}} else {console.error(JSON.stringify(value))}}).catch(e => {Logger.d(JSON.stringify(e))promptAction.showToast({message: '网络异常: ' + JSON.stringify(e),duration: 2000})}).finally(() => {this.running = false})}eventHubFunc() {this.context.eventHub.on('onConfigurationUpdate', (data) => {console.log(JSON.stringify(data))// let config = data as Configuration// this.screenDirection = config.direction// this.configureParamsByScreenDirection()});}configureParamsByScreenDirection(){if (this.screenDirection == 0) {this.columnsTemplate = '1fr 1fr'this.imageHeight = '20%'} else {this.columnsTemplate = '1fr 1fr 1fr 1fr'this.imageHeight = '50%'}}}

图片详情页

import { CommonConstants } from '../../common/CommonConstants';
import router from '@ohos.router';
import { ItemModel } from './ItemModel';
import common from '@ohos.app.ability.common';
import { Configuration } from '@ohos.app.ability.Configuration';@Entry
@Component
struct DetailIndex{private context = getContext(this) as common.UIAbilityContext;extParams: ItemModel@State previewUri: Resource = $r('app.media.splash')@State curRate: PlaybackSpeed = PlaybackSpeed.Speed_Forward_1_00_X@State isAutoPlay: boolean = false@State showControls: boolean = truecontroller: VideoController = new VideoController()@State screenDirection: number = 0@State videoWidth: string = '100%'@State videoHeight: string = '70%'@State tipWidth: string = '100%'@State tipHeight: string = '30%'@State componentDirection: number = FlexDirection.Column@State tipDirection: number = FlexDirection.ColumnaboutToAppear() {console.log('准备加载数据')if(AppStorage.Get('screenDirection') != 'undefined'){this.screenDirection = AppStorage.Get(CommonConstants.ScreenDirection)}this.configureParamsByScreenDirection()this.extParams = router.getParams() as ItemModelthis.eventHubFunc()}onBackPress(){this.context.eventHub.off('onConfigurationUpdate')}build() {Flex({direction: this.componentDirection}){Video({src: this.extParams.playUrl,previewUri: this.extParams.coverUrl,currentProgressRate: this.curRate,controller: this.controller,}).width(this.videoWidth).height(this.videoHeight).autoPlay(this.isAutoPlay).objectFit(ImageFit.Contain).controls(this.showControls).onStart(() => {console.info('onStart')}).onPause(() => {console.info('onPause')}).onFinish(() => {console.info('onFinish')}).onError(() => {console.info('onError')}).onPrepared((e) => {console.info('onPrepared is ' + e.duration)}).onSeeking((e) => {console.info('onSeeking is ' + e.time)}).onSeeked((e) => {console.info('onSeeked is ' + e.time)}).onUpdate((e) => {console.info('onUpdate is ' + e.time)})Flex({direction: this.tipDirection, justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center, alignContent: FlexAlign.Center}){Row() {Button('src').onClick(() => {// this.videoSrc = $rawfile('video2.mp4') // 切换视频源}).margin(5)Button('previewUri').onClick(() => {// this.previewUri = $r('app.media.poster2') // 切换视频预览海报}).margin(5)Button('controls').onClick(() => {this.showControls = !this.showControls // 切换是否显示视频控制栏}).margin(5)}Row() {Button('start').onClick(() => {this.controller.start() // 开始播放}).margin(5)Button('pause').onClick(() => {this.controller.pause() // 暂停播放}).margin(5)Button('stop').onClick(() => {this.controller.stop() // 结束播放}).margin(5)Button('setTime').onClick(() => {this.controller.setCurrentTime(10, SeekMode.Accurate) // 精准跳转到视频的10s位置}).margin(5)}Row() {Button('rate 0.75').onClick(() => {this.curRate = PlaybackSpeed.Speed_Forward_0_75_X // 0.75倍速播放}).margin(5)Button('rate 1').onClick(() => {this.curRate = PlaybackSpeed.Speed_Forward_1_00_X // 原倍速播放}).margin(5)Button('rate 2').onClick(() => {this.curRate = PlaybackSpeed.Speed_Forward_2_00_X // 2倍速播放}).margin(5)}}.width(this.tipWidth).height(this.tipHeight)}}eventHubFunc() {this.context.eventHub.on('onConfigurationUpdate', (data) => {console.log(JSON.stringify(data))let config = data as Configurationthis.screenDirection = config.directionthis.configureParamsByScreenDirection()});}configureParamsByScreenDirection(){if(this.screenDirection == 0){this.videoWidth = '100%'this.videoHeight = '70%'this.tipWidth = '100%'this.tipHeight = '30%'this.componentDirection = FlexDirection.Column} else {this.videoWidth = '60%'this.videoHeight = '100%'this.tipWidth = '40%'this.tipHeight = '100%'this.componentDirection = FlexDirection.Row}}}

鸿蒙开发岗位需要掌握那些核心要领?

目前还有很多小伙伴不知道要学习哪些鸿蒙技术?不知道重点掌握哪些?为了避免学习时频繁踩坑,最终浪费大量时间的。

自己学习时必须要有一份实用的鸿蒙(Harmony NEXT)资料非常有必要。 这里我推荐,根据鸿蒙开发官网梳理与华为内部人员的分享总结出的开发文档。内容包含了:【ArkTS、ArkUI、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、Harmony南向开发、鸿蒙项目实战】等技术知识点。

废话就不多说了,接下来好好看下这份资料。

如果你是一名Android、Java、前端等等开发人员,想要转入鸿蒙方向发展。可以直接领取这份资料辅助你的学习。鸿蒙OpenHarmony知识←前往。下面是鸿蒙开发的学习路线图。

针对鸿蒙成长路线打造的鸿蒙学习文档。鸿蒙(OpenHarmony )学习手册(共计1236页)与鸿蒙(OpenHarmony )开发入门教学视频,帮助大家在技术的道路上更进一步。

其中内容包含:

《鸿蒙开发基础》鸿蒙OpenHarmony知识←前往

  1. ArkTS语言
  2. 安装DevEco Studio
  3. 运用你的第一个ArkTS应用
  4. ArkUI声明式UI开发
  5. .……

《鸿蒙开发进阶》鸿蒙OpenHarmony知识←前往

  1. Stage模型入门
  2. 网络管理
  3. 数据管理
  4. 电话服务
  5. 分布式应用开发
  6. 通知与窗口管理
  7. 多媒体技术
  8. 安全技能
  9. 任务管理
  10. WebGL
  11. 国际化开发
  12. 应用测试
  13. DFX面向未来设计
  14. 鸿蒙系统移植和裁剪定制
  15. ……

《鸿蒙开发实战》鸿蒙OpenHarmony知识←前往

  1. ArkTS实践
  2. UIAbility应用
  3. 网络案例
  4. ……

最后

鸿蒙是完全具备无与伦比的机遇和潜力的;预计到年底将有 5,000 款的应用完成原生鸿蒙开发,这么多的应用需要开发,也就意味着需要有更多的鸿蒙人才。鸿蒙开发工程师也将会迎来爆发式的增长,学习鸿蒙势在必行!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/782722.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

图像分割论文阅读:Automatic Polyp Segmentation via Multi-scale Subtraction Network

这篇论文的主要内容是介绍了一种名为多尺度差值网络&#xff08;MSNet&#xff09;的自动息肉分割方法。 1&#xff0c;模型整体结构 整体结构包括编码器&#xff0c;解码器&#xff0c;编码器和解码器之间是多尺度差值模块模块&#xff08;MSM&#xff09;&#xff0c;以及一…

Vue3 使用 v-bind 动态绑定 CSS 样式

在 Vue3 中&#xff0c;可以通过 v-bind 动态绑定 CSS 样式。 语法格式&#xff1a; color: v-bind(数据); 基础使用&#xff1a; <template><h3 class"title">我是父组件</h3><button click"state !state">按钮</button>…

牛客NC31 第一个只出现一次的字符【simple map Java,Go,PHP】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/1c82e8cf713b4bbeb2a5b31cf5b0417c 核心 Map参考答案Java import java.util.*;public class Solution {/*** 代码中的类名、方法名、参数名已经指定&#xff0c;请勿修改&#xff0c;直接返回方法规定的值即可*…

INA350ABSIDDFR 仪表放大器 单路低功耗 TSOT-23-8

NA350ABSIDDFR 是一款高精度、低功耗、单片式精密运算放大器。它具有出色的直流精度和低失调电压&#xff0c;适用于需要高精度信号处理的应用。这款产品广泛应用于各种领域&#xff0c;如工业控制、医疗设备、测试与测量设备以及通信系统等。 制造商: Texas Instruments …

思维题,LeetCode331. 验证二叉树的前序序列化

一、题目 1、题目描述 序列化二叉树的一种方法是使用 前序遍历 。当我们遇到一个非空节点时&#xff0c;我们可以记录下这个节点的值。如果它是一个空节点&#xff0c;我们可以使用一个标记值记录&#xff0c;例如 #。 例如&#xff0c;上面的二叉树可以被序列化为字符串 &quo…

3.恒定乘积自动做市商算法及代码

中心化交易所的安全风险 在中心化交易所中注册账户时&#xff0c;是由交易所生成一个地址&#xff0c;用户可以向地址充币&#xff0c;充到地址之后交易所就会根据用户充币的数量显示在管理界面中。但是充币的地址是掌管在交易所之中的&#xff0c;资产的控制权还是在交易所。…

从0开始搭建基于VUE的前端项目(二) 安装和配置element-ui组件库

版本和地址 ElementUI 2.15.14 (https://element.eleme.io/)按需引入的插件 babel-plugin-component(1.1.1) https://github.com/ElementUI/babel-plugin-component安装 npm install element-ui完整引入(不建议) 这种方式最后打包的源文件很大,造成网络资源的浪费main.jsimpo…

MFC(二)集成基础控件

目录 OnCreateCStatic【标签&#xff0c;图片】CEdit【文本框&#xff0c;密码框&#xff0c;数值框&#xff0c;文本区】CButton【按钮&#xff0c;单选按钮&#xff0c;多选按钮】CComboBox【下拉列表&#xff0c;列表】CSliderCtrl【滑动条】CListCtrl【表格】CAnimateCtrl【…

C语言分支循环探秘:解锁编程逻辑的无限可能 篇章1

目录 1.if语句 2.关系操作符 3.条件操作符 4.逻辑操作符&#xff1a;&&&#xff0c;||&#xff0c;&#xff01; 5.switch语句 6.while循环 7.for循环 8.do-while循环 9.break和continue语句 10.循环的嵌套 11.goto 导入 C语言是结构化的程序设计语言&…

数字化服务升级:数字乡村改善农民生活质量

随着信息技术的迅猛发展&#xff0c;数字化浪潮已经深入社会的各个角落&#xff0c;为人们的生活带来了翻天覆地的变化。在乡村地区&#xff0c;数字化服务的升级正在逐步改变农民的生活方式&#xff0c;提高他们的生活质量。本文将围绕数字化服务升级&#xff0c;探讨数字乡村…

【蓝桥杯选拔赛真题51】C++百位上的数字 第十四届蓝桥杯青少年创意编程大赛 算法思维 C++编程选拔赛真题解析

目录 C百位上的数字 一、题目要求 1、编程实现 2、输入输出 二、算法分析 三、程序编写 四、程序说明 五、运行结果 六、考点分析 七、推荐资料 C百位上的数字 第十四届蓝桥杯青少年创意编程大赛C选拔赛真题 一、题目要求 1、编程实现 给定一个三位数&#xff0c…

京东云免费服务器申请入口,2024年最新免费云主机

京东云服务器免费6月申请入口 jdyfwq.com 在京东云免费云主机申请页面&#xff0c;免费云服务器配置为云主机2核4G5M和轻量云主机2C2G可以申请免费使用&#xff0c;目前京东云免费云服务器申请时长从之前的6个月缩短到1个月&#xff0c;如下图&#xff1a; 京东云免费云主机 云…

【Java】MyBatis快速入门及详解

文章目录 1. MyBatis概述2. MyBatis快速入门2.1 创建项目2.2 添加依赖2.3 数据准备2.4 编写代码2.4.1 编写核心配置文件2.4.2 编写SQL映射文件2.4.3 编写Java代码 3. Mapper代理开发4. MyBatis核心配置文件5. 案例练习5.1 数据准备5.2 查询数据5.2.1 查询所有数据5.2.2 查询单条…

Cisco ISR 4000 Series IOS XE Release IOSXE-17.13.1a ED

Cisco ISR 4000 Series IOS XE Release IOSXE-17.13.1a ED 思科 4000 系列集成服务路由器系统软件 请访问原文链接&#xff1a;https://sysin.org/blog/cisco-isr-4000/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.org 无耻抄…

Redis数据结构的基础插入操作

数据结构与内部编码 Redis常见的数据结构 数据结构和内部编码 数据结构的插入操作 在Redis中&#xff0c;数据结构的插入操作取决于你要插入的数据类型。以下是一些常见的数据结构和它们的插入操作&#xff1a; 字符串 (String)&#xff1a;使用 SET 命令来插入字符串。例…

yolov5+关键点检测实现溺水检测与警报提示(代码+原理)

往期热门博客项目回顾&#xff1a; 计算机视觉项目大集合 改进的yolo目标检测-测距测速 路径规划算法 图像去雨去雾目标检测测距项目 交通标志识别项目 yolo系列-重磅yolov9界面-最新的yolo 姿态识别-3d姿态识别 深度学习小白学习路线 //正文开始&#xff01; 人…

小狐狸ChatGPT付费AI创作系统V2.8.0独立版 + H5端 + 小程序前端

狐狸GPT付费体验系统的开发基于国外很火的ChatGPT&#xff0c;这是一种基于人工智能技术的问答系统&#xff0c;可以实现智能回答用户提出的问题。相比传统的问答系统&#xff0c;ChatGPT可以更加准确地理解用户的意图&#xff0c;提供更加精准的答案。同时&#xff0c;小狐狸G…

Java算法之哈希算法

Java算法之哈希算法 哈希表 哈希表&#xff08;Hash Table&#xff09;&#xff0c;也称为散列表&#xff0c;是一种根据关键码值&#xff08;Key Value&#xff09;直接进行访问的数据结构。它通过哈希函数&#xff08;Hash Function&#xff09;将关键码值映射到哈希表中的…

【Linux实践室】Linux用户管理实战指南:用户权限切换操作详解

&#x1f308;个人主页&#xff1a;聆风吟_ &#x1f525;系列专栏&#xff1a;Linux实践室、网络奇遇记 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 一. ⛳️任务描述二. ⛳️相关知识2.1 &#x1f514;图形化界面登录2.2 &#x1f514;使用login…

Collection与数据结构 链表与LinkedList (一):链表概述与单向无头非循环链表实现

1.ArrayList的缺点 上篇文章我们已经对顺序表进行了实现,并且对ArrayList进行了使用,我们知道ArrayList底层是使用数组实现的. 由于其底层是一段连续空间&#xff0c;当在ArrayList任意位置插入或者删除元素时&#xff0c;就需要将后序元素整体往前或者往后搬移&#xff0c;时…