HarmonyOS官网案例解析——新闻数据加载(ArkTS)


风晓
风晓 2024-01-09 14:53:04 51273 赞同 0 反对 0
分类: 资源 标签: 鸿蒙
HarmonyOS官网案例解析——新闻数据加载(ArkTS)
一、相关概念
List组件:列表包含一系列相同宽度的列表项。
Tabs:通过页签进行内容视图切换。
TabContent:仅在Tabs中使用,对应一个切换页签的内容视图。
数据请求:提供HTTP数据请求能力。
触摸事件onTouch:触摸动作触发调用该方法。
二、相关权限
网络数据请求需要申请权限:ohos.permission.INTERNET。
 
三、约束与限制
需要搭建服务端环境,参照使用说明NewsDataArkTS,参照文档打开DevEco Studio中Terminal进行配置,默认预览器可以打开预览,不需要开模拟器(重点是电脑内存扛不住)。
 
四、Tab和TabContent的使用
一个Tab对应多个TabContent,每个TabContent会有一个页签标题,因此会有一个页签的数组。
 
创建页签标题数组
import { CommonConstant as Const } from '../common/constant/CommonConstant';
 
export class NewsViewModel{
  getDefaultTypeList(): NewsTypeBean[]{
    return Const.TabBars_DEFAULT_NEWS_TYPES
  }
}
 
export default new NewsViewModel()
 
export class NewsTypeBean{
  id: number = 0
  name:ResourceStr = ''
}
 
实现Tab标签功能
在index.ets中使用该数组
 
@Component
export default struct TabBar {
  @State tabBarArray:NewsTypeBean[] = NewsViewModel.getDefaultTypeList()
}
 
 
通过查看API,我们大致知道怎么使用了(模板代码,不需要记忆)
 
 
@Component
export default struct TabBar {
 
  @State tabBarArray:NewsTypeBean[] = NewsViewModel.getDefaultTypeList()
  @State currentIndex: number = 0;
  @State currentPage: number = 1;
 
  build() {
    Tabs(){
      ForEach(this.tabBarArray,(tabsItem:NewsTypeBean,index:number)=>{
        TabContent(){
          Column(){
            Text('11')
          }
        }.tabBar(this.TabBuilder(tabsItem.id))
      },(item:NewsTypeBean)=>JSON.stringify(item))
    }
    .barHeight(Const.TabBars_BAR_HEIGHT)
    .barMode(BarMode.Scrollable)
    .barWidth(Const.TabBars_BAR_WIDTH)
    .onChange((index: number) => {
      this.currentIndex = index;
      this.currentPage = 1;
    })
    .vertical(false)
  }
 
  @Builder TabBuilder(index:number){
    Column(){
      Text(this.tabBarArray[index].name)
        .height(Const.FULL_HEIGHT)
        .padding({ left: Const.TabBars_HORIZONTAL_PADDING, right: Const.TabBars_HORIZONTAL_PADDING })
        .fontSize(this.currentIndex === index ? Const.TabBars_SELECT_TEXT_FONT_SIZE : Const.TabBars_UN_SELECT_TEXT_FONT_SIZE)
        .fontWeight(this.currentIndex === index ? Const.TabBars_SELECT_TEXT_FONT_WEIGHT : Const.TabBars_UN_SELECT_TEXT_FONT_WEIGHT)
        .fontColor('#182431')
    }
  }
}
 
 
至此,Tab功能已实现。
 
新建NewsList.ets,将currentIndex传递至子组件
 
import NewsViewModel,{ NewsTypeBean} from '../viewModel/NewsViewModel'
import { CommonConstant as Const } from '../common/constant/CommonConstant';
import NewsList from '../view/NewsList'
 
@Component
export default struct TabBar {
  build() {
    ...
        TabContent(){
          Column(){
            NewsList({currentIndex:$currentIndex})
          }
...
 
NewsList中的代码如下
 
@Component
export default struct NewsList {
  @Link currentIndex:number
 
  build() {
    Column() {
      Text(this.currentIndex.toString())
        .fontColor(Color.Red)
    }
  }
}
 
至此,我们发现TabContent中的内容与页面索引保持一致了。
 
 
五、请求网络
module.json5中别忘了加上网络权限
 
    "requestPermissions": [
      {
        "name": "ohos.permission.INTERNET"
      }
    ]
 
1.初步完成数据请求
(1)定义网络请求工具类
export function httpRequestGet(url: string): Promise<ResponseResult> {
  let httpRequest = http.createHttp();
  // 发送数据请求
  let responseResult = httpRequest.request(url, {
    method: http.RequestMethod.GET,
    readTimeout: Const.HTTP_READ_TIMEOUT,
    header: {
      'Content-Type': ContentType.JSON
    },
    connectTimeout: Const.HTTP_READ_TIMEOUT,
    extraData: {}
  });
 
  let serverData: ResponseResult = new ResponseResult();
 
  // Processes the data and returns.
  // 处理数据,并返回
  return responseResult.then((value: http.HttpResponse) => {
    if (value.responseCode === Const.HTTP_CODE_200) {
      // Obtains the returned data.
      // 处理数据,并返回
      let result = `${value.result}`;
      let resultJson: ResponseResult = JSON.parse(result);
      if (resultJson.code === Const.SERVER_CODE_SUCCESS) {
        serverData.data = resultJson.data;
      }
      serverData.code = resultJson.code;
      serverData.msg = resultJson.msg;
    } else {
      serverData.msg = `${'网络错误'}&${value.responseCode}`;
    }
    return serverData;
  }).catch(() => {
    serverData.msg = '网络错误';
    return serverData;
  })
}
 
 
(2)在NewsViewModel.ets中定义请求的方法,并配置解析的参数
import prompt from '@ohos.prompt';
import { CommonConstant as Const } from '../common/constant/CommonConstant';
import { httpRequestGet } from '../common/utils/HttpUtil';
import Logger from '../common/utils/Logger';
 
export class NewsViewModel {
 ....
 
  // 获取服务端新闻数据列表
  getNewsList(currentPage: number, pageSize: number, path: string): Promise<NewsData[]> {
    return new Promise(async (resolve: Function, reject: Function) => {
      let url = `${Const.SERVER}/${path}`;
      url += '?currentPage=' + currentPage + '&pageSize=' + pageSize;
      httpRequestGet(url).then((data: ResponseResult) => {
        if (data.code === Const.SERVER_CODE_SUCCESS) {
          resolve(data.data);
        } else {
          Logger.error('getNewsList failed', JSON.stringify(data));
          reject(Const.TabBars_DEFAULT_NEWS_TYPES)
        }
      })
        .catch((err: Error) => {
          Logger.error('getNewsList failed', JSON.stringify(err));
          reject(Const.TabBars_DEFAULT_NEWS_TYPES)
        });
    });
  }
}
 
...
 
export class ResponseResult {
  code: string
  msg: ResourceStr
  data: string | Object | ArrayBuffer
 
  constructor() {
    this.code = '';
    this.msg = '';
    this.data = '';
  }
}
 
export class NewsData {
  title: string = ''
  content: string = ''
  imagesUrl: Array<NewsFile> = [];
  source: string = ''
}
 
export class NewsFile {
  id: number = 0
  url: string = ''
  type: number = 0
  newId: number = 0
}
 
 
Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由JavaScript引擎提供,不用自己部署。
 
(3)在NewsList.ets中使用已定义好的方法
调用页面的生命周期aboutToAppear()方法,方法内再调用changeCategory()方法
 
  aboutToAppear() {
    this.changeCategory()
  }
  
  changeCategory() {
    NewsViewModel.getNewsList(this.currentPage, this.pageSize, Const.GET_NEWS_LIST)
      .then((data: NewsData[]) => {
        this.newsData = data;
      })
  }
 
(4)获取数据后,渲染数据,NewsList.ets代码如下:
import NewsViewModel, { NewsData } from '../viewModel/NewsViewModel'
import { CommonConstant as Const, PageState } from '../common/constant/CommonConstant';
 
@Component
export default struct NewsList {
  @Link currentIndex: number
  currentPage: number = 1;
  pageSize: number = Const.PAGE_SIZE;
  @State newsData: Array<NewsData> = [];
 
  changeCategory() {
    NewsViewModel.getNewsList(this.currentPage, this.pageSize, Const.GET_NEWS_LIST)
      .then((data: NewsData[]) => {
        this.newsData = data;
      })
  }
 
  aboutToAppear() {
    this.changeCategory()
  }
 
  build() {
    Column() {
      List() {
        ForEach(this.newsData, (item: NewsData) => {
          ListItem() {
            Text(item.title)
              .width('100%')
              .fontColor(Color.Red)
          }
          .height(256)
          .width('100%')
          .backgroundColor(Color.White)
          .margin({ top: 12 })
          .borderRadius(Const.NewsListConstant_ITEM_BORDER_RADIUS)
        }, (item: NewsData, index?: number) => JSON.stringify(item) + index)
      }
      .width('93.3%')
      .height('100%')
      .backgroundColor('#f1f3f5')
    }
  }
}
 
 
 
 
案例效果分析:此时我们发现,只有首次进入的时候有“哈哈哈”的提示,此后切换页面就不会再提示,因此无法检测到页面的切换。那么,我们该怎么实现呢?
答:用==@watch==
 
@Component
export default struct NewsList {
  @Watch('changeCategory')@Link currentIndex: number
  ...
}
 
当currentIndex改变时,会时刻调用changeCategory()方法。因此,可以做到,当页面进行切换时,可以获取当前index界面的数据。
 
 
 
此后在进行页面切换时,都会事实调用changeCategory()。
 
ps:根据页面的不同,可以拼接不同的参数,案例都是调用同一个接口,因此数据是一样的。实际工作中,可以把index拼到链接中传递给后端,让后端返回不同的参数,从而显示不同的界面。
 
2.网络请求优化
实际工作中,肯定有==“请求成功”、”请求失败“、”数据加载中“、”下拉刷新“、”上拉加载“==等各种场景。
 
参照案例,新建NewsModel.ets
 
export default class NewsModel {
  newsData: Array<NewsData> = [];
  currentPage: number = 1;
  pageSize: number = Const.PAGE_SIZE;
  pullDownRefreshText: Resource = $r('app.string.pull_down_refresh_text');
  pullDownRefreshImage: Resource = $r('app.media.ic_pull_down_refresh');
  pullDownRefreshHeight: number = Const.CUSTOM_LAYOUT_HEIGHT;
  isVisiblePullDown: boolean = false;
  pullUpLoadText: Resource = $r('app.string.pull_up_load_text');
  pullUpLoadImage: Resource = $r('app.media.ic_pull_up_load');
  pullUpLoadHeight: number = Const.CUSTOM_LAYOUT_HEIGHT;
  isVisiblePullUpLoad: boolean = false;
  offsetY: number = 0;
  pageState: number = PageState.Loading;
  hasMore: boolean = true;
  startIndex = 0;
  endIndex = 0;
  downY = 0;
  
  lastMoveY = 0;
  isRefreshing: boolean = false;
  isCanRefresh = false;
  isPullRefreshOperation = false;
  isLoading: boolean = false;
  isCanLoadMore: boolean = false;
}
 
 
import NewsViewModel, { NewsData } from '../viewModel/NewsViewModel'
import { CommonConstant as Const, PageState } from '../common/constant/CommonConstant';
import prompt from '@ohos.prompt';
import NewsModel from '../viewModel/NewsModel';
 
@Component
export default struct NewsList {
  @Watch('changeCategory')@Link currentIndex: number
  @State newsModel: NewsModel = new NewsModel()
...//报错的地方自行修改,加上.newsModel即可
}
 
2.1 “请求成功”、”请求失败“、”数据加载中“
先定义pageState为PageState.Loading,当请求成功时,设置成PageState.Success,请求失败时,设置成PageState.Fail
 
先完善NewsList.ets中changeCategory代码
 
当数据请求成功时,加一个请求成功状态码PageState.Success
当数据请求失败时,加一个请求失败状态码PageState.Fail
changeCategory() {
    NewsViewModel.getNewsList(this.newsModel.currentPage, this.newsModel.pageSize, Const.GET_NEWS_LIST)
      .then((data: NewsData[]) => {
        this.newsModel.pageState = PageState.Success;
        this.newsModel.newsData = data;
      })
      .catch((err:string | Resource)=>{
        this.newsModel.pageState = PageState.Fail
      })
  }
 
根据请求状态码,显示不同的界面,因newsModel对象中的属性被@state修饰,因此视图是可以刷新的
 
    Column() {
      if (this.newsModel.pageState === PageState.Success) {
        ...
      } else if (this.newsModel.pageState === PageState.Loading) {
       ...
      } else {
       ...
      }
    }
 
NewsList.ets完整代码如下:
 
import NewsViewModel, { NewsData } from '../viewModel/NewsViewModel'
import { CommonConstant as Const, PageState } from '../common/constant/CommonConstant';
import prompt from '@ohos.prompt';
import NewsModel from '../viewModel/NewsModel';
import promptAction from '@ohos.promptAction';
import Logger from '../common/utils/Logger';
 
@Component
export default struct NewsList {
  @Watch('changeCategory')@Link currentIndex: number
  @State newsModel: NewsModel = new NewsModel()
 
  changeCategory() {
    NewsViewModel.getNewsList(this.newsModel.currentPage, this.newsModel.pageSize, Const.GET_NEWS_LIST)
      .then((data: NewsData[]) => {
        this.newsModel.pageState = PageState.Success;
        this.newsModel.newsData = data;
      })
      .catch((err:string | Resource)=>{
        this.newsModel.pageState = PageState.Fail
      })
  }
 
  aboutToAppear() {
    this.changeCategory()
  }
 
  build() {
    Column() {
      if (this.newsModel.pageState === PageState.Success) {
        //请求成功
      } else if (this.newsModel.pageState === PageState.Loading) {
        //加载中
        Text('数据加载中')
      } else {
        //请求失败
        Text('请求失败')
      }
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
 
}
 
 
 
 
2.2完善“请求成功”、”请求失败“、”数据加载中“具体界面
2.2.1 ”请求失败“
import NewsViewModel, { CustomRefreshLoadLayoutClass, NewsData } from '../viewModel/NewsViewModel'
import { CommonConstant as Const, PageState } from '../common/constant/CommonConstant';
import prompt from '@ohos.prompt';
import NewsModel from '../viewModel/NewsModel';
import promptAction from '@ohos.promptAction';
import Logger from '../common/utils/Logger';
import { CustomRefreshLoadLayout } from './CustomRefreshLoadLayout';
 
@Component
export default struct NewsList {
  @Watch('changeCategory') @Link currentIndex: number
  @State newsModel: NewsModel = new NewsModel()
 
  changeCategory() {
    NewsViewModel.getNewsList(this.newsModel.currentPage, this.newsModel.pageSize, Const.GET_NEWS_LIST)
      .then((data: NewsData[]) => {
        this.newsModel.pageState = PageState.Success;
        this.newsModel.newsData = data;
      })
      .catch(() => {
        this.newsModel.pageState = PageState.Fail
      })
  }
 
  aboutToAppear() {
    this.changeCategory()
  }
 
  build() {
    Column() {
      if (this.newsModel.pageState === PageState.Success) {
 
      } else if (this.newsModel.pageState === PageState.Loading) {
 
      } else {
        //请求失败
        this.FailLayout()
      }
 
    }
    .width('100%')
    .height('100%')
    .backgroundColor(Color.White)
    .justifyContent(FlexAlign.Center)
  }
 
 
  @Builder FailLayout() {
    Image($r('app.media.none'))
      .height(Const.NewsListConstant_NONE_IMAGE_SIZE)
      .width(Const.NewsListConstant_NONE_IMAGE_SIZE)
    Text('网络加载失败')
      .opacity(Const.NewsListConstant_NONE_TEXT_opacity)
      .fontSize(Const.NewsListConstant_NONE_TEXT_size)
      .fontColor('#182431')
      .margin({ top: Const.NewsListConstant_NONE_TEXT_margin })
  }
}
 
 
2.2 先显示加载中,然后显示失败的界面。
先定义CustomRefreshLoadLayout.ets,画出界面,以及参数传递。
 
(1)在NewsViewModel.ets中添加如下代码
 
@Observed
export class CustomRefreshLoadLayoutClass{
  isVisible:boolean 
  imageSrc:Resource
  textValue:string
  heightValue:number
 
  constructor(isVisible: boolean, imageSrc: Resource, textValue: string, heightValue: number) {
    this.isVisible = isVisible;
    this.imageSrc = imageSrc;
    this.textValue = textValue;
    this.heightValue = heightValue;
  }
}
 
(2)在CustomRefreshLoadLayout.ets中添加如下代码
 
import { CustomRefreshLoadLayoutClass } from '../viewModel/NewsViewModel'
@Component
export struct CustomRefreshLoadLayout {
  @ObjectLink customRefreshLoadClass:CustomRefreshLoadLayoutClass
  build() {
    Row() {
      Image(this.customRefreshLoadClass.imageSrc)
        .width(18)
        .height(18)
 
      Text(this.customRefreshLoadClass.textValue)
        .margin({left:7})
        .fontSize(17)
        // .textAlign(TextAlign.Center)
    }
    .clip(true)
    .width('100%')
    .justifyContent(FlexAlign.Center)
    .height(this.customRefreshLoadClass.heightValue)
  }
}
 
 
ps:聪明的你已经发现了,这里用到了==@Observed和@ObjectLink==
 
总结:数组的元素是对象,当对象的属性发生修改时,不能触发视图的重新渲染。
 
  @Builder LoadLayout() {
    CustomRefreshLoadLayout({
      customRefreshLoadClass: new CustomRefreshLoadLayoutClass(true, $r('app.media.ic_pull_up_load'), '加载中', this.newsModel.pullDownRefreshHeight)
    })
  }
 
 
 
我们发现,后端环境正常时,数据加载正常;后端服务没起时,数据加载异常。
 
完整代码如下:
 
import NewsViewModel, { CustomRefreshLoadLayoutClass, NewsData } from '../viewModel/NewsViewModel'
import { CommonConstant as Const, PageState } from '../common/constant/CommonConstant';
import NewsModel from '../viewModel/NewsModel';
import { CustomRefreshLoadLayout } from './CustomRefreshLoadLayout';
 
@Component
export default struct NewsList {
  @Watch('changeCategory') @Link currentIndex: number
  @State newsModel: NewsModel = new NewsModel()
 
  changeCategory() {
    NewsViewModel.getNewsList(this.newsModel.currentPage, this.newsModel.pageSize, Const.GET_NEWS_LIST)
      .then((data: NewsData[]) => {
        this.newsModel.pageState = PageState.Success;
        this.newsModel.newsData = data;
      })
      .catch(() => {
        this.newsModel.pageState = PageState.Fail
      })
  }
 
  aboutToAppear() {
    this.changeCategory()
  }
 
  build() {
    Column() {
      if (this.newsModel.pageState === PageState.Success) {
        //请求成功
 
      } else if (this.newsModel.pageState === PageState.Loading) {
        //加载中
        this.LoadLayout()
      } else {
        //请求失败
        this.FailLayout()
      }
    }
    .width('100%')
    .height('100%')
    .backgroundColor(Color.White)
    .justifyContent(FlexAlign.Center)
  }
 
  @Builder ListLayout() {
    List() {
      ForEach(this.newsModel.newsData, (item: NewsData) => {
        ListItem() {
          Text(item.title)
            .width('100%')
            .fontColor(Color.Red)
        }
        .height(256)
        .width('100%')
        .backgroundColor(Color.White)
        .margin({ top: 12 })
        .borderRadius(Const.NewsListConstant_ITEM_BORDER_RADIUS)
 
      }, (item: NewsData, index?: number) => JSON.stringify(item) + index)
    }
    .width('93.3%')
    .height('100%')
    .backgroundColor('#f1f3f5')
  }
 
  @Builder LoadLayout() {
    CustomRefreshLoadLayout({
      customRefreshLoadClass: new CustomRefreshLoadLayoutClass(true, $r('app.media.ic_pull_up_load'), '加载中', this.newsModel.pullDownRefreshHeight)
    })
  }
 
  @Builder FailLayout() {
    Image($r('app.media.none'))
      .height(Const.NewsListConstant_NONE_IMAGE_SIZE)
      .width(Const.NewsListConstant_NONE_IMAGE_SIZE)
    Text('网络加载失败')
      .opacity(Const.NewsListConstant_NONE_TEXT_opacity)
      .fontSize(Const.NewsListConstant_NONE_TEXT_size)
      .fontColor('#182431')
      .margin({ top: Const.NewsListConstant_NONE_TEXT_margin })
  }
}
 
 
3.完善listItem界面
 
新建NewsItem.ets
 
import { NewsData, NewsFile } from '../viewModel/NewsViewModel'
import { CommonConstant, CommonConstant as Const } from '../common/constant/CommonConstant';
 
@Component
export struct NewsItem {
  newsData:NewsData = new NewsData()
  build() {
    Column() {
      Row(){
        Image($r('app.media.news'))
          .width('11.9%')
          .height(20)
          .objectFit(ImageFit.Fill)
        Text(this.newsData.title)
          .fontSize(20)
          .fontColor('#000000')
          .width('78.6%')
          .maxLines(1)
          .margin({ left: '2.4%' })
          .textOverflow({ overflow: TextOverflow.Ellipsis })
          .fontWeight(FontWeight.Regular)
      }
      .alignItems(VerticalAlign.Center)
      .height(22)
 
      Text(this.newsData.content)
        .fontSize(14)
        .fontColor('#000000')
        .width('93%')
        .height('16.8%')
        .maxLines(2)
        .margin({ top:'3.5%' })
        .textOverflow({ overflow: TextOverflow.Ellipsis })
        .fontWeight(FontWeight.Regular)
      
      Grid(){
        ForEach(this.newsData.imagesUrl,(itemImg:NewsFile)=>{
          GridItem(){
            Image(Const.SERVER+itemImg.url)
              .objectFit(ImageFit.Cover)
              .borderRadius(8)
          }
 
        },(itemImg:NewsFile,index?:number)=>JSON.stringify(itemImg)+index)
      }
      .columnsTemplate('1fr'.repeat(this.newsData.imagesUrl.length))
      .columnsGap(5)
      .rowsTemplate('1fr')
      .width('93%')
      .height('31.5%')
      .margin({left:'3.5%',right:'3.5%',top:'5%'})
 
      Text(this.newsData.source)
        .fontSize(Const.NewsSource_FONT_SIZE)
        .fontColor('#FF989898')
        .height(Const.NewsSource_HEIGHT)
        .width(Const.NewsSource_WIDTH)
        .maxLines(Const.NewsSource_MAX_LINES)
        .margin({ left: Const.NewsSource_MARGIN_LEFT, top: Const.NewsSource_MARGIN_TOP })
        .textOverflow({ overflow: TextOverflow.None })
    }
  }
}
 
 
import NewsViewModel, { CustomRefreshLoadLayoutClass, NewsData } from '../viewModel/NewsViewModel'
import { CommonConstant as Const, PageState } from '../common/constant/CommonConstant';
import NewsModel from '../viewModel/NewsModel';
import { CustomRefreshLoadLayout } from './CustomRefreshLoadLayout';
import { NewsItem } from './NewsItem';
 
@Component
export default struct NewsList {
...
  @Builder ListLayout() {
    List() {
      ForEach(this.newsModel.newsData, (item: NewsData) => {
        ListItem() {
          NewsItem({newsData:item})
        }
       ...
      }
    }
    ...
  }
...
}
 
 
 
 
哇,看到界面,神清气爽~~~
 
六、下拉刷新、上拉加载
import NewsViewModel, { CustomRefreshLoadLayoutClass, NewsData } from '../viewModel/NewsViewModel'
import { CommonConstant as Const, PageState } from '../common/constant/CommonConstant';
import NewsModel from '../viewModel/NewsModel';
import { CustomRefreshLoadLayout } from './CustomRefreshLoadLayout';
import { NewsItem } from './NewsItem';
import promptAction from '@ohos.promptAction';
import Logger from '../common/utils/Logger';
import { LoadMoreLayout } from './LoadMoreLayout';
import { NoMoreLayout } from './NoMoreLayout';
import { listTouchEvent } from '../common/utils/PullDownRefresh';
import prompt from '@ohos.prompt';
import RefreshLayout from './RefreshLayout';
 
@Component
export default struct NewsList {
  @Watch('changeCategory') @Link currentIndex: number
  @State newsModel: NewsModel = new NewsModel()
 
  changeCategory() {
    NewsViewModel.getNewsList(this.newsModel.currentPage, this.newsModel.pageSize, Const.GET_NEWS_LIST)
      .then((data: NewsData[]) => {
        this.newsModel.pageState = PageState.Success;
        if (data.length === this.newsModel.pageSize) {
          this.newsModel.currentPage++
          this.newsModel.hasMore = true
        }else {
          this.newsModel.hasMore = false
        }
        this.newsModel.newsData = data;
      })
      .catch(() => {
        this.newsModel.pageState = PageState.Fail
      })
  }
 
 
  build() {
    Column() {
      if (this.newsModel.pageState === PageState.Success) {
        this.ListLayout()
      } else if (this.newsModel.pageState === PageState.Loading) {
        this.LoadLayout()
      } else {
        this.FailLayout()
      }
    }
...
    .onTouch((event: TouchEvent | undefined) => {
      if (event) {
        if (this.newsModel.pageState === PageState.Success) {
          listTouchEvent(this.newsModel, event);
        }
      }
    })
  }
 
  @Builder ListLayout() {
    List() {
      ListItem() {
        RefreshLayout({
          refreshLayoutClass: new CustomRefreshLoadLayoutClass(this.newsModel.isVisiblePullDown, this.newsModel.pullDownRefreshImage,
            this.newsModel.pullDownRefreshText, this.newsModel.pullDownRefreshHeight)
        })
      }
...
      ListItem(){
        if (this.newsModel.hasMore){
          LoadMoreLayout({
            loadMoreLayoutClass:new CustomRefreshLoadLayoutClass(this.newsModel.isVisiblePullUpLoad,
            this.newsModel.pullUpLoadImage,this.newsModel.pullUpLoadText,this.newsModel.pullUpLoadHeight)
          })
        }else {
          NoMoreLayout()
        }
      }
    }
    .width('93.3%')
    .height('100%')
    .backgroundColor('#f1f3f5')
    .margin({ left: Const.NewsListConstant_LIST_MARGIN_LEFT, right: Const.NewsListConstant_LIST_MARGIN_RIGHT })
    .divider({
      color: '#e2e2e2',
      strokeWidth: Const.NewsListConstant_LIST_DIVIDER_STROKE_WIDTH,
      endMargin: Const.NewsListConstant_LIST_MARGIN_RIGHT
    })
    // Remove the rebound effect.
    .edgeEffect(EdgeEffect.None)
    .offset({ x: 0, y: `${this.newsModel.offsetY}px` })
    .onScrollIndex((start: number, end: number) => {
      // Listen to the first index of the current list.
      this.newsModel.startIndex = start;
      this.newsModel.endIndex = end;
    })
  }
}
 
 
以下文件可直接CV官网PullDownRefresh.ets、PullUpLoadMore.ets、LoadMoreLayout.ets、NoMoreLayout.ets、RefreshLayout.ets
 
效果如下:
 
 
 
至此,所有功能已全部完成。
 

如果您发现该资源为电子书等存在侵权的资源或对该资源描述不正确等,可点击“私信”按钮向作者进行反馈;如作者无回复可进行平台仲裁,我们会在第一时间进行处理!

评价 0 条
风晓L1
粉丝 1 资源 2038 + 关注 私信
最近热门资源
银河麒麟桌面操作系统备份用户数据  130
统信桌面专业版【全盘安装UOS系统】介绍  128
银河麒麟桌面操作系统安装佳能打印机驱动方法  120
银河麒麟桌面操作系统 V10-SP1用户密码修改  108
麒麟系统连接打印机常见问题及解决方法  28
最近下载排行榜
银河麒麟桌面操作系统备份用户数据 0
统信桌面专业版【全盘安装UOS系统】介绍 0
银河麒麟桌面操作系统安装佳能打印机驱动方法 0
银河麒麟桌面操作系统 V10-SP1用户密码修改 0
麒麟系统连接打印机常见问题及解决方法 0
作者收入月榜
1

prtyaa 收益393.62元

2

zlj141319 收益218元

3

1843880570 收益214.2元

4

IT-feng 收益210.13元

5

风晓 收益208.24元

6

777 收益172.71元

7

Fhawking 收益106.6元

8

信创来了 收益105.84元

9

克里斯蒂亚诺诺 收益91.08元

10

技术-小陈 收益79.5元

请使用微信扫码

加入交流群

请使用微信扫一扫!