当前位置: 首页 > 编程日记 > 正文

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

官网效果图

本篇Codelab是基于ArkTS的声明式开发范式实现的样例,主要介绍了数据请求和touch事件的使用。包含以下功能:
1.数据请求。
2.列表下拉刷新。
3.列表上拉加载。
官方代码链接:[https://gitee.com/harmonyos/codelabs/tree/master/NewsDataArkTS](Codelabs: 分享知识与见解,一起探索HarmonyOS的独特魅力。 - Gitee.com)

一、相关概念

  • 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构造函数接受一个函数作为参数,该函数的两个参数分别是resolvereject。它们是两个函数,由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')
    }
  }
}

sloop

案例效果分析:此时我们发现,只有首次进入的时候有“哈哈哈”的提示,此后切换页面就不会再提示,因此无法检测到页面的切换。那么,我们该怎么实现呢?
答:用==@watch==

@Component
export default struct NewsList {
  @Watch('changeCategory')@Link currentIndex: number
  ...
}

当currentIndex改变时,会时刻调用changeCategory()方法。因此,可以做到,当页面进行切换时,可以获取当前index界面的数据。

sloop

此后在进行页面切换时,都会事实调用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)
  }

}

等待视频加载时间5秒返回结果

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)
    })
  }

33

我们发现,后端环境正常时,数据加载正常;后端服务没起时,数据加载异常。

完整代码如下:

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})
        }
       ...
      }
    }
    ...
  }
	...
}

image-20231220225752914

哇,看到界面,神清气爽~~~

六、下拉刷新、上拉加载

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

效果如下:

33

至此,所有功能已全部完成。

代码链接:https://gitee.com/runitwolf/NewsDataArkTS

typora笔记链接:https://gitee.com/runitwolf/NewsDataArkTS/blob/master/NewsDataArkTS.md

ps: 软件有点小bug,不知道是开发软件的问题还是代码的问题,点击NewsModel的isCanLoadMore,提示No usages found in All Places Press Ctrl+Alt+F7 again to search in ‘Project Files’,但是我明明有地方使用啊,没找到解决方案。

相关文章:

openharmony开发版应用安装签名

配置签名信息应用/服务在真机设备上运行,需要提前为应用/服务进行签名,DevEco Studio为开发者提供了自动化签名方案,可以一键完成应用/服务签名。具体操作如下:单击File > Project Structure > Project > Signing Configs界面勾选Automatically generate signature,等待自动签名完成即可,单击OK。如下图所示:说明。

说说你对 TypeScript 的理解?与 JavaScript 的区别?

超集,不得不说另外一个概念,子集,怎么理解这两个呢,举个例子,如果一个集合 A 里面的的所有元素集合 B 里面都存在,那么我们可以理解集合 B 是集合 A 的超集,集合 A 为集合 B 的子集。其是一种静态类型检查的语言,提供了类型注解,在代码编译阶段就可以检查出数据类型的错误。通过类型批注提供在编译时启动类型检查的静态类型,这是可选的,而且可以忽略而使用。如果缺乏声明而不能推断出类型,那么它的类型被视作默认的动态。等数据格式,对象的类型就是用接口来描述的。的语法,所以任何现有的。对于基本类型的批注是。

鸿蒙Harmony-页面路由(router)详解

慢慢理解世界,慢慢更新自己,希望你的每一个昨天,今天,和明天都会很快乐,你知道的,先好起来的从来都不是生活,而是你自己

鸿蒙harmony--数据库sqlite详解

今天是1月20号星期六,早安,岁末大寒至,静后春归来。愿他乡故人,漂泊有归宿,前程有奔赴,愿人间不寒,温暖常伴,诸事顺利,喜乐长安。

HarmonyOS 应用开发入门

HarmonyOS 应用有两种模型,分别是 FA(Feature Ability)模型和Stage模型。FA模型ArkTS应用(过时)JS应用(最新版IDE已不再支持)Stage模型ArkTS应用(推荐)应用模型的演进API 4-8 仅支持FA模型,API 9 后新增 Stage模型,是目前主推且会长期演进的模型,FA 暂时保留但不推荐。Stage模型的优点为复杂应用而设计支持多设备和多窗口形态平衡应用能力和系统管控成本对比传统FA模型和Stage模型。

解决鸿蒙APP的内存泄漏

解决鸿蒙应用的内存泄漏问题需要开发者对鸿蒙框架和组件的生命周期有深入的了解,以确保资源能够在适当的时候得到释放。LeakCanary同样可以用于鸿蒙应用,它是一款流行的内存泄漏检测库,可以在应用运行时检测内存泄漏并提供详细的报告。使用这些工具来监测应用的内存使用情况,找到潜在的内存泄漏问题。在某些场景下,使用弱引用(WeakReference)可以帮助避免对对象的强引用,从而减少内存泄漏的可能性。通过模拟各种使用场景,找到潜在的内存泄漏问题。释放不再需要的资源,避免在组件销毁后仍然持有对它的引用。

15.鸿蒙HarmonyOS App(JAVA)进度条与圆形进度条

/设置无限模式,运行查看动态效果。15.鸿蒙HarmonyOS App(JAVA)进度条与圆形进度条。//创建并设置无限模式元素。

鸿蒙APP闪退的问题

以下是一些建议的步骤,可以帮助你定位和解决鸿蒙应用闪退的原因,希望对大家有所帮助。使用鸿蒙开发者工具中的调试功能,尝试在应用发生闪退的情况下进行调试。这可以帮助你实时观察应用的状态,查看变量的值,并找到可能的问题。如果你的应用使用版本控制工具(如Git),回退到之前的稳定版本,看看问题是否仍然存在。有时,一个小的更改可能导致应用闪退,因此查看最近的代码修改是很重要的。如果应用使用第三方库或依赖,确保它们的版本与应用的其他部分兼容。使用鸿蒙开发者工具提供的崩溃分析服务,分析应用的崩溃报告。

鸿蒙Harmony-层叠布局(Stack)详解

我们总是为了太多遥不可及的东西去拼命,却忘了人生真正的幸福不过是灯火阑珊处的温暖,柴米油盐的充实,人生无论你赚的钱,是多还是少,经历的事情是好还是坏,都不如过好当下的每一天!

HarmonyOS开发FA应用模型下多个页面的声明方式

HarmonyOS配套的IDE是DevEco Studio,目前的版本是3.1。官网可以直接下载​。

华为路由器OSPF动态链路路由协议配置

【代码】华为路由器OSPF动态链路路由协议配置。

鸿蒙开发-ArkTS基础,它与TS区别在那?

ArkTS是HarmonyOS优选的主力应用开发语言。ArkTS围绕应用开发在TypeScript(简称TS)生态基础上做了进一步扩展,继承了TS的所有特性,是TS的超集。说明: 也就是前端开发过程中所有的js/ts语法大部分支持的,比如es6中的箭头函数-模板字符串-promise-async/await-数组对象方法- 注意: 根据对下一代的Next版本的内部沟通,下一版本的ArkTs对类型最了更一步的限制。

TypeScript基础知识:类型断言

语法,我们可以将一个值断言为特定类型或将联合类型的变量断言为其中一个类型。在实际开发中,合理使用类型断言可以提高代码的可读性和维护性。中,类型断言是一种强制将一个值视为特定类型的方式。它允许开发人员在编译时指定变量的类型,从而获得更好的类型检查和代码提示。类型断言是一种告诉编译器某个值的具体类型的方法。中的一项强大特性,它允许开发人员在编译时明确指定变量的类型,以获得更好的类型检查和代码提示。中的类型断言,并提供丰富的示例代码帮助读者更好地理解和应用这一特性。将一个联合类型的变量断言为其中一个类型。

【HarmonyOS】装饰器下的状态管理与页面路由跳转实现

从今天开始,博主将开设一门新的专栏用来讲解市面上比较热门的技术 “鸿蒙开发”,对于刚接触这项技术的小伙伴在学习鸿蒙开发之前,有必要先了解一下鸿蒙,从你的角度来讲,你认为什么是鸿蒙呢?它出现的意义又是什么?鸿蒙仅仅是一个手机操作系统吗?它的出现能够和Android和IOS三分天下吗?它未来的潜力能否制霸整个手机市场呢?抱着这样的疑问和对鸿蒙开发的好奇,让我们开始今天对ArkUI状态管理的掌握吧!

OpenHarmony之消息机制实现

以上只是消息机制核外用户态的实现,最后会执行到系统调用以上的内容只是简单介绍了OpenHarmony之消息机制实现,没有具体到代码分析,移植等细节。要想成为一名鸿蒙高级开发,以上知识点是必须要掌握的,除此之外,还需要掌握一些鸿蒙应用开发相关的一些技术,需要我们共同去探索。

HarmonyOS应用开发学习笔记 包名、icon图标,应用名修改 UIAbility组件介绍、UIAbility启动模式、UIAbility组件基本用法

UIAbility组件是一种包含UI界面的应用组件,主要用于和用户交互。UIAbility组件是系统调度的基本单元,为应用提供绘制界面的窗口;一个UIAbility组件中可以通过多个页面来实现一个功能模块。每一个UIAbility组件实例,都对应于一个最近任务列表中的任务。UIAbility声明:UIAbility编辑完成后,还需要在module.json5上声明,才能使用"name": "EntryAbility", // UIAbility组件的名称。

HarmonyOS UI框架简介

HarmonyOSUI框架是一个用于构建跨设备应用的开发框架,它属于HarmonyOS系统架构的上层框架。该框架通过提供一系列的开发模型、声明式UI范式、系统API等,帮助开发者更高效地构建用户界面。在HarmonyOSUI框架中,开发语言目前主要支持arkts/TS语言。该框架通过自研的声明式UI范式,使开发者能够描述用户界面的状态和变化,而无需关注具体的实现细节。这种范式降低了学习成本,提高了开发效率。

HarmonyOS 应用开发学习笔记 ets组件生命周期

官网文档里有一句话:一个页面有且仅能有一个@Entry。只有被@Entry装饰的组件才可以调用页面的生命周期经木子测,自定义组件用@Entry修饰了不能触发(onPageShow、onPageHide、onBackPress )回调index.ets 组件(入口组件)能触发 onPageShow、onPageHide、onBackPress、aboutToAppear、aboutToDisappear自定义组件ComponentA。

是否需要跟上鸿蒙(OpenHarmony)开发岗位热潮?

自打华为2019年发布鸿蒙操作系统以来,网上各种声音百家争鸣。尤其是2023年发布会公布的鸿蒙4.0宣称不再支持Android,更激烈的讨论随之而来。本文没有宏大的叙事,只有基于现实的考量。

OpenHarmony之HDF驱动框架

HDF最核心几大块:配置管理,驱动管理,对外服务,消息机制对外接口侧重于服务,消息,而不是设备节点,这个是很大的一个转变以上内容主要概括了《OpenHarmony之HDF驱动框架》基础知识,为了让大家更快的了解《OpenHarmony4.0&Next》,我特意邀请几位行业大佬,联合整理了一份思维导图提供大家参考学习,大家可以根据自己的情况借鉴:《做鸿蒙应用开发到底学习些啥?除了上面整理的思维导图以外,这里还特别整理的一份《鸿蒙 (Harmony OS)开发学习手册》给大家进行参考学习:一、入门必看。

在云计算环境中,如何利用 AI 改进云计算系统和数据库系统性能

2023年我想大家讨论最多,热度最大的技术领域就是 AIGC 了,AI绘画的兴起,ChatGPT的火爆,在微软背后推手的 OpenAI 大战 Google几回合后,国内各种的大语言模型产品也随之各家百花齐放,什么文心一言、通义千问、科大讯飞的星火以及华为的盘古等等,一下子国内也涌现出几十种人工智能的大语言模型产品。ChatGPT 爆火之后,你是否有冷静的思考过 AIGC 的兴起对我们有哪些机遇与挑战?我们如何将AI 应用到我们现有的工作学习中?_aigc k8s

HarmonyOS page生命周期函数讲解

因为已经跳转 index 组件隐藏 onPageHide 触发 然后 AppView 页面显示 onPageShow触发。然后 我们打开预览器 运行index组件 然后 index 组件 被显示 onPageShow生命周期随之触发。然后 设置了一个button按钮 点击跳转向 pages/AppView页面。这个函数是页面返回时 触发 这里所说的返回不是说 router.back。调回 index界面 AppView被隐藏 触发onPageHide。

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之Dialog对话框组件

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之Dialog对话框组件

HarmonyOS应用性能与功耗云测试

应用程序:选择待测试的HarmonyOS应用包,包括HAP和APP两种格式,如果未上传应用,请先从本地上传一个HarmonyOS应用包。应用程序:选择待测试的HarmonyOS应用包,包括HAP和APP两种格式,如果未上传应用,请先从本地上传一个HarmonyOS应用包。在测试报告的概览页,可以查看测试任务的整体情况,如测试通过率、问题分布、在各个测试终端上的问题分布情况。在测试报告的概览页,可以查看测试任务的整体情况,如测试通过率、问题分布、在各个测试终端上的问题分布情况。

HarmonyOS的功能及场景应用

鸿蒙HarmonyOS主要应用的设备包括智慧屏、平板、手表、智能音箱、IoT设备等。具体来说,鸿蒙系统是一款面向全场景(移动办公、运动健康、社交通信、媒体娱乐等)的分布式操作系统,能够支持手机、平板、智能穿戴、智慧屏、车机等多种终端设备,通过同一套系统能力、适配多种终端形态。以下是关于鸿蒙系统技术知识的介绍:分布式架构首次用于终端OS,实现跨终端无缝协同体验。

【HarmonyOS开发】拖拽动画的实现

在开发拖拽动画时,发现png的图片在拖拽结束后,会出现图片闪动的不流畅问题,改为svg图片解决。因此通过大量的对比验证,确认为鸿蒙底层窜然问题。

【Harmony】鸿蒙操作系统架构

鸿蒙操作系统以其微内核架构、分布式能力和全场景覆盖的设计理念,成为当前技术领域一颗璀璨的明星。其架构设计满足了当前多样化的设备需求,注重了设备之间的协同工作和开发者的友好体验。随着鸿蒙操作系统的不断演进和生态系统的丰富,我们对于这个在全球范围内掀起一场科技变革的产物充满期待。在未来的智能互联时代,鸿蒙操作系统必将发挥更为重要的作用,引领技术的潮流。_鸿蒙系统的架构

HarmonyOS之 开发环境搭建

HarmonyOS开发环境搭建_鸿蒙开发环境搭建

鸿蒙开发软件用什么编程语言?

鸿蒙经过几年的迭代,抛弃了Java,基于TS出了一个官方推荐的ArkTS语言,甩开了JVM,提升效率,同时支持自己研发的一些现代化特性,没有版权的问题,现在唯一的问题就是各大公司愿不愿意为它去适配生态了,还好的是,目前各大互联网公司已经开始适配了。

HarmonyOS 数据管理与应用数据持久化(二)

文章浏览阅读31次。关系型数据库基于 SQLite 组件,适用于存储包含复杂关系数据的场景,比如一个班级的学生信息,需要包括姓名、学号、各科成绩等,又或者公司的雇员信息,需要包括姓名、工号、职位等,由于数据之间有较强的对应关系,复杂程度比键值型数据更高,此时需要使用关系型数据库来持久化保存数据。