当前位置: 首页 > news >正文

HarmonyOS NEXT:实现电影列表功能展示界面

时至今日HarmonyOS NEXT早已发布运行了,等其正式推出并大规模商用后,HarmonyOS的历史使命就完成并将退出历史舞台,为用户提供丰富的应用选择。但是Harmony NEXT是在HarmonyOS基础上剔除安卓(AOSP)后的产品,属于全新的手机系统。

今天实现一个简单的小案例,从零开始讲解如何通过鸿蒙开发实现一个电影列表功能的案例。

目录

新建初始项目

菜单与电影页

数据接口封装

加载与适配页

电影列表界面


新建初始项目

进入到编辑器中点击左上角的文件点击新建项目,然后选择空的 Empty Ability 进行创建:

然后接下来输入自己的项目名称就行,点击finish即可:

运行本地预览器,可以看到我们的初始项目已经跑通:

菜单与电影页

接下来开始编写我们类似H5端的tabBar导航菜单按钮的功能,这里我们可以借助官网给我们提供的选项卡(Tabs)进行类似的操作,如下所示:

接下来我们开始使用这个tabs选项卡的功能,首选我们先在pages目录下新建两个ArkTS文件然后把这两个文件都暴露出去,如下所示:

然后我们在index.ets文件下通过模块导入的方式,将两个文件进行一个导入,代码如下:

import Home from './home'
import Cinema from './cinema'@Entry
@Component
struct Index {build() {Tabs({ barPosition: BarPosition.End }) {TabContent() {Home().height('100%')}.tabBar("电影")TabContent() {Cinema().height('100%')}.tabBar("影院")}}
}

效果如下所示:

接下来我们就可以在首页组件中撰写相应的首页内容,这里我们也是借助了官方文档中的轮播器进行实现轮播图的操作,具体的代码如下所示:

@Component
struct Home {@State flag: boolean = trueimgList: string[] = ["http://124.223.69.156:5500/h5-01.jpg","http://124.223.69.156:5500/h5-02.jpg","http://124.223.69.156:5500/h5-03.png",]build() {Scroll() {Column() {// 轮播图Swiper() {ForEach(this.imgList, (item: string) => {Image(item).width('100%').height(180)})}.autoPlay(true) // 自动轮播.loop(true) // 无缝衔接// 即将上映Flex({ justifyContent: FlexAlign.SpaceAround }) {Text('正在热映').padding(10).border({ width: { bottom: this.flag ? 3 : 0 } }).borderColor(Color.Red).onClick(() => this.flag = true)Text('即将上映').padding(10).border({ width: { bottom: !this.flag ? 3 : 0 } }).borderColor(Color.Red).onClick(() => this.flag = false)}.margin({ top: 10, bottom: 10 })if (this.flag) {Text('正在热映')} else {Text('即将上映')}}}}
}
export default Home

最终呈现的效果如下所示:

上面我们仅仅是实现了静态页面的内容,如果想实现动态渲染的话就需要调用相应的接口函数,这里我们可以借助官方给我们提供的第三方仓库,网站:地址 如下所示:

然后我们下载我们调用接口的第三方库,终端执行如下命令进行安装:

ohpm install @ohos/axios

然后我们可以在项目的该文件下可以查看项目中安装的相关依赖信息:

数据接口封装

接下来开始对我们调用第三方接口进行一个初始的axios数据封装,在ets目录下新建utils工具目录,然后在工具目录当中封装如下的数据:

import axios, { AxiosResponse } from "@ohos/axios"// 参数统一处理
interface options_type {url: string,method?: string,data?: object,params?: object,headers?: object
}
// 请求返回类型
interface request_dataType {status: number,msg?: string,data: object
}// 创建axios实例
const request = axios.create({baseURL: "https://m.maizuo.com/",headers: {'X-Client-Info': '{"a":"3000","ch":"1002","v":"5.0.4","e":"1596502176387264316178433","bc":"310100"}'}
})
const http = (options: options_type) => {const method = options.method || 'GET'if (method.toLowerCase() === 'get') {options.params = options.data}return request(options)
}
// 针对请求返回的结果处理
const resultFn = async<T> (options: options_type): Promise<T> => {const result: AxiosResponse<request_dataType> = await http(options)return result.data as T
}export default resultFn

然后接下来我们继续在ets目录下新建api目录,用于存放后期的api文件,这里我们新建home.ets文件,然后在该文件中编写对应的home页的内容数据:

import request from '../utils/request'interface return_Home_type {files: filems[],total: number
}
interface filems {poster?: string,name?: string,filmId: number,nation: string,runtime: number,actors: [name: string]
}
interface myHeader {"X-Host": string
}// 获取首页数据
export const getHomeData = (page: number) => {return request<return_Home_type>({url: `gateway?cityId=310100&pageNum=${page}&pageSize=15&type=1&k=266615`,headers: {"X-Host": "mall.film-ticket.film.list"} as myHeader,method: "GET"})
}

然后我们在首页中通过调用生命周期函数 aboutToAppear ,在页面加载之前调用函数打印数据:

然后我们调用写好的接口函数获取对应的数据:

aboutToAppear(): void {getHomeData(this.page).then(res => {console.log(JSON.stringify(res))this.moveData.films = res.films?.map((item: filems) => {item.poster = item.poster?.replace("pic.maizuo.com", "static.maizuo.com/pc/v5")return item})})
}

通过props的方式,将获取到的接口数据传递给子组件:

import { return_Home_type, filems, actors_type } from '../api/home'@Component
struct MoveList {@Prop moveData: return_Home_type@Link page: number// 处理参演人员数据fillter_actors(arr: actors_type[] | undefined) {if (!arr) return "暂无主演"return arr.map((item: actors_type) => {return item.name}).join(' ') // 通过数组转为字符串,用空格拼接}build() {Column() { // 兼容不同的终端GridRow({ gutter: { y: 20 } }) {ForEach(this.moveData.films, (item: filems) => {GridCol({span: { sm:12, md:6, lg: 3 }}) {Flex({ justifyContent: FlexAlign.SpaceBetween }) {Image(item.poster).width('20%').height(100).margin({ right: 10 })Column() {Text(item.name).fontSize(20).fontWeight(FontWeight.Bold).lineHeight(30)Text(this.fillter_actors(item.actors))Row() {Text(item.nation + '|').fontSize(15)Text(`${item.runtime}`).fontSize(15)}}.alignItems(HorizontalAlign.Start).width('80%')}.padding(10)}})}}}
}export default MoveList

最终呈现的效果如下所示:

加载与适配页

为了实现性能上的优化,这里我们需要实现一下下拉加载更多的功能,在Scroll容器当中是有下拉触底的函数的,这里我们简单的调用一下:

为了方便调用,这里我们将之前获取数据的函数抽离一下:

// 请求电影列表数据
getMovieData() {getHomeData(this.page).then(res => {this.moveData.films = res.films?.map((item: filems) => {item.poster = item.poster?.replace("pic.maizuo.com", "static.maizuo.com/pc/v5")return item})// 如果是第一页则直接覆盖move_dataif (this.page === 1) {this.moveData = res} else {// 如果是其他页则直接对films进行拼接(this.moveData as return_Home_type).films =(this.moveData as return_Home_type).films?.concat(res.films as filems[])}})
}
aboutToAppear(): void {this.getMovieData()
}

最终呈现的效果如下所示:

接下来我们给其加上一个回到顶部的按钮,这里我们首先需要给滚动容器创建实例:

然后设置停止滚动触发的函数,然后根据条件的判断来动态显示按钮:

在列表界面,调用屏幕适配变化的函数,来动态的调用获取数据的函数:

呈现的效果如下所示:

电影列表界面

接下来开始实现影院列表界面,首先我们先编写相应的接口函数,代码如下所示:

import request from '../utils/request'interface MyHeader {"X-Host": string
}
interface return_cinema_type {cinemas: cinemas[]
}
export interface cinemas {name: string,address: string
}
interface return_city_type {cities: cities[]
}
export  interface cities {name: string,cityId: number
}
// 获取影院列表
export const getCinemaData = (cityId: number) => {return request<return_cinema_type>({url: `gateway?cityId=${cityId}&ticketFlag=1&k=2500238`,headers: {"X-Host": "mall.film-ticket.cinema.list"} as MyHeader,method: "get"})
}
// 获取城市列表
export const getCity = () => {return request<return_city_type>({url: `gateway?k=2323064`,headers: {"X-Host": "mall.film-ticket.cinema.list"} as MyHeader,method: "get"})
}

接下来我们开始编写对应的影院列表内容数据的布局:

@Component
struct Cinema {@State cityName: string = '上海' // 城市名称@State cityId: number = 310100@State cinemas: cinemas[] = [] // 影院列表cityDialog: CustomDialogController = new CustomDialogController({builder: CityDialog({updateCity: (city: cities) => { this.updateCity(city) }})})updateCity(city: cities) {// 关闭弹框this.cityDialog.close()// 调用列表接口this.getData(city.cityId)// 修改显示名称this.cityName = city.name}// 请求数据getData(cityId: number) {getCinemaData(cityId).then(res=>{this.cinemas = res.cinemas})}aboutToAppear(): void {this.getData(this.cityId)}build() {Scroll() {Column() {Text(this.cityName).width('100%').margin({ bottom: 10 }).textAlign(TextAlign.Center).onClick(()=>{// 展示弹框this.cityDialog.open()})ForEach(this.cinemas, (item: cinemas) => {Text(item.name).fontSize(18).margin({ left: 5 })Text(item.address).fontSize(15).fontColor(Color.Gray).margin({ left: 5 })})}.alignItems(HorizontalAlign.Start)}}
}
export default Cinema

这里我们用到了一个弹框的效果,代码如下所示:

// 创建城市弹框
@CustomDialog
@Component
struct CityDialog {@State cities: cities[] = []controller: CustomDialogController// 定义父组件传入的通信函数updateCity: (city: cities) => void = () => {}aboutToAppear(): void {getCity().then(res => {this.cities = res.cities})}build() {Scroll() {Text('请选择城市').fontSize(20).fontColor(Color.Red).margin(5).padding(5).border({ width: { bottom: 1 } })ForEach(this.cities, (item: cities) => {Text(item.name).width('100%').height(50).textAlign(TextAlign.Center).onClick(() => {this.updateCity(item)})})}}
}

最终呈现的效果如下所示:

具体的界面搭建与接口调用参考的是 卖座电影 的一个H5案例,如下所示:

截止现在,一个简易的电影列表展示内容已经做完了,大家可以自行去练习一下!

相关文章:

  • IDEA相关设置总结
  • 03Frenet与Cardesian坐标系(Frenet转Cardesian公式推导)
  • Win10 QT 配置Android开发环境-jdk/sdk/gradle
  • 探究Spring的单例设计模式--单例Bean
  • 25中国烟草校园招聘面试问题总结 烟草面试全流程及面试攻略
  • 国外电商系统开发-需求记录
  • 【C++】异常处理
  • Android Stuido中编译信息出现乱码的解决方式
  • ClickHouse | 查询
  • C++ | Leetcode C++题解之第446题等差数列划分II-子序列
  • 最大正方形 Python题解
  • 第二十三节:学习拦截器或者使用AOP实现用户token参数请求检测(自学Spring boot 3.x的第六天)
  • IDEA几大常用AI插件
  • springboot+satoken实现刷新token(值变化)
  • STL之stackqueue篇(上)探索C++ STL中的Queue与Stack——构建数据处理的基础框架
  • 11111111
  • axios 和 cookie 的那些事
  • C++入门教程(10):for 语句
  • C学习-枚举(九)
  • iOS高仿微信项目、阴影圆角渐变色效果、卡片动画、波浪动画、路由框架等源码...
  • iOS小技巧之UIImagePickerController实现头像选择
  • JavaWeb(学习笔记二)
  • js
  • js作用域和this的理解
  • MySQL用户中的%到底包不包括localhost?
  • node-glob通配符
  • webpack+react项目初体验——记录我的webpack环境配置
  • webpack4 一点通
  • 程序员该如何有效的找工作?
  • 分享几个不错的工具
  • 基于OpenResty的Lua Web框架lor0.0.2预览版发布
  • 基于Volley网络库实现加载多种网络图片(包括GIF动态图片、圆形图片、普通图片)...
  • 驱动程序原理
  • 如何解决微信端直接跳WAP端
  • 深入浏览器事件循环的本质
  • 微信小程序:实现悬浮返回和分享按钮
  • 学习Vue.js的五个小例子
  • 验证码识别技术——15分钟带你突破各种复杂不定长验证码
  • 要让cordova项目适配iphoneX + ios11.4,总共要几步?三步
  • 原生Ajax
  • Android开发者必备:推荐一款助力开发的开源APP
  • Java总结 - String - 这篇请使劲喷我
  • MyCAT水平分库
  • 蚂蚁金服CTO程立:真正的技术革命才刚刚开始
  • ​1:1公有云能力整体输出,腾讯云“七剑”下云端
  • ​探讨元宇宙和VR虚拟现实之间的区别​
  • # AI产品经理的自我修养:既懂用户,更懂技术!
  • #pragma once
  • (01)ORB-SLAM2源码无死角解析-(66) BA优化(g2o)→闭环线程:Optimizer::GlobalBundleAdjustemnt→全局优化
  • (DFS + 剪枝)【洛谷P1731】 [NOI1999] 生日蛋糕
  • (二)windows配置JDK环境
  • (回溯) LeetCode 40. 组合总和II
  • (解决办法)ASP.NET导出Excel,打开时提示“您尝试打开文件'XXX.xls'的格式与文件扩展名指定文件不一致
  • (三)Pytorch快速搭建卷积神经网络模型实现手写数字识别(代码+详细注解)
  • (三)终结任务