跳到主要内容

案例知识点

从本地JSON文件异步读取数据

src/main/resources/rawfile/products.json

[
{
"id": "001",
"productName": "自行车",
"productPrice": 100
},
{
"id": "002",
"productName": "笔记本",
"productPrice": 10000
}
]

方式一:导入即对象

//src/main/ets/pages/GetDataFromRawfile.ets
import data from '../../resources/rawfile/products.json'
// 我更喜欢这种方式。导入即可使用。无需使用上下文对象,资源管理对象,文本解析对象等。
interface Product {
id: string,
productName: string,
productPrice: number,
}

@Entry
@Component
struct GetDataFromRawfile {
products: Product[] = data as Product[]

build() {
Column({ space: 30 }) {
ForEach(this.products, (item: Product) => {
Row() {
Text(item.productName).fontSize(22)
Text(item.productPrice.toString()).fontSize(22)
}
})
}
.width('100%')
.height('100%')
.backgroundColor($r('app.color.theme_color'))

}
}

方式二:使用上下文对象的资源管理器对象

//src/main/ets/pages/GetDataFromRawfile.ets
import { util } from '@kit.ArkTS'

// 本案例为读取Rawfile文件。其中使用到文本解析工具类。需要模拟器或者真机!!!预览器不支持。
// The decodeToString interface in the previewer is a mocked implementation and may behave differently than on a real device.

interface Product {
id: string,
productName: string,
productPrice: number,
}

@Entry
@Component
struct GetDataFromRawfile {
products: Product[] = []

aboutToAppear(): void {
// 资源管理器中同步获取文件内容,返回一个 Uint8Array 类型的数据
const uint8Array = this.getUIContext().getHostContext()?.resourceManager.getRawFileContentSync('products.json')
// 实例化一个文本解析工具,用于将 Uint8Array 类型的数据解析为字符串
const textDecoder = new util.TextDecoder()
// 调用 textDecoder 的 decodeToString 方法解析数据,解析为字符串
const res = textDecoder.decodeToString(uint8Array)
console.log(`dxin => ${res}`)
this.products = JSON.parse(res) as Product[]
}

build() {
Column({ space: 30 }) {
ForEach(this.products, (item: Product) => {
Row() {
Text(item.productName).fontSize(22)
Text(item.productPrice.toString()).fontSize(22)
}
})
}
.width('100%')
.height('100%')
.backgroundColor($r('app.color.theme_color'))
}
}

官方推荐的一种写法:


// Byte flow into understandable strings
function uint8ArrayToString(array: Uint8Array) {
let textDecoderOptions: util.TextDecoderOptions = {
fatal: false,
ignoreBOM: true
}
let decodeToStringOptions: util.DecodeToStringOptions = {
stream: false
}
let textDecoder = util.TextDecoder.create('utf-8', textDecoderOptions);
let retStr = textDecoder.decodeToString(array, decodeToStringOptions);
console.info('Byte flow into understandable strings:' + retStr);
return retStr;
}

仿抖音视频滑动案例

包含了点赞收藏效果。重点在于如何解决上滑下滑监听

@ObservedV2
class VideoType {
src: Resource
@Trace isLike?: boolean = false
@Trace isFavorited?: boolean = false

constructor(src: Resource, isLike?: boolean, isFavorited?: boolean) {
this.src = src
this.isLike = isLike
this.isFavorited = isFavorited
}
}

@Entry
@ComponentV2
struct Index {
@Local videoArr: VideoType[] = [
new VideoType($rawfile('bro.mp4')),
new VideoType($rawfile('duanju.mp4')),
new VideoType($rawfile('guitar.mp4')),
new VideoType($rawfile('gunshu.mp4')),
new VideoType($rawfile('singer.mp4')),
new VideoType($rawfile('sport.mp4')),
new VideoType($rawfile('xinghe.mp4')),
new VideoType($rawfile('xinghedashui.mp4')),
new VideoType($rawfile('yuefei.mp4'))
]
@Local currentIndex: number = 0

build() {
Stack({ alignContent: Alignment.End }) {
/**
* Video组件:单个视频播放器
* - src: 视频源,
* - previewUri: 预览图URI(当前为空)
* - currentProgressRate: 播放速度(1.0表示正常速度)
*/
Video({
src: this.videoArr[this.currentIndex].src, // 获取视频资源(第三步实现的方法)
previewUri: $r('app.media.douyin'),
currentProgressRate: 1.0
})
.width('100%') // 宽度占满父容器
.height('100%') // 高度占满父容器(全屏显示)
.autoPlay(true) // 自动播放:视频加载后自动开始播放
.controls(true)// 隐藏默认控件:使用自定义UI
.loop(true) // 循环播放:视频播放结束后自动重新开始
.muted(false) // 非静音:播放时带声音
.objectFit(ImageFit.Contain) // 填充方式:Cover表示填充整个容器
.onError((err) => { // 错误处理:视频加载或播放出错时的回调
console.error(`视频播放错误 [视频${this.videoArr[this.currentIndex]}]:`, JSON.stringify(err));
})
.gesture(
// 滑动事件只能判断方向,不能区分上滑下滑
// SwipeGesture({ direction: SwipeDirection.Vertical })
// .onAction((e) => {
// if (e.offsetY < 0) {
// console.log(`dxin offsetY=> ${e.offsetY}-完犊子-上滑`)
// }
// else {
// console.log(`dxin offsetY=> ${e.offsetY}--下滑`)
// }
// this.currentIndex++
// if (this.currentIndex === this.videoArr.length) {
// this.currentIndex = 0
// }
// })

// 拖拽事件:配置拖拽方向为垂直。则只监听垂直方向的拖拽动作。distance可选:拖动2vp距离才会响应。
PanGesture({ direction: PanDirection.Vertical, distance: 2 })
.onActionEnd((e) => {
if (e.offsetY < 0) {
console.log(`dxin offsetY=> ${e.offsetY}--上滑`)
this.currentIndex++
if (this.currentIndex === this.videoArr.length) {
this.currentIndex = 0
}
} else {
console.log(`dxin offsetY=> ${e.offsetY}--下滑`)
this.currentIndex--
if (this.currentIndex <= -1) {
this.currentIndex = this.videoArr.length - 1
}
}
})
)

Column() {
Column() {
// 点赞图标:根据状态显示不同的emoji(第二步定义的状态变量)
Text(this.videoArr[this.currentIndex].isLike ? '❤️' : '🤍')
.fontSize(32)

// 点赞文字:根据状态显示不同的文字
Text(this.videoArr[this.currentIndex].isLike ? '已赞' : '点赞')
.fontSize(14)
.fontColor(Color.White)
.margin({ top: 4 })
}
.width(80)
.height(80)
.justifyContent(FlexAlign.Center)
.onClick(() => { // 点击事件:点击时切换点赞状态(第四步实现的方法)
this.videoArr[this.currentIndex].isLike = !this.videoArr[this.currentIndex].isLike
})

Column() {
// 收藏图标:根据状态显示不同的emoji(第二步定义的状态变量)
Text(this.videoArr[this.currentIndex].isFavorited ? '⭐' : '☆')
.fontSize(32)
// 收藏文字:根据状态显示不同的文字
Text(this.videoArr[this.currentIndex].isFavorited ? '已收藏' : '收藏')
.fontSize(14)
.fontColor(Color.White)
}
.width(80)
.height(80)
.justifyContent(FlexAlign.Center)
.onClick(() => { // 点击事件:点击时切换收藏状态(第四步实现的方法)
this.videoArr[this.currentIndex].isFavorited = !this.videoArr[this.currentIndex].isFavorited
})
}
.width('60') // 宽度:占满整个屏幕宽度
.height('40%') // 高度:100像素,足够容纳按钮和文字
.backgroundColor('rgba(167, 177, 157, 0.8)') // 背景色:半透明黑色(透明度0.4),不遮挡视频
.justifyContent(FlexAlign.SpaceEvenly) // 水平分布:两个按钮均匀分布
}
.width('100%') // 宽度:占满整个屏幕
.height('100%') // 高度:占满整个屏幕(全屏显示)
.backgroundColor(Color.Black) // 背景色:黑色,视频加载前的背景
}
}
加载中...