1. List组件要完成的功能
● 支持下拉刷新
● 支持上拉加载
● 支持自定义结构
● 支持传入数据渲染内容
● 支持设置加载提示文本
● 支持设置加载完成文本
● 支持控制是否显示加载的进度条
2. 基础list组件实现
2.1. 实现列表懒加载
- 懒加载list指南
- 支持自定义结构 -- 设置renderItem 构建函数,后期使用组件可传入item结构
@BuilderParam
renderItem: (item: object) => void
LazyForEach(this.lazyDataSource, (item: object, index: number) => {
ListItem() {
if (this.renderItem) {
this.renderItem(item)
}
}
})
- 使用懒加载,list数据源必须采用 lazyDataSource结构类定义
- 创建 ListDataSource.ets 文件
-
- 创建BasicDataSource类 继承 IDataSource(官方定义)
- 创建ListDataSource类 继承 BasicDataSource,创建操作数据类
export class BasicDataSource implements IDataSource {
private listeners: DataChangeListener[] = [];
private originDataArray: object[] = [];
public totalCount(): number {
return 0;
}
public getData(index: number): object {
return this.originDataArray[index];
}
registerDataChangeListener(listener: DataChangeListener): void {
if (this.listeners.indexOf(listener) < 0) {
console.info('add listener');
this.listeners.push(listener);
}
}
unregisterDataChangeListener(listener: DataChangeListener): void {
const pos = this.listeners.indexOf(listener);
if (pos >= 0) {
console.info('remove listener');
this.listeners.splice(pos, 1);
}
}
notifyDataReload(): void {
this.listeners.forEach(listener => {
listener.onDataReloaded();
})
}
notifyDataAdd(index: number): void {
this.listeners.forEach(listener => {
listener.onDataAdd(index);
})
}
notifyDataChange(index: number): void {
this.listeners.forEach(listener => {
listener.onDataChange(index);
})
}
notifyDataDelete(index: number): void {
this.listeners.forEach(listener => {
listener.onDataDelete(index);
})
}
notifyDataMove(from: number, to: number): void {
this.listeners.forEach(listener => {
listener.onDataMove(from, to);
})
}
}
export class ListDataSource extends BasicDataSource {
private dataArray: object[] = [];
public totalCount(): number {
return this.dataArray.length;
}
public getData(index: number): object {
return this.dataArray[index];
}
public addData(index: number, data: object): void {
this.dataArray.splice(index, 0, data);
this.notifyDataAdd(index);
}
public pushData(data: object): void {
this.dataArray.push(data);
this.notifyDataAdd(this.dataArray.length - 1);
}
public reloadData(data: object[]): void {
this.dataArray = data
this.notifyDataReload()
}
}
import { ListDataSource } from './ListDataSource'
@Component
export struct ListComponent {
@State
lazyDataSource:ListDataSource= new ListDataSource()
@BuilderParam
renderItem: (item: object) => void
build() {
List() {
LazyForEach(this.lazyDataSource, (item: object, index: number) => {
ListItem() {
if (this.renderItem) {
this.renderItem(item)
}
}
})
}
}
}
2.2. 设置底部加载文本
- 根据变量判断底部是否正在加载还是已经加载完毕,显示对应的文字提醒信息
@State
finished: boolean = false
@State
loading: boolean = false
loadingText: string = "拼命加载中..."
finishText: string = "没有拉"
@Builder
getBottomBuilder() {
Row({ space: 10 }) {
if (this.finished) {
Text(this.finishText)
.fontSize(14)
.fontColor($r("app.color.text_secondary"))
} else {
if (this.loading) {
Text(this.loadingText)
.fontSize(14)
.fontColor($r("app.color.text_secondary"))
LoadingProgress()
.width(20)
.aspectRatio(1)
.color($r("app.color.text_secondary"))
}
}
}
.width("100%")
.justifyContent(FlexAlign.Center)
.height(50)
}
List() {
LazyForEach(this.lazyDataSource, (item: object, index: number) => {
.....
})
ListItem() {
this.getBottomBuilder()
}
}
2.3. 支持上拉加载
- 当list 组件滑动到最底部时触发 onReachEnd 事件,我们在这里面写上执行下拉加载
onLoad: () => void = () => {
}
.onReachEnd(async () => {
if (!this.loading && !this.finished) {
this.loading = true
await this.onLoad()
this.loading = false
}
})
2.4. 支持下拉刷新
- 下拉刷新我们需要在最外层包裹一个Refresh组件,里面添加参数
- LoadingProgress() 为加载组件,当我们触发下拉刷新是,可以显示加载效果,加载完毕则隐藏
@State
refreshIng: boolean = false
@Builder
getRefreshDisplay() {
Row({ space: 10 }) {
LoadingProgress()
.width(40)
.aspectRatio(1)
.color($r("app.color.primary"))
Text(this.getStatusText())
.fontSize(14)
.fontColor($r("app.color.text_secondary"))
}
.justifyContent(FlexAlign.Center)
.width("100%")
.height(50)
}
build() {
Refresh({ refreshing: $$this.refreshIng, builder: this.getRefreshDisplay }) {
List() {
...
}
}
}
3. 源代码
import { promptAction } from '@kit.ArkUI'
import { ListDataSource } from './ListDataSource'
@Component
export struct ListComponent {
@State
refreshStatus: RefreshStatus = RefreshStatus.Inactive
@State
refreshIng: boolean = false
@Prop
finished: boolean = false
@Prop
@Watch("updateDataSource")
dataSource: object[] = []
@BuilderParam
renderItem: (item: object) => void
onLoad: () => void = () => {
}
onRefresh: () => void = () => {
}
loadingText: string = "拼命加载中..."
finishText: string = "没有拉"
@State
loading: boolean = false
lazyDataSource: ListDataSource = new ListDataSource()
updateDataSource() {
this.lazyDataSource.reloadData(this.dataSource)
}
@Builder
getBottomBuilder() {
Row({ space: 10 }) {
if (this.finished) {
Text(this.finishText)
.fontSize(14)
.fontColor($r("app.color.text_secondary"))
} else {
if (this.loading) {
Text(this.loadingText)
.fontSize(14)
.fontColor($r("app.color.text_secondary"))
LoadingProgress()
.width(20)
.aspectRatio(1)
.color($r("app.color.text_secondary"))
}
}
}
.width("100%")
.justifyContent(FlexAlign.Center)
.height(50)
}
getStatusText() {
switch (this.refreshStatus) {
case RefreshStatus.Drag:
return "继续下拉"
case RefreshStatus.OverDrag:
return "松手加载"
case RefreshStatus.Refresh:
return "加载中"
}
return ""
}
@Builder
getRefreshDisplay() {
Row({ space: 10 }) {
LoadingProgress()
.width(40)
.aspectRatio(1)
.color($r("app.color.primary"))
Text(this.getStatusText())
.fontSize(14)
.fontColor($r("app.color.text_secondary"))
}
.justifyContent(FlexAlign.Center)
.width("100%")
.height(50)
}
build() {
Refresh({ refreshing: $$this.refreshIng, builder: this.getRefreshDisplay }) {
List() {
LazyForEach(this.lazyDataSource, (item: object, index: number) => {
ListItem() {
if (this.renderItem) {
this.renderItem(item)
}
}
})
ListItem() {
this.getBottomBuilder()
}
}
.onReachEnd(async () => {
if (!this.loading && !this.finished) {
this.loading = true
await this.onLoad()
this.loading = false
}
})
}
.onStateChange(async (state) => {
this.refreshStatus = state
if (state === RefreshStatus.Refresh) {
await this.onRefresh()
this.refreshIng = false
this.loading = false
sound.playAudio("sound.mp3")
}
})
}
}
export class BasicDataSource implements IDataSource {
private listeners: DataChangeListener[] = [];
private originDataArray: object[] = [];
public totalCount(): number {
return 0;
}
public getData(index: number): object {
return this.originDataArray[index];
}
registerDataChangeListener(listener: DataChangeListener): void {
if (this.listeners.indexOf(listener) < 0) {
console.info('add listener');
this.listeners.push(listener);
}
}
unregisterDataChangeListener(listener: DataChangeListener): void {
const pos = this.listeners.indexOf(listener);
if (pos >= 0) {
console.info('remove listener');
this.listeners.splice(pos, 1);
}
}
notifyDataReload(): void {
this.listeners.forEach(listener => {
listener.onDataReloaded();
})
}
notifyDataAdd(index: number): void {
this.listeners.forEach(listener => {
listener.onDataAdd(index);
})
}
notifyDataChange(index: number): void {
this.listeners.forEach(listener => {
listener.onDataChange(index);
})
}
notifyDataDelete(index: number): void {
this.listeners.forEach(listener => {
listener.onDataDelete(index);
})
}
notifyDataMove(from: number, to: number): void {
this.listeners.forEach(listener => {
listener.onDataMove(from, to);
})
}
}
export class ListDataSource extends BasicDataSource {
private dataArray: object[] = [];
public totalCount(): number {
return this.dataArray.length;
}
public getData(index: number): object {
return this.dataArray[index];
}
public addData(index: number, data: object): void {
this.dataArray.splice(index, 0, data);
this.notifyDataAdd(index);
}
public pushData(data: object): void {
this.dataArray.push(data);
this.notifyDataAdd(this.dataArray.length - 1);
}
public reloadData(data: object[]): void {
this.dataArray = data
this.notifyDataReload()
}
}