HarmonyOS NEXT应用开发边学边玩系列:从零实现一影视APP (五、电影详情页的设计实现)

news2025/1/17 21:52:48

在上一篇文章中,完成了电影列表页的开发。接下来,将进入电影详情页的设计实现阶段。这个页面将展示电影的详细信息,包括电影海报、评分、简介以及相关影人等。将使用 HarmonyOS 提供的常用组件,并结合第三方库 nutpi/axios 来实现网络请求。下面,一步步地来看如何实现电影详情页。

在这里插入图片描述
开源项目地址:https://atomgit.com/csdn-qq8864/hmmovie

好的作品是需要不断打磨,在你的学习和体验过程中有任何问题,欢迎到我的开源项目代码仓下面提交issue,持续优化。

获取电影详情接口

首先,需要定义一个用于获取电影详情的接口。这里使用 axiosClient.post 方法来实现,代码如下:

export const getDetailMv = (id: string): HttpPromise<DetailMvResp> => axiosClient.post({ url: '/detailmovie', data: { id: id } });

电影详情接口的具体信息如下:

POST http://120.27.146.247:8000/api/v1/detailmovie
Content-Type: application/json

{
    "id": "1292052"
}
电影详情页的组件使用

在电影详情页中,将使用 BadgeSymbolSpanButtonRating 等组件来展示电影的详细信息。

  1. Badge 组件

    Badge 组件用于在某个组件的右上角显示一个标记,通常用于展示某种数量或状态。在电影详情页中,将使用 Badge 组件来展示“想看”和“看过”的数量。

  2. SymbolSpan 组件

    SymbolSpan 组件用于在文本中嵌入图标。在电影详情页中,将使用 SymbolSpan 组件来展示“想看”和“看过”的图标。

  3. Button 组件

    Button 组件用于创建按钮,用户可以通过点击按钮来执行特定的操作。在电影详情页中,将使用 Button 组件来创建“播放”按钮,用户点击后可以跳转到视频播放页面。

  4. Rating 组件

    Rating 组件用于展示评分。在电影详情页中,将使用 Rating 组件来展示电影的豆瓣评分。

电影详情页的实现

下面,将展示电影详情页的具体实现代码:

import { getDetailMv, getMovieSrc } from "../../common/api/movie"
import { Log } from "../../utils/logutil"
import { BusinessError } from "@kit.BasicServicesKit"
import { DetailMvResp, DetailMvRespCast } from "../../common/bean/DetailMvResp"
import { LengthMetrics, promptAction } from "@kit.ArkUI"
import { MvSourceResp } from "../../common/bean/MvSourceResp"

@Builder
export function MovieDetailPageBuilder() {
  Detail()
}

@Component
struct Detail {
  pageStack: NavPathStack = new NavPathStack()
  private uid = ''
  @State detailData: DetailMvResp | null = null;
  private srcData: MvSourceResp | null = null;

  private description: string = ''
  private isToggle = false
  @State toggleText: string = ''
  @State toggleBtn: string = '展开'

  build() {
    NavDestination() {
      Column({ space: 0 }) {
        Row() {
          Image(this.detailData?.images).objectFit(ImageFit.Auto).width(120).borderRadius(5)
          Column({ space: 8 }) {
            Text(this.detailData?.title).fontSize(18)
            Text(this.detailData?.year + " " + this.detailData?.genre).fontSize(14)

            Row() {
              Badge({
                count: this.detailData?.wish_count,
                maxCount: 10000,
                position: BadgePosition.RightTop,
                style: { badgeSize: 22, badgeColor: '#fffab52a' }
              }) {
                Row() {
                  Text() {
                    SymbolSpan($r('sys.symbol.heart'))
                      .fontWeight(FontWeight.Lighter)
                      .fontSize(32)
                      .fontColor(['#fffab52a'])
                  }
                  Text('想看')
                }.backgroundColor('#f8f4f5').borderRadius(5).padding(5)
              }.padding(8)

              Blank(10).width(40)
              Badge({
                count: this.detailData?.reviews_count,
                maxCount: 10000,
                position: BadgePosition.RightTop,
                style: { badgeSize: 22, badgeColor: '#fffab52a' }
              }) {
                Row() {
                  Text() {
                    SymbolSpan($r('sys.symbol.star'))
                      .fontWeight(FontWeight.Lighter)
                      .fontSize(32)
                      .fontColor(['#fffab52a'])
                  }
                  Text('看过')
                }.backgroundColor('#f8f4f5').borderRadius(5).padding(5)
              }.padding(8)
            }

            Button('播放', { buttonStyle: ButtonStyleMode.NORMAL, role: ButtonRole.NORMAL })
              .borderRadius(8)
              .borderColor('#fffab52a')
              .fontColor('#fffab52a')
              .width(100)
              .height(35)
              .onClick(() => {
                console.info('Button onClick')
                if (this.srcData != null) {
                  this.pageStack.pushDestinationByName("VideoPlayerPage", { item: { video: this.srcData.urls[0], tvurls: this.srcData.tvurls, title: this.srcData.title, desc: this.detailData?.summary } }).catch((e: Error) => {
                    // 跳转失败,会返回错误码及错误信息
                    console.log(`catch exception: ${JSON.stringify(e)}`)
                  }).then(() => {
                    // 跳转成功

                  });
                } else {
                  promptAction.showToast({ message: '暂无资源' })
                }
              })
          }.alignItems(HorizontalAlign.Start) // 水平方向靠左对齐
            .justifyContent(FlexAlign.Start)   // 垂直方向靠上对齐
            .padding(10)
        }.height(160).width('100%')

        Row() {
          Text('豆瓣评分').fontSize(16).padding(5)
          Rating({ rating: (this.detailData?.rate ?? 0) / 2, indicator: true })
            .stars(5)
            .stepSize(0.5).height(28)

          Text(this.detailData?.rate.toString()).fontColor('#fffab52a').fontWeight(FontWeight.Bold).fontSize(36).padding(5)
        }.width('100%').height(80).borderRadius(5).backgroundColor('#f8f4f5').margin(20)

        Text('简介').fontSize(18).padding({ bottom: 10 }).fontWeight(FontWeight.Bold).alignSelf(ItemAlign.Start)

        Text(this.toggleText).fontSize(14).lineHeight(20).alignSelf(ItemAlign.Start)
        Text(this.toggleBtn).fontSize(14).fontColor(Color.Gray).padding(10).alignSelf(ItemAlign.End).onClick(() => {
          this.isToggle = !this.isToggle
          if (this.isToggle) {
            this.toggleBtn = '收起'
            this.toggleText = this.description
          } else {
            this.toggleBtn = '展开'
            this.toggleText = this.description.substring(0, 100) + '...'
          }
        })

        Text('影人').fontSize(18).padding({ bottom: 10 }).fontWeight(FontWeight.Bold).alignSelf(ItemAlign.Start)
        Scroll() {
          Row({ space: 5 }) {
            ForEach(this.detailData?.cast, (item: DetailMvRespCast) => {
              Column({ space: 0 }) {
                Image(item.cover).objectFit(ImageFit.Auto).height(120).borderRadius(5)
                  .onClick(() => {

                  })

                Text(item.name)
                  .alignSelf(ItemAlign.Center)
                  .maxLines(1)
                  .textOverflow({ overflow: TextOverflow.Ellipsis })
                  .fontSize(14).padding(10)
              }.justifyContent(FlexAlign.Center)

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

      }.padding({ left: 10, right: 10 })
    }.title("电影详情")
      .width('100%')
      .height('100%')
      .onReady(ctx => {
        this.pageStack = ctx.pathStack
        //从上个页面拿参数
        this.pageStack.getParamByName("MovieDetailPage")
        interface params {
          id: string;
        }
        let par = ctx.pathInfo.param as params
        Log.debug("par:%s", par.id)
        this.uid = par.id
      })
      .onShown(() => {
        console.info('Detail onShown');
        getDetailMv(this.uid).then((res) => {
          Log.debug(res.data.message)
          Log.debug("request", "res.data.code:%{public}d", res.data.code)
          this.detailData = res.data
          this.description = this.detailData.summary
          this.toggleText = this.description.substring(0, 100) + '...'
        }).catch((err: BusinessError) => {
          Log.debug("request", "err.data.code:%d", err.code)
          Log.debug("request", err.message)
        });

        getMovieSrc(this.uid).then((res) => {
          Log.debug(res.data.message)
          Log.debug("request", "res.data.code:%{public}d", res.data.code)
          if (res.data.code == 0) {
            this.srcData = res.data
          }
        }).catch((err: BusinessError) => {
          Log.debug("request", "err.data.code:%d", err.code)
          Log.debug("request", err.message)
        });
      })
  }
}
折叠效果的实现

在电影详情页中,对于电影的简介,使用了折叠效果,即默认只显示部分简介内容,用户点击“展开”按钮后可以查看完整简介。这个效果的实现主要通过控制 Text 组件的显示内容来实现。具体代码如下:

Text(this.toggleText).fontSize(14).lineHeight(20).alignSelf(ItemAlign.Start)
Text(this.toggleBtn).fontSize(14).fontColor(Color.Gray).padding(10).alignSelf(ItemAlign.End).onClick(() => {
  this.isToggle = !this.isToggle
  if (this.isToggle) {
    this.toggleBtn = '收起'
    this.toggleText = this.description
  } else {
    this.toggleBtn = '展开'
    this.toggleText = this.description.substring(0, 100) + '...'
  }
})

通过上述代码,实现了电影简介内容的折叠效果。

总结

在本文中,完成了电影详情页的设计与实现。主要使用了 BadgeSymbolSpanButtonRating 等组件,并结合 nutpi/axios 库来实现网络请求。通过这些技术,可以快速地开发出功能丰富、用户体验良好的影视应用。希望本文对你有所帮助。

作者介绍

作者:csdn猫哥

原文链接:https://blog.csdn.net/yyz_1987

团队介绍

坚果派团队由坚果等人创建,团队拥有12个华为HDE带领热爱HarmonyOS/OpenHarmony的开发者,以及若干其他领域的三十余位万粉博主运营。专注于分享HarmonyOS/OpenHarmony、ArkUI-X、元服务、仓颉等相关内容,团队成员聚集在北京、上海、南京、深圳、广州、宁夏等地,目前已开发鸿蒙原生应用和三方库60+,欢迎交流。

版权声明

本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

其他资源

官方系统图标资源:https://developer.huawei.com/consumer/cn/doc/design-guides/system-icons-0000001929854962

https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/reference/apis-arkui/arkui-ts/ts-basic-components-symbolSpan.md

https://developer.huawei.com/consumer/cn/design/harmonyos-symbol/

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

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

相关文章

在 macOS 上,用命令行连接 MySQL(/usr/local/mysql/bin/mysql -u root -p)

根据你提供的文件内容&#xff0c;MySQL 的安装路径是 /usr/local/mysql。要直接使用 mysql 命令&#xff0c;你需要找到 mysql 可执行文件的路径。 在 macOS 上&#xff0c;mysql 客户端通常位于 MySQL 安装目录的 bin 子目录中。因此&#xff0c;完整的路径应该是&#xff1…

使用docker-compose安装ELK(elasticsearch,logstash,kibana)并简单使用

首先服务器上需要安装docker已经docker-compose&#xff0c;如果没有&#xff0c;可以参考我之前写的文章进行安装。 https://blog.csdn.net/a_lllk/article/details/143382884?spm1001.2014.3001.5502 1.下载并启动elk容器 先创建一个网关&#xff0c;让所有的容器共用此网…

解决“无法定位程序输入点 av_buffer_create 于动态链接库 XXX\Obsidian.exe 上”问题

解决“无法定位程序输入点 av_buffer_create 于动态链接库 XXX\Obsidian.exe 上”问题 问题描述 本人在使用zotero中的zotero one&#xff08;青柠学术插件&#xff09;的时候&#xff0c;使用插件跳转obsidian中的对应笔记&#xff0c;出现上图情况。&#xff08;错误中提到的…

《小迪安全》学习笔记05

目录 读取&#xff1a; 写入&#xff1a; &#xff08;其中的读取和写入时我认为比较重要的&#xff0c;所以单独做成了目录&#xff0c;这里的读取和写入是指在进行sql注入的时候与本地文件进行的交互&#xff09; 好久没发博客了。。。从这篇开始的小迪安全学习笔记就开始…

Jupyter notebook中运行dos指令运行方法

Jupyter notebook中运行dos指令运行方法 目录 Jupyter notebook中运行dos指令运行方法一、DOS(磁盘操作系统&#xff09;指令介绍1.1 DOS介绍1.2 DOS指令1.2.1 DIR - 显示当前目录下的文件和子目录列表。1.2.2 CD 或 CHDIR - 改变当前目录1.2.3 使用 CD .. 可以返回上一级目录1…

excel 判断某个单元格的日期,如果超过3天,则在另一个单元格显示超过三天的公式

excel 判断某个单元格的日期&#xff0c;如果超过3天&#xff0c;则在另一个单元格显示超过三天的公式&#xff0c;公式如下&#xff1a; IF(DATEDIF(C627,TODAY(),"d")<4,"3天以内","超过三天") IF(D627"超过3天","文件赶紧…

内存与缓存:保姆级图文详解

文章目录 前言1、计算机存储设备1.1、硬盘、内存、缓存1.2、金字塔结构1.3、数据流通过程 2、数据结构内存效率3、数据结构缓存效率 前言 亲爱的家人们&#xff0c;创作很不容易&#xff0c;若对您有帮助的话&#xff0c;请点赞收藏加关注哦&#xff0c;您的关注是我持续创作的…

eBay账号安全攻略:巧妙应对风险

在跨境电商的浪潮中&#xff0c;eBay宛如一座璀璨的灯塔&#xff0c;照亮了无数买卖双方的交易之路。但别忘了&#xff0c;网络安全的阴霾也在悄然蔓延&#xff0c;让eBay账号时刻处于黑客攻击、数据泄露、钓鱼诈骗等风险的阴影之下。别担心&#xff0c;今天就来为你支支招&…

宝塔php7.4安装报错,无法安装,php8以上可以安装,以下的不行,gd库什么的都正常

宝塔的依赖问题导致的问题&#xff0c;最后手动挂载后才解决。。。废了三天三夜终于搞好了。。。。无语&#xff5e; 建议&#xff1a;不要一直升级宝塔版本&#xff0c;升级前备份或者开服务商的实例镜像&#xff0c;方便恢复&#xff0c;不然&#xff0c;可就GG了&#xff5…

【大数据】机器学习 -----关于data.csv数据集分析案例

打开表 import pandas as pd df2 pd.read_csv("data.csv",encoding"gbk") df2.head()查看数据属性&#xff08;列标题&#xff0c;表形状&#xff0c;类型&#xff0c;行标题&#xff0c;值&#xff09; print("列标题:",df2.columns)Data…

【数据可视化-12】数据分析岗位招聘分析

&#x1f9d1; 博主简介&#xff1a;曾任某智慧城市类企业算法总监&#xff0c;目前在美国市场的物流公司从事高级算法工程师一职&#xff0c;深耕人工智能领域&#xff0c;精通python数据挖掘、可视化、机器学习等&#xff0c;发表过AI相关的专利并多次在AI类比赛中获奖。CSDN…

RabbitMQ故障全解析:消费、消息及日常报错处理与集群修复

文章目录 前言&#xff1a;1 消费慢2 消息丢失3 消息重复消费4 日常报错及解决4.1 报错“error in config file “/etc/rabbitmq/rabbitmq.config” (none): no ending found”4.2 生产者发送消息报错4.3 浏览器打开IP地址&#xff0c;无法访问 RabbitMQ&#xff08;白屏没有结…

网络安全——常用语及linux系统

一、网络安全概念及法规 网络安全&#xff1a;网络空间安全 cyber security 信息系统&#xff1a;由计算机硬件、网络和通信设备、计算机软件、信息资源、信息用户和规章制度组成的已处理信息流为目的的人机一体化系统 信息系统安全三要素&#xff08;CIA&#xff09; 保密…

00_专栏《Redis 7.x企业级开发实战教程》介绍

大家好,我是袁庭新。Redis作为一款高性能、多用途的内存数据库,凭借其丰富的数据结构、高速读写能力、原子操作特性及发布订阅等功能,在缓存加速、分布式锁、消息队列等场景中不可或缺,极大提升了系统性能与开发效率,是现代互联网应用架构的关键组件。 你是否在学习Redis…

深度学习中的卷积和反卷积(四)——卷积和反卷积的梯度

本系列已完结&#xff0c;全部文章地址为&#xff1a; 深度学习中的卷积和反卷积&#xff08;一&#xff09;——卷积的介绍 深度学习中的卷积和反卷积&#xff08;二&#xff09;——反卷积的介绍 深度学习中的卷积和反卷积&#xff08;三&#xff09;——卷积和反卷积的计算 …

SpringMVC Idea 搭建 部署war

1.创建 Idea项目 使用Maven模板 创建 webApp模板项目 2.导入依赖 <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4.0.0 http://ma…

ElasticSearch下

DSL查询 叶子查询&#xff1a;在特定字段里查询特定值&#xff0c;属于简单查询&#xff0c;很少单独使用复合查询&#xff1a;以逻辑方式组合多个叶子查询或更改叶子查询的行为方式 在查询后还可以对查询结果做处理&#xff1a; 排序&#xff1a;按照1个或多个字段做排序分页…

读取长文本,使用读取底表

文章目录 代码有原始数据内表作为主表连接STXL的示例获取物料分类获取物料分类的文本的宏读取分类 https://blog.csdn.net/DeveloperMrMeng/article/details/118354649 代码 "第三种&#xff1a;读取底表获取文本 DATA: LT_TLINE TYPE STANDARD TABLE OF TLINE. DATA: LS…

Ubuntu升级Linux内核教程

本文作者CVE-柠檬i: CVE-柠檬i-CSDN博客 本文使用的方法是dpkg安装&#xff0c;目前版本为5.4.0-204&#xff0c;要升级成5.8.5版本 下载 下载网站&#xff1a;https://kernel.ubuntu.com/mainline/ 在该网站下载deb包&#xff0c;选择自己想要升级的版本&#xff0c;这里是5…

JWT在线解密/解码 - 加菲工具

JWT在线解密/解码 首先进入加菲工具 选择 “JWT 在线解密/解码” https://www.orcc.online 或者直接进入JWT 在线解密/解码 https://www.orcc.online/tools/jwt 进入功能页面 使用 输入对应的jwt内容&#xff0c;点击解码按钮即可