鸿蒙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.rhkb.cn/news/292179.html

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

相关文章

构建操作可靠的数据流系统

文章目录 前言数据流动遇到的困难先从简单开始可靠性延迟丢失 性能性能损失性能——分层重试 可扩展性总结 前言 在流式架构中&#xff0c;任何对非功能性需求的漏洞都可能导致严重后果。如果数据工程师没有将可伸缩性、可靠性和可操作性等非功能性需求作为首要考虑因素来构建…

单例设计模式(3)

单例模式&#xff08;3&#xff09; 实现集群环境下的分布式单例类 如何理解单例模式中的唯一性&#xff1f; 单例模式创建的对象是进程唯一的。以springboot应用程序为例&#xff0c;他是一个进程&#xff0c;可能包含多个线程&#xff0c;单例代表在这个进程的某个类是唯一…

mybatis标签解析教程

mybatis标签解析 标签结构 我们在mapper的xml文件中&#xff0c;使用动态SQL&#xff0c;那么这些标签<where>、<if>、<set>、<ForEach>、<Choose>、<Trim> 等是怎么解析的呢&#xff1f;我们先看包的结构 包结构中&#xff0c;script…

【Qt】:多种方式编辑hello world

多种方式编辑hello world 一.QLabel二.对象树三.使用单行编辑框四.使用按钮 (小技巧&#xff1a;1.可以使用F4来进行头文件和对应cpp文件的切换&#xff1b;2.写完一个函数的声名之后,按下altenter,就可以自动的在对应的cpp 文件中添加函数的定义了.) 一.QLabel 注意这里是QSt…

第九届蓝桥杯大赛个人赛省赛(软件类)真题C 语言 A 组-分数

solution1 直观上的分数处理 #include <iostream> using namespace std; int main() {printf("1048575/524288");return 0; }#include<stdio.h> #include<math.h> typedef long long ll; struct fraction{ll up, down; }; ll gcd(ll a, ll b){if…

相册清理大师-手机重复照片整理、垃圾清理软件

相册清理大师是一款超级简单实用的照片视频整理工具。通过便捷的操作手势&#xff0c;帮助你极速整理相册中的照片和视频、释放手机存储空间。 【功能简介】 向上滑动&#xff1a;删除不要的照片 向左滑动&#xff1a;切换下一张照片 向右滑动&#xff1a;返回上一张照片 整理分…

【2023】kafka在linux和docker安装(kafka-1)

目录&#x1f4bb; 一、linux安装kafka1. 安装jdk2. 上传解压到/usr/local目录下3、使用kafka 二、docker安装kafka1. 下载2. 安装zookeeper3. 安装kafka 一、linux安装kafka 环境主机 mac m2、虚拟机Ubuntu22.04.4 1. 安装jdk yum install -y java-1.8.0-openjdk.x86_64下载k…

电商系统秒杀三 秒杀兜底方案之熔断降级

一 秒杀场景介绍 1.1 秒杀场景的特点 1、秒杀具有瞬时高并发的特点&#xff0c;秒杀请求在时间上高度集中于某一特定的时间点&#xff08;秒杀开始那一秒&#xff09;&#xff0c;这样一来&#xff0c;就会导致一个特别高的流量峰值&#xff0c;它对资源的消耗是瞬时的。 2、…

LATTICE进阶篇DDR2--(1)获取官网DDR2例程并仿真

前言 本章主要讲述如何从官网下载DDR2的DEMO例程&#xff0c;并将例程的仿真运行起来。 官网的DEMO在Diamond工程里是没有调用任何任何IP核的&#xff0c;只是在仿真的时候调用了CORE文件夹下的IP核源文件进行仿真&#xff0c;该DEMO工程主要是拿来产生仿真波形&#xff0c;对…

算法学习——LeetCode力扣图论篇2

算法学习——LeetCode力扣图论篇2 1020. 飞地的数量 1020. 飞地的数量 - 力扣&#xff08;LeetCode&#xff09; 描述 给你一个大小为 m x n 的二进制矩阵 grid &#xff0c;其中 0 表示一个海洋单元格、1 表示一个陆地单元格。 一次 移动 是指从一个陆地单元格走到另一个相…

这回轮到鸿蒙禁用安卓了!!!

1月18日&#xff0c;鸿蒙生态千帆仪式上&#xff0c;华为正式宣布了HarmonyOS NEXT&#xff08;下简称鸿蒙星河版或纯血鸿蒙&#xff09;开发者预览已向开发者开放申请&#xff0c;纯血鸿蒙开始走向普及阶段。伴随着不再兼容安卓的纯血鸿蒙铺开&#xff0c;鸿蒙走进了运营属于自…

本地虚拟机服务器修改站点根目录并使用域名访问的简单示例

说明&#xff1a;本文提及效果是使用vmware虚拟机&#xff0c;镜像文件是Rocky8.6 一、配置文件路径 1. /etc/httpd/conf/httpd.conf #主配置文件 2. /etc/httpd/conf.d/*.conf #调用配置文件 调用配置文件的使用&#xff1a; vim /etc/httpd/conf.d/webpage.conf 因为在主配…

python opencv之提取轮廓并拟合圆

图片存储地址为&#xff1a;C:\Users\Pictures\test.png&#xff0c;该图像图片背景是黑色的&#xff0c;目标区域是亮的&#xff0c;目标区域是两段圆弧和两段曲线构成的封闭区域&#xff0c;其中两段圆弧属于同一个圆&#xff0c;但在目标区域的相对位置&#xff0c;也就是不…

CrossOver软件2024免费 最新版本详细介绍 CrossOver软件好用吗 Mac电脑玩Windows游戏

CrossOver是一款由CodeWeavers公司开发的软件&#xff0c;它可以在Mac和Linux等操作系统上运行Windows软件&#xff0c;而无需在计算机上安装Windows操作系统。这款软件的核心技术是Wine&#xff0c;它是一种在Linux和macOS等操作系统上运行Windows应用程序的开源软件。 Cross…

Linux系统---如何理解Linux中的文件系统

顾得泉&#xff1a;个人主页 个人专栏&#xff1a;《Linux操作系统》 《C从入门到精通》 《LeedCode刷题》 键盘敲烂&#xff0c;年薪百万&#xff01; 一、理解文件系统 1.ls与stat 我们使用ls -l的时候看到的除了看到文件名&#xff0c;还看到了文件元数据。 每行包含7列…

rabbitMQ版本问题与下载

都到现在了&#xff0c;大家不会安装东西还是不看版本吧 云服务器买的是centos7&#xff0c;而erlang在24版本后不支持centos7了 所以需要找24版本以下的erlang&#xff0c;而不同erlang对应不同rabbitmq所以需要对应 下载erlang 说实话&#xff0c;自己安装&#xff0c;还是…

前端小白如何理解mvc mvp mvvm

架构、框架、设计模式是都是啥&#xff1f; 架构&#xff1a;抽象出来不同组织或者对象亦或是简单组件&#xff0c;根据需求和各个单元的功能&#xff0c;进行组合排列。 从而完成系统的运行或者是实现目标。 框架&#xff1a;使用什么样的规则&#xff0c;什么样的开发语言&…

个人用户免费,亚马逊正式推出 AI 编程服务 CodeWhisperer

IT之家消息 亚马逊于 2022 年 6 月以预览版的形式&#xff0c;推出了 AI 辅助编程服务 CodeWhisperer。2023年4 月 14 日宣布该服务正式上线&#xff0c;并免费向个人用户开放。点击亚马逊云科技可以注册使用 CodeWhisperer是一种人工智能&#xff08;基于机器学习&#xff09…

消息队列的七种经典应用场景

在笔者心中&#xff0c;消息队列&#xff0c;缓存&#xff0c;分库分表是高并发解决方案三剑客。 在职业生涯中&#xff0c;笔者曾经使用过 ActiveMQ 、RabbitMQ 、Kafka 、RocketMQ 这些知名的消息队列 。 这篇文章&#xff0c;笔者结合自己的真实经历&#xff0c;和大家分享…

搜索与图论——染色法判定二分图

一个图是二分图当且仅当这个图中不含奇数环 由于图中没有奇数环&#xff0c;所以染色过程中一定没有矛盾 所以一个二分图一定可以成功被二染色&#xff0c;反之在二染色的过程中出现矛盾的图中一定有奇数环&#xff0c;也就一定不是二分图 #include<iostream> #includ…