HarmonyOS鸿蒙Next中list里在播放音频时如何更新item
HarmonyOS鸿蒙Next中list里在播放音频时如何更新item 使用的是abner/refresh 加载数据,ef_audio 播放音频。当某条itemlayout音频在播放时,列表上需要动态显示播放进度(progress)并且播放图标变化。于此同时,其他的在播的itemlayout 的 progress 需要归零。我是用的更新当前itemlayout上的 Databean 数据,然后刷新的方法。但是这样做,当前条目会一直闪,而且闪几秒应用就崩溃了.有点懵
import { ListView, RefreshController, RefreshDataSource } from '@abner/refresh'
import { DefaultMediaModel, DefaultMediaType, MediaPreview, MediaPreviewOptions } from '@lyb/media-preview'
import { APIs } from '../common/APIs'
import { CommonConstants } from '../common/CommonConstants'
import { DataBean, FileBean } from '../models/DataBean'
import { HttpUtils, ResType } from '../utils/HttpUtils'
import { getRecordTime } from '../utils/Utils'
import HdNav from '../views/HdNav'
import { EfAVPlayer, EfAVPlayerState } from '@yunkss/ef_audio'
@Component
struct MyData {
@State type: number = 0
@State showEmptyLayout: boolean = false
@State heightMap: Map<number, number> = new Map([])
//音频播放
@State currentIndex: number = 0;
efAVPlay: EfAVPlayer = new EfAVPlayer()
controller: RefreshController = new RefreshController() //刷新控制器
lazyDataSource?: RefreshDataSource = new RefreshDataSource()
private page: number = 1;
//可在初始化的时候set回调,注:options 也不一定需要全局申明,可在点击的时候创建
options: MediaPreviewOptions<DefaultMediaModel> = new MediaPreviewOptions<DefaultMediaModel>()
.setPageChangedListener((_item, index) => {
this.index = index
})
.setBackListener(() => {
this.index = -1
})
@State index: number = -1
aboutToAppear(): void {
const params = this.getUIContext().getRouter().getParams() as Record<string, number>; // 获取传递过来的参数对象
if (params) {
this.type = params.type as number
}
this.page = 1
this.getdata()
this.efAVPlay.init()
this.efAVPlay.onStateChange((state: EfAVPlayerState) => {
if (state === 'stopped' || state === 'completed') {
let beans: DataBean[] = this.lazyDataSource?.getDataAll() as DataBean[]
beans.forEach((bean: DataBean) => {
bean.value = 0
bean.play = 0
})
this.lazyDataSource?.modifyAllData()
}
})
this.efAVPlay.onTimeUpdate((time: number) => {
let bean: DataBean = this.lazyDataSource?.getData(this.currentIndex) as DataBean
bean.play = 1
bean.value = time
bean.total = this.efAVPlay.duration
this.lazyDataSource?.changeData(this.currentIndex,bean)
})
}
getdata() {
const params: object =
Object({ 'page': this.page, 'limit': CommonConstants.PAGESIZE, 'cate_id': this.type })
HttpUtils.post(APIs.myData, params, '').then((res: ResType) => {
if (this.page == 1) {
this.controller.finishRefresh()
} else {
this.controller.finishLoadMore()
}
let beans: DataBean[] = res.data
if (beans.length > 0) {
this.showEmptyLayout = false
if (this.page == 1) {
this.lazyDataSource?.initData(beans)
} else {
this.lazyDataSource?.pushDataArray(beans)
}
this.page += 1
} else {
if (this.page == 1) {
this.showEmptyLayout = true
} else {
this.controller.finishLoadMore(true)
}
}
}).catch((err: Error) => {
if (this.page == 1) {
this.showEmptyLayout = true
} else {
this.controller.finishLoadMore(false)
}
})
}
build() {
Column() {
HdNav({
title: this.getTitle()
})
ListView({
lazyDataSource: this.lazyDataSource,
itemLayout: (item, index) => this.itemLayout(item as DataBean, index),
controller: this.controller,
emptyLayout: this.emptyLayout,
showEmptyLayout: this.showEmptyLayout,
isRefreshSticky: false,
onRefresh: () => {
//下拉刷新 模拟耗时
this.page = 1;
this.getdata();
},
onLoadMore: () => {
// 上拉加载 模拟耗时
this.getdata();
}
}).margin({ top: -32 })
}
.height('100%')
.width('100%').backgroundColor($r('app.color.color_EDEEF5'))
}
getTitle(): string {
if (this.type == 1) {
return '我的音频'
} else if (this.type == 2) {
return '我的图片'
} else if (this.type == 3) {
return '我的文本'
} else if (this.type == 4) {
return '我的视频'
} else {
return '我的数据'
}
}
@Builder
itemLayout(item: DataBean, position: number): void {
Column() {
Row() {
// 左侧时间轴
Column() { // 前景圆点和短线
Divider()
.width(2)
.height(14)
.visibility(position == 0 ? Visibility.Hidden : Visibility.Visible)
.backgroundColor($r('app.color.color_D9DAE3'))
Circle()
.width(8)
.height(8)
.fill(Color.White)
.stroke($r('app.color.color_9A1E2E'))
.strokeWidth(2)
Divider()
.width(2)
.backgroundColor($r('app.color.color_D9DAE3'))
.height(this.heightMap.get(position)) // 让分割线填充剩余空间
}
.alignItems(HorizontalAlign.Center)
.margin({ left: 10 })
.width(20) // 固定时间轴宽度
// 右侧内容
Column() {
Text(item.upload_time)
.fontSize(13)
.width('100%')
.padding({
bottom: 10
})
.textAlign(TextAlign.Start)
.alignSelf(ItemAlign.Center)
.fontColor($r('app.color.color_000000'))
Column() {
Text(item.task_title)
.fontSize(13)
.fontColor($r('app.color.color_000000'))
.width('100%')
.textAlign(TextAlign.Start)
.padding({
top: 10,
bottom: 10
})
if (item.cate == 1) {
// 音频播放器
Row() {
Image(item.play == 1 ? $r('app.media.icon_record_data_play') : $r('app.media.icon_record_data_pause'))
.width(22)
.height(22)
.onClick(() => {
if (item.result[0].path) {
this.efAVPlay.stop()
this.currentIndex = position
this.efAVPlay.setUrl(item.result[0].path)
}
})
Progress({ value: item.value, total: item.total })
.layoutWeight(1)
.margin({ left: 8, right: 8 })
Text(getRecordTime(item.result[0].duration))
.fontSize(10)
.fontColor($r('app.color.color_9B9B9B'))
}
.height(36)
.margin({ top: 10 })
.padding({ left: 14, right: 14 })
.backgroundColor($r('app.color.color_EDEEF5'))
.borderRadius(100)
}
//图片或者视频
if (item.cate == 2 || item.cate == 4) {
Row() {
ForEach(item.result.slice(0, Math.min(3, item.result.length)), (fileBean: FileBean, index: number) => {
Stack() {
Image(fileBean.cate == 2 ? fileBean.path : fileBean.snapshot)
.width('31%')// (100% - 2 * 3.5%) / 3 ≈ 31%
.aspectRatio(1)// 保持宽高比 1:1
.borderRadius(8)
.objectFit(ImageFit.Cover)
.onClick(() => {
let resources: DefaultMediaModel[] = getMedia(item.result)
this.options
.setInitIndex(index)
.setMedias(resources)
MediaPreview.open(this.getUIContext(), this.options)
})
if (fileBean.cate == 4) {
Image($r('app.media.icon_data_play')).width(24).height(24)
.objectFit(ImageFit.Fill)
}
if (index === 2 && item.result.length > 3) {
Column() {
Text(`+${item.result.length - 3}`)
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor(Color.White)
}
.width('31%')
.aspectRatio(1)
.backgroundColor('rgba(0, 0, 0, 0.5)')
.borderRadius(8)
.justifyContent(FlexAlign.Center)
}
}.margin({ right: index < 2 ? '3.5%' : 0 })
})
}
.width('100%')
}
// 文件列表
if (item.cate == 3) {
ForEach(item.result, (fileBean: FileBean) => {
Row() {
Image($r('app.media.icon_default_text'))
.width(16)
.height(16)
Text(fileBean.file_name)
.fontSize(12)
.fontColor($r('app.color.color_333333'))
.margin({ left: 8 })
}
.margin({ top: 10 })
.height(36)
.width('100%')
.padding({ left: 14, right: 14 })
.backgroundColor($r('app.color.color_EDEEF5'))
.borderRadius(100)
})
}
// 参数信息
if (item.result.length == 1) {
Text('音频参数:' + item.result[0].data_param)
.fontSize(11)
.width('100%')
.padding({ top: 10, bottom: 10 })
.textAlign(TextAlign.Start)
.fontColor($r('app.color.color_828282'))
}
// 底部操作栏
Row() {
Text('DID:4353')
.fontSize(10)
.fontColor($r('app.color.color_626262'))
.layoutWeight(1)
.visibility(Visibility.Hidden)
Image($r('app.media.icon_audio_message'))
.width(16)
.height(16)
.onClick(() => {
})
Image($r('app.media.icon_audio_edit'))
.width(16)
.height(16)
.margin({ left: 16 })
.visibility(Visibility.None)
}.width('100%')
.height(32)
.padding({ top: 10, bottom: 10 })
}
.padding({ left: 12, right: 12 })
.backgroundColor($r('app.color.color_ffffff'))
.borderRadius(16)
}
.margin({ left: 10, right: 10 })
.layoutWeight(1)
.onAreaChange((oldVal: Area, newVal: Area) => {
let hei = newVal.height.valueOf() as number
this.heightMap.set(position, hei)
})
}.width('100%')
}
}
/*
* 空布局
*/
@Builder
emptyLayout() {
Column() {
Image($r('app.media.icon_data_empty')).width(50).height(50).margin({ top: 40 })
Text("暂时没有数据").fontSize(14).margin({ top: 10 }).fontColor($r('app.color.color_333333'))
}.width("100%")
.height("100%")
.backgroundColor($r('app.color.color_EDEEF5'))
.align(Alignment.Top)
}
aboutToDisappear(): void {
// 在aboutToDisappear中:
this.lazyDataSource?.deleteAll()
}
}
function getMedia(result: FileBean[]): DefaultMediaModel[] {
let models: DefaultMediaModel[] = []
result.forEach(element => {
models.push(new DefaultMediaModel(element.path, element.snapshot,
element.cate == 4 ? DefaultMediaType.VIDEO : DefaultMediaType.IMAGE))
});
return models
}
更多关于HarmonyOS鸿蒙Next中list里在播放音频时如何更新item的实战教程也可以访问 https://www.itying.com/category-93-b0.html
在HarmonyOS鸿蒙Next中,当在List
组件中播放音频时,更新item
可以通过List
组件的setItem
方法来实现。首先,确保你已经在List
组件中绑定了数据源,并且每个item
都有唯一的标识符。当音频播放状态发生变化时,可以通过调用setItem
方法来更新特定的item
。例如,假设你有一个List
组件,并且每个item
包含一个音频播放按钮和播放状态显示。当用户点击播放按钮时,可以先获取当前item
的数据,更新其播放状态,然后调用setItem
方法将更新后的数据重新设置到List
中。这样,List
组件会自动刷新并显示更新后的item
内容。具体代码示例如下:
// 假设listItem是List组件的一个item
let updatedItem = listItem;
updatedItem.audioState = 'playing'; // 更新播放状态
this.list.setItem(listItem.index, updatedItem); // 更新List中的item
更多关于HarmonyOS鸿蒙Next中list里在播放音频时如何更新item的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
在HarmonyOS鸿蒙Next中,当List
中的某个item
正在播放音频时,可以通过以下步骤更新item
:
- 数据更新:首先更新与
item
相关的数据源,例如播放状态、进度等。 - 通知更新:通过
List
的notifyItemChanged(index)
方法通知列表刷新指定的item
,确保UI与数据同步。 - 避免性能问题:如果频繁更新,建议使用
DiffUtil
或AsyncListDiffer
来优化列表更新性能。