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

news2025/1/15 10:18:00

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

安装nutpi/axios

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

ohpm install @ohos/axios

在这里插入图片描述

实现最近上映电影接口

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

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对象。

实现首页组件

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

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文件中配置路由。

{
  "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组件用于创建弹性布局容器,可以控制子组件的排列方式和对齐方式。

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

Scroll

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

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组件用于遍历数组并为每个元素生成一个子组件。

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

Image

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

Image(item.cover).objectFit(ImageFit.Auto).height(160).borderRadius(5)

Text

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

Text(item.title).fontSize(14).fontWeight(FontWeight.Bold).padding(10)

ColumnRow

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

Column({ space: 0 }) {
  // 子组件
}

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

onClick

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

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组件代码:


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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2276950.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

快速、可靠且高性价比的定制IP模式提升芯片设计公司竞争力

作者&#xff1a;Karthik Gopal&#xff0c;SmartDV Technologies亚洲区总经理 智权半导体科技&#xff08;厦门&#xff09;有限公司总经理 无论是在出货量巨大的消费电子市场&#xff0c;还是针对特定应用的细分芯片市场&#xff0c;差异化芯片设计带来的定制化需求也在芯片…

基础入门-抓包技术HTTPS协议APP小程序PC应用Web证书信任转发联动

知识点&#xff1a; 1、抓包技术-Web应用-http/s-Burp&Yakit 2、抓包技术-APP应用-http/s-Burp&Yakit 3、抓包技术-PC端应用-http/s-Burp&Yakit 4、抓包技术-WX小程序-http/s-Burp&Yakit 5、抓包技术-软件联动-http/s-Proxifier 6、抓包技术-通用方案-http/s-R…

贪心算法详细讲解(沉淀中)

文章目录 1. 什么是贪心算法&#xff1f;&#xff08;贪婪鼠目寸光&#xff09;经典例题1.1.1 找零问题1.1.2最小路径和1.1.3 背包问题 2.贪心算法的特点2.1 证明例1 3.学习贪心的方向心得体会 1. 什么是贪心算法&#xff1f;&#xff08;贪婪鼠目寸光&#xff09; 贪心策略&a…

SQL面试题2:留存率问题

引言 场景介绍&#xff1a; 在互联网产品运营中&#xff0c;用户注册量和留存率是衡量产品吸引力和用户粘性的关键指标&#xff0c;直接影响产品的可持续发展和商业价值。通过分析这些数据&#xff0c;企业可以了解用户行为&#xff0c;优化产品策略&#xff0c;提升用户体验…

学会使用开源软件jclasslib 字节码文件的组成 详解

应用场景 1 应用场景 2 学习路线 以正确的姿势打开文件 字节码文件的组成 玩转字节码常用工具 以正确的姿势打开文件 开源软件 jclasslib github 地址 https://github.com/ingokegel/jclasslib 工具使用 字节码文件的组成 基本信息 常量池 字段 方法 属性 详解 魔数 主副版…

primitive 的 Appearance编写着色器材质

import { nextTick, onMounted, ref } from vue import * as Cesium from cesium import gsap from gsaponMounted(() > { ... })// 1、创建矩形几何体&#xff0c;Cesium.RectangleGeometry&#xff1a;几何体&#xff0c;Rectangle&#xff1a;矩形 let rectGeometry new…

详情页 路由传值

路由传值获取参数 渲染数据 主页面 <template><div class"km"><div v-for"item in items" :key"item.id"><div class"title-km" ><img :src"item.imageUrl" alt"Image" class"…

OpenCV基础:矩阵的创建、检索与赋值

本文主要是介绍如何使用numpy进行矩阵的创建&#xff0c;以及从矩阵中读取数据&#xff0c;修改矩阵数据。 创建矩阵 import numpy as npa np.array([1,2,3]) b np.array([[1,2,3],[4,5,6]]) #print(a) #print(b)# 创建全0数组 eros矩阵 c np.zeros((8,8), np.uint8) #prin…

解锁未来情感科技:AI 机器人 Ropet 搭载的前沿智能黑科技

2025年的国际消费电子产品展览会&#xff08;CES&#xff09;上&#xff0c;一只可爱的“毛绒玩具”成了全场焦点。 当然&#xff0c;这并不是一个单纯的玩偶&#xff0c;而是和《超能陆战队》的大白一样温暖的陪伴机器人。 相信有很多人和小编一样&#xff0c;当年看完《超能…

软件测试 —— Selenium常用函数

软件测试 —— Selenium常用函数 操作测试对象点击/提交对象 click()模拟按键输入 send_keys("")清除文本内容 clear() 模拟用户键盘行为 Keys包示例用法 获取文本信息 textget_attribute("属性名称") 获取当前页面标题 title获取当前页面的 url current_u…

【WEB】网络传输中的信息安全 - 加密、签名、数字证书与HTTPS

文章目录 1. 概述2. 网络传输安全2.1.什么是中间人攻击2.2. 加密和签名2.2.1.加密算法2.2.2.摘要2.2.3.签名 2.3.数字证书2.3.1.证书的使用2.3.2.根证书2.3.3.证书链 2.4.HTTPS 1. 概述 本篇主要是讲解讲一些安全相关的基本知识&#xff08;如加密、签名、证书等&#xff09;&…

服务器数据恢复—EMC存储POOL中数据卷被删除的数据恢复案例

服务器数据恢复环境&故障&#xff1a; EMC Unity 400存储连接了2台硬盘柜。2台硬盘柜上一共有21块硬盘&#xff08;520字节&#xff09;。21块盘组建了2组RAID6&#xff1a;一组有11块硬盘&#xff0c;一组有10块硬盘。 在存储运行过程中&#xff0c;管理员误操作删除了 2组…

python 轮廓 获取环形区域

目录 效果图&#xff1a; 代码&#xff1a; 效果图&#xff1a; 代码&#xff1a; import cv2 import numpy as np# 读取图像 image cv2.imread(rE:\project\jijia\tools_jijia\img_tools\ground_mask.jpg, cv2.IMREAD_GRAYSCALE) # 二值化图像 # 二值化图像 _, binary cv…

使用 WPF 和 C# 将纹理应用于三角形

此示例展示了如何将纹理应用于三角形,以使场景比覆盖纯色的场景更逼真。以下是为三角形添加纹理的基本步骤。 创建一个MeshGeometry3D对象。像往常一样定义三角形的点和法线。通过向网格的TextureCoordinates集合添加值来设置三角形的纹理坐标。创建一个使用想要显示的纹理的 …

算法妙妙屋-------2..回溯的奇妙律动

回溯算法是一种用于系统性地搜索和解决问题的算法&#xff0c;它以深度优先搜索&#xff08;DFS&#xff09;为基础&#xff0c;用来探索所有可能的解决方案。通过递归地尝试候选解并在必要时回退&#xff08;即“回溯”&#xff09;&#xff0c;它能够高效地解决许多涉及组合、…

如何在Jupyter中快速切换Anaconda里不同的虚拟环境

目录 介绍 操作步骤 1. 选择环境&#xff0c;安装内核 2. 注册内核 3. 完工。 视频教程 介绍 很多网友在使用Jupyter的时候会遇到各种各样的问题&#xff0c;其中一个比较麻烦的问题就是我在Anaconda有多个Python的环境里面&#xff0c;如何让jupyter快速切换不同的Pyt…

《自动驾驶与机器人中的SLAM技术》ch9:自动驾驶车辆的离线地图构建

目录 1 点云建图的流程 2 前端实现 2.1 前端流程 2.2 前端结果 3 后端位姿图优化与异常值剔除 3.1 两阶段优化流程 3.2 优化结果 ① 第一阶段优化结果 ② 第二阶段优化结果 4 回环检测 4.1 回环检测流程 ① 遍历第一阶段优化轨迹中的关键帧。 ② 并发计算候选回环对…

2025/1/12 复习JS

我乞求你别再虚度光阴 ▶ 空心 --------------------------------------------------------------------------------------------------------------------------------- 摘自哔哩哔哩听课笔记。 01 上篇&#xff1a;核心语法 1.基于页面效果的操作 <!DOCTYPE html>…

初学stm32 --- II2C_AT24C02,向EEPROM中读写数据

目录 IIC总线协议介绍 IIC总线结构图 IIC协议时序 1. ACK&#xff08;Acknowledge&#xff09; 2. NACK&#xff08;Not Acknowledge&#xff09; IO口模拟II2C协议 发送起始信号&#xff1a; 发送停止信号&#xff1a; 检测应答信号&#xff1a; 发送应答信号&#x…

Angular-生命周期及钩子函数

什么是生命周期 Angular 创建和渲染组件及其子组件&#xff0c;当它们绑定的属性发生变化时检查它们&#xff0c;并在从 DOM 中移除它之前销毁它们。生命周期函数通俗的讲就是组件创建、组件更新、组件销毁的时候会触发的一系列的方法。当 Angular 使用构造函数新建一个组件或…