HarmonyOS NEXT应用开发边学边玩系列:从零实现一影视APP (四、最近上映电影滚动展示及加载更多的实现)

在HarmonyOS NEXT开发环境中,我们可以使用多种组件和库来构建丰富且交互友好的应用。本文将展示如何使用HarmonyOS NEXT框架和nutpi/axios库,从零开始实现一个简单的影视APP的首页,主要关注最近上映电影的滚动展示及加载更多功能的实现。

开源项目地址:https://atomgit.com/csdn-qq8864/hmmovie

安装nutpi/axios

首先,我们需要在项目中安装nutpi/axios库。

bash 复制代码
ohpm install @ohos/axios

实现最近上映电影接口

我们使用nutpi/axios库来实现最近上映电影的接口请求。接口请求如下:

typescript 复制代码
import {axiosClient,HttpPromise} from '../../utils/axiosClient';
import { HotMovieReq, MovieRespData, SwiperData } from '../bean/ApiTypes';

// 1.获取轮播图接口
export const getSwiperData = (): HttpPromise<SwiperData> => axiosClient.get({url:'/swiperdata'});

// 2.获取即将上映影视接口
export const getSoonMovie = (start:number,count:number): HttpPromise<MovieRespData> => axiosClient.post({url:'/soonmovie',data: { start:start,count:count }});

// 3.获取热门影视接口
export const getHotMovie = (req:HotMovieReq): HttpPromise<MovieRespData> => axiosClient.post({url:'/hotmovie',data:req});

代码讲解

  1. 创建axios客户端 :我们创建了一个axios客户端axiosClient,并设置了基础URL和请求超时时间。
  2. 定义接口函数getSoonMovies函数向接口发送GET请求以获取最近上映的电影数据,并返回一个Promise对象。

实现首页组件

接下来,我们将实现首页组件,包括最近上映电影的滚动展示和加载更多功能。

typescript 复制代码
import { getSoonMovies } from '../../common/api/movie';
import { MovieItem } from '../../common/bean/MovieItem';
import { Log } from '../../utils/logutil';
import { NavPathStack } from '@ohos.router';
import { BusinessError } from '@kit.BasicServicesKit';

@Builder
export function HomePageBuilder() {
  HomePage()
}

@Component
struct HomePage {
  pageStack: NavPathStack = new NavPathStack()
  @State soonMvList: MovieItem[] = []

  // 组件生命周期
  aboutToAppear() {
    Log.info('HomePage aboutToAppear');
    this.fetchSoonMovies();
  }

  // 获取最近上映电影数据
  fetchSoonMovies() {
    getSoonMovies().then((res) => {
      Log.debug(res.data.message)
      Log.debug("request", "res.data.code:%{public}d", res.data.code)
      if (res.data.code == 0) {
        this.soonMvList = res.data.data
      }
    })
    .catch((err: BusinessError) => {
      Log.debug("request", "err.data.code:%d", err.code)
      Log.debug("request", err.message)
    });
  }

  build() {
    Column({ space: 0 }) {
      Flex({
        direction: FlexDirection.Row,
        justifyContent: FlexAlign.SpaceBetween,
        alignContent: FlexAlign.SpaceBetween
      }) {
        Text('即将上映').fontWeight(FontWeight.Bold)
        Text('加载更多 >>').fontSize(14).fontColor(Color.Black)
          .onClick(() => {
            this.pageStack.pushDestinationByName("MovieMorePage", { types: 1 }).catch((e: Error) => {
              console.log(`catch exception: ${JSON.stringify(e)}`)
            }).then(() => {
              // 跳转成功
            });
          })
      }.padding(10)

      Scroll() {
        Row({ space: 5 }) {
          ForEach(this.soonMvList, (item: MovieItem) => {
            Column({ space: 0 }) {
              Image(item.cover).objectFit(ImageFit.Auto).height(160).borderRadius(5)
                .onClick(() => {
                  this.pageStack.pushDestinationByName("MovieDetailPage", { id: item.id }).catch((e: Error) => {
                    console.log(`catch exception: ${JSON.stringify(e)}`)
                  }).then(() => {
                    // 跳转成功
                  });
                })

              Text(item.title)
                .alignSelf(ItemAlign.Center)
                .maxLines(1)
                .textOverflow({ overflow: TextOverflow.Ellipsis })
                .width(100)
                .fontSize(14).fontWeight(FontWeight.Bold).padding(10)
            }.justifyContent(FlexAlign.Center)
          }, (itm: MovieItem) => itm.id)
        }
      }.scrollable(ScrollDirection.Horizontal)
    }
    .width('100%')
    .height('100%')
  }
}

代码讲解

  1. 导入模块 :我们导入了之前定义的getSoonMovies函数,以及一些其他必要的模块。
  2. 定义组件状态
    • soonMvList:用于存储最近上映电影的数据。
  3. 组件生命周期
    • aboutToAppear:组件即将出现在页面时执行的日志记录,并调用fetchSoonMovies函数获取数据。
  4. 构建页面
    • Column:垂直布局容器。
    • Flex:弹性布局容器,用于布局"即将上映"和"加载更多"按钮。
      • Text('即将上映'):显示"即将上映"标题。
      • Text('加载更多 >>'):显示"加载更多"按钮,并设置点击事件,点击时导航到加载更多页面。
    • Scroll:滚动容器,设置为水平滚动。
      • Row:水平布局容器,用于放置电影项。
      • ForEach:遍历soonMvList数组,为每个电影项创建一个Column
        • Column:垂直布局容器,用于放置电影的图片和标题。
          • Image:显示电影封面图片,并设置点击事件,点击时导航到电影详情页面。
          • Text:显示电影标题,并设置样式。

配置路由

为了实现页面跳转,我们需要在entry/src/main/resources/base/profile路径下的route_map.json文件中配置路由。

json 复制代码
{
  "routerMap": [
    {
      "name": "HomePage",
      "pageSourceFile": "src/main/ets/pages/home/Home.ets",
      "buildFunction": "HomePageBuilder",
      "data": {
        "description": "this is HomePage"
      }
    },
    {
      "name": "MovieMorePage",
      "pageSourceFile": "src/main/ets/pages/home/MovieMore.ets",
      "buildFunction": "MovieMorePageBuilder",
      "data": {
        "description": "this is MovieMorePage"
      }
    },
    {
      "name": "MovieDetailPage",
      "pageSourceFile": "src/main/ets/pages/home/Detail.ets",
      "buildFunction": "MovieDetailPageBuilder",
      "data": {
        "description": "this is MovieDetailPage"
      }
    }
  ]
}

代码讲解

  1. 配置路由
    • HomePage:配置首页的路由信息。
    • MovieMorePage:配置加载更多页面的路由信息。
    • MovieDetailPage:配置电影详情页面的路由信息。

涉及到的常用组件

Flex

Flex组件用于创建弹性布局容器,可以控制子组件的排列方式和对齐方式。

typescript 复制代码
Flex({
  direction: FlexDirection.Row,
  justifyContent: FlexAlign.SpaceBetween,
  alignContent: FlexAlign.SpaceBetween
}) {
  Text('即将上映').fontWeight(FontWeight.Bold)
  Text('加载更多 >>').fontSize(14).fontColor(Color.Black)
}

Scroll

Scroll组件用于创建可滚动的容器,可以设置滚动方向为水平或垂直。

typescript 复制代码
Scroll() {
  Row({ space: 5 }) {
    ForEach(this.soonMvList, (item: MovieItem) => {
      Column({ space: 0 }) {
        Image(item.cover).objectFit(ImageFit.Auto).height(160).borderRadius(5)
      }
    }, (itm: MovieItem) => itm.id)
  }
}.scrollable(ScrollDirection.Horizontal)

ForEach

ForEach组件用于遍历数组并为每个元素生成一个子组件。

typescript 复制代码
ForEach(this.soonMvList, (item: MovieItem) => {
  Column({ space: 0 }) {
    Image(item.cover).objectFit(ImageFit.Auto).height(160).borderRadius(5)
  }
}, (itm: MovieItem) => itm.id)

Image

Image组件用于显示图片,可以设置图片的显示方式、大小和形状。

typescript 复制代码
Image(item.cover).objectFit(ImageFit.Auto).height(160).borderRadius(5)

Text

Text组件用于显示文本,可以设置文本的大小、颜色、对齐方式等。

typescript 复制代码
Text(item.title).fontSize(14).fontWeight(FontWeight.Bold).padding(10)

ColumnRow

ColumnRow组件分别用于创建垂直和水平布局容器。

typescript 复制代码
Column({ space: 0 }) {
  // 子组件
}

Row({ space: 5 }) {
  // 子组件
}

onClick

onClick事件用于处理组件的点击事件。

typescript 复制代码
Text('加载更多 >>').fontSize(14).fontColor(Color.Black)
  .onClick(() => {
    this.pageStack.pushDestinationByName("MovieMorePage", { types: 1 }).catch((e: Error) => {
      console.log(`catch exception: ${JSON.stringify(e)}`)
    }).then(() => {
      // 跳转成功
    });
  })

总结

通过本文,我们展示了如何使用HarmonyOS NEXT框架和nutpi/axios库来实现一个简单的影视APP的首页,包括最近上映电影的滚动展示和加载更多功能。nutpi/axios库的使用大大简化了网络请求的操作,使代码更加简洁易读。同时,我们还介绍了涉及到的常用组件的使用方法。

希望这篇文章对你有所帮助,让你在开发HarmonyOS NEXT应用时更加得心应手。如果你有任何问题或建议,欢迎在评论区留言交流!

参考代码

以下是完整的HomePage组件代码:

typescript 复制代码
import { Log } from '../../utils/logutil';
import { BusinessError } from '@kit.BasicServicesKit';
import { router } from '@kit.ArkUI';
import { MovieItem, SwiperItem } from '../../common/bean/ApiTypes';
import { getHotMovie, getHotTv,
  getMvTop250,
  getNewMovie,
  getPiaoMovie,
  getSoonMovie, getSwiperData, getUsHotMv } from '../../common/api/movie';
import { PiaoFangRespData } from '../../common/bean/PiaoFangResp';


class BasicDataSource<T> implements IDataSource {

  private listeners: DataChangeListener[] = [];
  private originDataArray: T[] = [];

  totalCount(): number {
    return this.originDataArray.length;
  }

  getData(index: number): T {
    return this.originDataArray[index];
  }

  registerDataChangeListener(listener: DataChangeListener): void {
    if (this.listeners.indexOf(listener) < 0) {
      this.listeners.push(listener);
    }
  }

  unregisterDataChangeListener(listener: DataChangeListener): void {
    const pos = this.listeners.indexOf(listener);
    if (pos >= 0) {
      this.listeners.slice(pos, 1);
    }
  }

  // 通知LazyForEach组件需要重新重载所有子组件
  notifyDataReload(): void {
    this.listeners.forEach(listener => {
      listener.onDataReloaded();
    })
  }

  // 通知LazyForEach组件需要在index对应索引处添加子组件
  notifyDataAdd(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataAdd(index);
    })
  }
}

class SwiperDataSource<T> extends BasicDataSource<T> {

  private dataArray: T[] = [];

  totalCount(): number {
    return this.dataArray.length;
  }

  getData(index: number): T {
    return this.dataArray[index];
  }

  // 在列表末尾添加数据并通知监听器
  pushData(data: T): void {
    this.dataArray.push(data);
    this.notifyDataAdd(this.dataArray.length - 1);
  }

  // 重载数据
  reloadData(): void {
    // 不会引起状态变化
    this.dataArray = [];
    // 必须通过DataChangeListener来更新
    this.notifyDataReload();
  }
}

@Component
export default struct Home {
  @Consume pageStack: NavPathStack
  private swiperController: SwiperController = new SwiperController()
  private swiperData: SwiperDataSource<SwiperItem> = new SwiperDataSource()
  @State soonMvList:MovieItem[]=[]
  @State hotMvList:MovieItem[]=[]
  @State hotTvList:MovieItem[]=[]
  @State usMvList:MovieItem[]=[]
  @State topMvList:MovieItem[]=[]
  @State piaoList:PiaoFangRespData[]=[]

  // 组件生命周期
  aboutToAppear() {
    Log.info('Home aboutToAppear');
    getSwiperData().then((res) => {
      Log.debug(res.data.message)
      Log.debug("request","res.data.code:%{public}d",res.data.code)
      for (const itm of res.data.data) {
        this.swiperData.pushData(itm)
      }
    }).catch((err: BusinessError) => {
      Log.debug("request","err.data.code:%d",err.code)
      Log.debug("request",err.message)
    });

    getPiaoMovie().then((res) => {
      Log.debug(res.data.message)
      for (const itm of res.data.data) {
        this.piaoList.push(itm)
      }

    }).catch((err: BusinessError) => {
      Log.debug("request","err.data.code:%d",err.code)
      Log.debug("request",err.message)
    });

    getSoonMovie(1,10).then((res) => {
      Log.debug(res.data.message)
      Log.debug("request","res.data.code:%{public}d",res.data.code)
      for (const itm of res.data.data) {
        this.soonMvList.push(itm)
      }
    }).catch((err: BusinessError) => {
      Log.debug("request","err.data.code:%d",err.code)
      Log.debug("request",err.message)
    });

    getHotMovie({start:1,count:10,city:'郑州'}).then((res) => {
      Log.debug(res.data.message)
      Log.debug("request","res.data.code:%{public}d",res.data.code)
      for (const itm of res.data.data) {
        this.hotMvList.push(itm)
      }
    }).catch((err: BusinessError) => {
      Log.debug("request","err.data.code:%d",err.code)
      Log.debug("request",err.message)
    });

    getHotTv(1,10).then((res) => {
      Log.debug(res.data.message)
      Log.debug("request","res.data.code:%{public}d",res.data.code)
      for (const itm of res.data.data) {
        this.hotTvList.push(itm)
      }
    }).catch((err: BusinessError) => {
      Log.debug("request","err.data.code:%d",err.code)
      Log.debug("request",err.message)
    });

    getUsHotMv(1,10).then((res) => {
      Log.debug(res.data.message)
      Log.debug("request","res.data.code:%{public}d",res.data.code)
      for (const itm of res.data.data) {
        this.usMvList.push(itm)
      }
    }).catch((err: BusinessError) => {
      Log.debug("request","err.data.code:%d",err.code)
      Log.debug("request",err.message)
    });

    getMvTop250(1,10).then((res) => {
      Log.debug(res.data.message)
      for (const itm of res.data.data) {
        this.topMvList.push(itm)
      }

    }).catch((err: BusinessError) => {
      Log.debug("request","err.data.code:%d",err.code)
      Log.debug("request",err.message)
    });
  }

  // 组件生命周期
  aboutToDisappear() {
    Log.info('Home aboutToDisappear');
  }

  build() {
      Scroll() {
        Column({ space: 0 }) {
          //title
          Flex({
            direction: FlexDirection.Row,
            justifyContent: FlexAlign.SpaceBetween,
            alignContent: FlexAlign.SpaceBetween
          })
          {
            Blank().width(40)
            Text('爱影家').fontSize(26).fontWeight(FontWeight.Bold)
            Image($r('app.media.search')).width(42).height(42).padding({bottom:8}).onClick(() => {
              this.pageStack.pushDestinationByName("SearchPage", { }).catch((e:Error)=>{
                // 跳转失败,会返回错误码及错误信息
                console.log(`catch exception: ${JSON.stringify(e)}`)
              }).then(()=>{
                // 跳转成功
              });
            })
          }
          .padding(10)
          .width('100%').height(50)
          // 轮播图
          Swiper(this.swiperController) {
            LazyForEach(this.swiperData, (item: SwiperItem) => {
              Stack({ alignContent: Alignment.Center }) {
                Image(item.imageUrl)
                  .width('100%')
                  .height(180)
                  .zIndex(1)
                  .onClick(() => {
                    this.pageStack.pushDestinationByName("MovieDetailPage", { id:item.id }).catch((e:Error)=>{
                      // 跳转失败,会返回错误码及错误信息
                      console.log(`catch exception: ${JSON.stringify(e)}`)
                    }).then(()=>{
                      // 跳转成功
                    });
                  })

                // 显示轮播图标题
                Text(item.title)
                  .padding(5)
                  .margin({ top: 135 })
                  .width('100%')
                  .height(60)
                  .textAlign(TextAlign.Center)
                  .maxLines(2)
                  .textOverflow({ overflow: TextOverflow.Clip })
                  .fontSize(22)
                  .fontColor(Color.White)
                  .opacity(100)// 设置标题的透明度 不透明度设为100%,表示完全不透明
                  .backgroundColor('#808080AA')// 背景颜色设为透明
                  .zIndex(2)
                  .onClick(() => {
                    this.pageStack.pushDestinationByName("MovieDetailPage", { id:item.id }).catch((e:Error)=>{
                      // 跳转失败,会返回错误码及错误信息
                      console.log(`catch exception: ${JSON.stringify(e)}`)
                    }).then(()=>{
                      // 跳转成功
                    });
                  })
              }
            }, (item: SwiperItem) => item.id)
          }
          .cachedCount(2)
          .index(1)
          .autoPlay(true)
          .interval(4000)
          .loop(true)
          .indicatorInteractive(true)
          .duration(1000)
          .itemSpace(0)
          .curve(Curve.Linear)
          .onChange((index: number) => {
            console.info(index.toString())
          })
          .onGestureSwipe((index: number, extraInfo: SwiperAnimationEvent) => {
            console.info("index: " + index)
            console.info("current offset: " + extraInfo.currentOffset)
          })
          .height(180) // 设置高度
          Text('今日票房').fontWeight(FontWeight.Bold).padding(10).alignSelf(ItemAlign.Start)
          Scroll() {
            Row({ space: 5 }) {
              ForEach(this.piaoList, (item: PiaoFangRespData,idx) => {
                Column({ space: 2 }) {
                  Text(idx.toString()).fontSize(20).fontColor(Color.Orange)
                  Text(item.name)
                    .maxLines(1)
                    .textOverflow({ overflow: TextOverflow.Ellipsis })
                    .fontSize(14).fontWeight(FontWeight.Bold).alignSelf(ItemAlign.Center).padding(5)
                  Text(item.release_date).fontSize(12).fontColor(Color.White)
                    .alignSelf(ItemAlign.Center)
                  Text('票房:'+item.box_million).fontSize(12).fontColor(Color.White)
                    .alignSelf(ItemAlign.Center)
                  Text('占比:'+item.share_box).fontSize(12).fontColor(Color.White)
                    .alignSelf(ItemAlign.Center)

                }.width(120).height(120).backgroundColor('rgba(85, 170, 255, 0.60)').borderRadius(5).justifyContent(FlexAlign.Center)

              }, (itm: PiaoFangRespData, idx) => itm.name)
            }
          }.scrollable(ScrollDirection.Horizontal)

          Flex({
            direction: FlexDirection.Row,
            justifyContent: FlexAlign.SpaceBetween,
            alignContent: FlexAlign.SpaceBetween
          }) {
            Text('即将上映').fontWeight(FontWeight.Bold)
            Text('加载更多 >>').fontSize(14).fontColor(Color.Black) .onClick(() => {
              this.pageStack.pushDestinationByName("MovieMorePage", {types:1}).catch((e:Error)=>{
                // 跳转失败,会返回错误码及错误信息
                console.log(`catch exception: ${JSON.stringify(e)}`)
              }).then(()=>{
                // 跳转成功
              });
            })
          }.padding(10)
          Scroll() {
            Row({ space: 5 }) {
              ForEach(this.soonMvList, (item: MovieItem) => {
                Column({ space: 0 }) {
                  Image(item.cover).objectFit(ImageFit.Auto).height(160).borderRadius(5)
                    .onClick(() => {
                      this.pageStack.pushDestinationByName("MovieDetailPage", { id:item.id }).catch((e:Error)=>{
                        // 跳转失败,会返回错误码及错误信息
                        console.log(`catch exception: ${JSON.stringify(e)}`)
                      }).then(()=>{
                        // 跳转成功
                      });
                    })

                  Text(item.title)
                    .alignSelf(ItemAlign.Center)
                    .maxLines(1)
                    .textOverflow({ overflow: TextOverflow.Ellipsis })
                    .width(100)
                    .fontSize(14).fontWeight(FontWeight.Bold).padding(10)
                }.justifyContent(FlexAlign.Center)

              }, (itm: MovieItem, idx) => itm.id)
            }
          }.scrollable(ScrollDirection.Horizontal)

          Flex({
            direction: FlexDirection.Row,
            justifyContent: FlexAlign.SpaceBetween,
            alignContent: FlexAlign.SpaceBetween
          }) {
            Text('热映电影').fontWeight(FontWeight.Bold)
            Text('加载更多 >>').fontSize(14).fontColor(Color.Black) .onClick(() => {
              this.pageStack.pushDestinationByName("MovieMorePage", {types:2}).catch((e:Error)=>{
                // 跳转失败,会返回错误码及错误信息
                console.log(`catch exception: ${JSON.stringify(e)}`)
              }).then(()=>{
                // 跳转成功
              });
            })
          }.padding(10)
          Scroll() {
            Row({ space: 5 }) {
              ForEach(this.hotMvList, (item: MovieItem) => {
                Column({ space: 0 }) {
                  Image(item.cover).objectFit(ImageFit.Auto).height(160).borderRadius(5)
                    .onClick(() => {
                      this.pageStack.pushDestinationByName("MovieDetailPage", { id:item.id }).catch((e:Error)=>{
                        // 跳转失败,会返回错误码及错误信息
                        console.log(`catch exception: ${JSON.stringify(e)}`)
                      }).then(()=>{
                        // 跳转成功
                      });
                    })

                  Text(item.title)
                    .alignSelf(ItemAlign.Center)
                    .maxLines(1)
                    .textOverflow({ overflow: TextOverflow.Ellipsis })
                    .width(100)
                    .fontSize(14).fontWeight(FontWeight.Bold).padding(10)
                }.justifyContent(FlexAlign.Center)

              }, (itm: MovieItem, idx) => itm.id)
            }
          }.scrollable(ScrollDirection.Horizontal)

          // Flex({
          //   direction: FlexDirection.Row,
          //   justifyContent: FlexAlign.SpaceBetween,
          //   alignContent: FlexAlign.SpaceBetween
          // }) {
          //   Text('热门资讯').fontWeight(FontWeight.Bold)
          //   Text('加载更多 >>').fontSize(14).fontColor(Color.Black) .onClick(() => {
          //
          //   })
          // }.padding(10)
          Flex({
            direction: FlexDirection.Row,
            justifyContent: FlexAlign.SpaceBetween,
            alignContent: FlexAlign.SpaceBetween
          }) {
            Text('热门剧集').fontWeight(FontWeight.Bold)
            Text('加载更多 >>').fontSize(14).fontColor(Color.Black) .onClick(() => {
              this.pageStack.pushDestinationByName("MovieMorePage", {types:3}).catch((e:Error)=>{
                // 跳转失败,会返回错误码及错误信息
                console.log(`catch exception: ${JSON.stringify(e)}`)
              }).then(()=>{
                // 跳转成功
              });
            })
          }.padding(10)
          Scroll() {
            Row({ space: 5 }) {
              ForEach(this.hotTvList, (item: MovieItem) => {
                Column({ space: 0 }) {
                  Image(item.cover).objectFit(ImageFit.Auto).height(160).borderRadius(5)
                    .onClick(() => {
                      this.pageStack.pushDestinationByName("MovieDetailPage", { id:item.id }).catch((e:Error)=>{
                        // 跳转失败,会返回错误码及错误信息
                        console.log(`catch exception: ${JSON.stringify(e)}`)
                      }).then(()=>{
                        // 跳转成功
                      });
                    })

                  Text(item.title)
                    .alignSelf(ItemAlign.Center)
                    .maxLines(1)
                    .textOverflow({ overflow: TextOverflow.Ellipsis })
                    .width(100)
                    .fontSize(14).fontWeight(FontWeight.Bold).padding(10)
                }.justifyContent(FlexAlign.Center)

              }, (itm: MovieItem, idx) => itm.id)
            }
          }.scrollable(ScrollDirection.Horizontal)

          Flex({
            direction: FlexDirection.Row,
            justifyContent: FlexAlign.SpaceBetween,
            alignContent: FlexAlign.SpaceBetween
          }) {
            Text('豆瓣排行榜').fontWeight(FontWeight.Bold)
            Text('加载更多 >>').fontSize(14).fontColor(Color.Black) .onClick(() => {
              this.pageStack.pushDestinationByName("MovieMorePage", {types:4}).catch((e:Error)=>{
                // 跳转失败,会返回错误码及错误信息
                console.log(`catch exception: ${JSON.stringify(e)}`)
              }).then(()=>{
                // 跳转成功
              });
            })
          }.padding(10)
          Scroll() {
            Row({ space: 5 }) {
              ForEach(this.topMvList, (item: MovieItem) => {
                Column({ space: 0 }) {
                  Image(item.cover).objectFit(ImageFit.Auto).height(160).borderRadius(5)
                    .onClick(() => {
                      this.pageStack.pushDestinationByName("MovieDetailPage", { id:item.id }).catch((e:Error)=>{
                        // 跳转失败,会返回错误码及错误信息
                        console.log(`catch exception: ${JSON.stringify(e)}`)
                      }).then(()=>{
                        // 跳转成功
                      });
                    })

                  Text(item.title)
                    .alignSelf(ItemAlign.Center)
                    .maxLines(1)
                    .textOverflow({ overflow: TextOverflow.Ellipsis })
                    .width(100)
                    .fontSize(14).fontWeight(FontWeight.Bold).padding(10)
                }.justifyContent(FlexAlign.Center)

              }, (itm: MovieItem, idx) => itm.id)
            }
          }.scrollable(ScrollDirection.Horizontal)

          Flex({
            direction: FlexDirection.Row,
            justifyContent: FlexAlign.SpaceBetween,
            alignContent: FlexAlign.SpaceBetween
          }) {
            Text('北美票房榜').fontWeight(FontWeight.Bold)
            Text('加载更多 >>').fontSize(14).fontColor(Color.Black) .onClick(() => {
              this.pageStack.pushDestinationByName("MovieMorePage", {types:5}).catch((e:Error)=>{
                // 跳转失败,会返回错误码及错误信息
                console.log(`catch exception: ${JSON.stringify(e)}`)
              }).then(()=>{
                // 跳转成功
              });
            })
          }.padding(10)
          Scroll() {
            Row({ space: 5 }) {
              ForEach(this.usMvList, (item: MovieItem) => {
                Column({ space: 0 }) {
                  Image(item.cover).objectFit(ImageFit.Auto).height(160).borderRadius(5)
                    .onClick(() => {
                      this.pageStack.pushDestinationByName("MovieDetailPage", { id:item.id }).catch((e:Error)=>{
                        // 跳转失败,会返回错误码及错误信息
                        console.log(`catch exception: ${JSON.stringify(e)}`)
                      }).then(()=>{
                        // 跳转成功
                      });
                    })

                  Text(item.title)
                    .alignSelf(ItemAlign.Center)
                    .maxLines(1)
                    .textOverflow({ overflow: TextOverflow.Ellipsis })
                    .width(100)
                    .fontSize(14).fontWeight(FontWeight.Bold).padding(10)
                }.justifyContent(FlexAlign.Center)

              }, (itm: MovieItem, idx) => itm.id)
            }
          }.scrollable(ScrollDirection.Horizontal)

        }.width('100%')
      }
      .width('100%').height('100%')
      .scrollable(ScrollDirection.Vertical)
    }
}
相关推荐
指尖动听知识库3 小时前
华为2024嵌入式研发面试题
数据结构·算法·华为
轻口味8 小时前
【HarmonyOS NAPI 深度探索4】安装开发环境(Node.js、C++ 编译器、node-gyp)
c++·node.js·harmonyos·harmonyos next·napi
塞尔维亚大汉8 小时前
OpenHarmony(鸿蒙南向开发)——轻量系统芯片移植指南(一)
操作系统·harmonyos
特立独行的猫a15 小时前
HarmonyOS NEXT应用开发边学边玩系列:从零实现一影视APP (三、影视搜索页功能实现)
华为·harmonyos·影视app
塞尔维亚大汉15 小时前
OpenHarmony驱动框架HDF中设备管理服务构建过程详解(一)
harmonyos·领域驱动设计
小五很懒18 小时前
Windows安装HDC工具及鸿蒙手机开启HDC调试
windows·华为·智能手机·harmonyos·hdc
HarmonyOS_SDK18 小时前
【FAQ】HarmonyOS SDK 闭源开放能力 —Map Kit(4)
harmonyos
time_silence18 小时前
ArkTS 组件事件、状态管理与资源管理
华为·arkts
光明_吖吼1 天前
HarmonyOS应用开发者初级认证最新版– 2025/1/13号题库新版
华为·harmonyos