HarmonyOS NEXT 5.0鸿蒙开发一套影院APP(附带源码)

简介: 本项目基于HarmonyOS NEXT 5.0开发了一款影院应用程序,主要实现了电影和影院信息的展示功能。应用包括首页、电影列表、影院列表等模块。首页包含轮播图与正在热映及即将上映的电影切换显示;电影列表模块通过API获取电影数据并以网格形式展示,用户可以查看电影详情;影院列表则允许用户选择城市后查看对应影院信息,并支持城市选择弹窗。此外,项目中还集成了Axios用于网络请求,并进行了二次封装以简化接口调用流程,同时添加了请求和响应拦截器来处理通用逻辑。整体代码结构清晰,使用了组件化开发方式,便于维护和扩展。该简介概括了提供的内容,但请注意实际开发中还需考虑UI优化、性能提升等方面的工作。

鸿蒙开发HarmonyOS NEXT5.0开发一套影院APP


效果图

电影 影院

创建项目

tabs菜单实现

在Tabs中使用TabContent,对应一个切换页签的内容视图。改写Index.ets,实现tabs菜单的切换

@Entry

@Component

struct Index {

 build() {

   Tabs({ barPosition: BarPosition.End }) {

     TabContent() {

       Text('电影页面')

     }.tabBar('电影')

     TabContent() {

       Text('影院页面')

     }.tabBar('影院')

   }

 }

}

第三方axios引入和基本使用

简介

Axios ,是一个基于 promise 的网络请求库,可以运行 node.js 和浏览器中。本库基于Axios 原库v1.3.4版本进行适配,使其可以运行在 OpenHarmony,并沿用其现有用法和特性。

  • http 请求
  • Promise API
  • request 和 response 拦截器
  • 转换 request 和 response 的 data 数据
  • 自动转换 JSON data 数据

axios三方库封装的意义对axios进行封装的意义在于提供更高层次的抽象,以便简化网络请求的使用和管理。以下是一些具体的理由:

1.统一接口:封装后,可以统一管理所有的网络请求接口,使得在应用中调用网络请求时更加一致,减少重复代码。

2.简化配置:封装可以避免每次请求都需要重复配置相似的参数(例如headers、请求方式等),通过配置对象直接传入更简洁。

3.请求和响应拦截器:封装允许在发送请求之前或收到响应之后,对请求或响应进行处理,比如添加公共的请求头、处理错误、数据格式化等。

4.错误处理:通过自定义的错误处理机制,可以实现统一的错误处理逻辑,比如根据状态码处理特定的错误(例如401未登录、403权限不足等)。

5.增强功能:可以根据项目需求添加额外的功能,例如显示加载状态、处理用户登录状态等。

6.提高可维护性:将网络请求相关的逻辑集中管理,可以让代码更加清晰,降低维护成本。

7.支持特定业务需求:可根据实际的业务需求扩展功能,比如提供缓存机制、重试机制等,增强请求的灵活性。

下载安装

ohpm install @ohos/axios

OpenHarmony ohpm 环境配置等更多内容,请参考如何安装 OpenHarmony ohpm 包

需要权限

ohos.permission.INTERNET

module.json5文件中添加以下内容,开启网络请求权限。

   "requestPermissions": [

     {

       "name": "ohos.permission.INTERNET"

     }

   ]

axios二次封装和类型定义

创建request.ets,实现axios的二次封装

import axios, { InternalAxiosRequestConfig, AxiosResponse, AxiosError } from '@ohos/axios'

import { ResultEnum } from '../enums/ResultEnum';

// 创建 axios 实例

const request = axios.create({

 baseURL: "https://m.maizuo.com",

 timeout: 50000,

 headers: {

   "Content-Type": "application/json;charset=utf-8",

   'x-Client-Info': '{"a":"3000","ch":"1002","v":"5.2.1","e":"1734338281267481973260289","bc":"320800"}'

 }

})

// 添加请求拦截器

request.interceptors.request.use((config: InternalAxiosRequestConfig) => {

 // 对请求数据做点什么 如 添加token

 return config;

}, (error: AxiosError) => {

 // 对请求错误做些什么

 return Promise.reject(error);

});

// 添加响应拦截器

request.interceptors.response.use((response: AxiosResponse) => {

 const result: AxiosResponse<R> = response.data

 console.info(JSON.stringify(response.data))

 if (result.status === ResultEnum.SUCCESS) {

   return response.data.data;

 }

 return Promise.reject(new Error(result.data.msg || "Error"));

}, (error: AxiosError) => {

 // 对响应错误做点什么

 return Promise.reject(error.message);

});

// 统一返回类型

interface R {

 status: number,

 msg?: string,

 data: object

}

export default request;

正在热映和即将上映实现

创建Home.etsCinema.ets组件,以及index.etsapi组件

首页api封装

import request from '../utils/request'

export interface MoveData {

 films?: Films[],

 total?: number

}

export interface Films {

 filmId: number;

 name: string;

 poster: string;

 actors: Array<Actors>;

 director: string;

 category: string;

 synopsis: string;

 filmType: FilmType;

 nation: string;

 language?: string;

 videoId?: string;

 premiereAt: number;

 timeType: number;

 runtime: number;

 grade: string;

 item: Item;

 isPresale: boolean;

 isSale: boolean;

};

export interface Actors {

 name: string;

 role: string;

 avatarAddress: string;

};

export interface FilmType {

 name: string;

 value: number;

};

export interface Item {

 name: string;

 type: number;

};

export const getIndex = (page: number) => {

 return request<string,MoveData>({

   url: `/gateway?cityId=320800&pageNum=${page}&pageSize=10&type=1&k=5393095`,

   method: "get",

   headers:{

     "X-Host": "mall.film-ticket.film.list",

   }

 })

}

首页内容

这里分两个部分,轮播图和电影列表,使用Swiper组件实现轮播图功能,Flex组件实现正在热映和即将上映得功能,配合Scroll组件实现内容滚动、点击返回顶部,并刷新请求接口

import { Films, getIndex, MoveData } from '../api/index'

import MovieList from '../components/MovieList'

@Component

struct Home {

 // 显示正在热映/即将上映

 @State flag: boolean = true

 // 分页参数

 @State page: number = 1

 // 正在热映的电影数据

 @State moveData: MoveData = {}

 @State len: number = 0

 scroll: Scroller = new Scroller()

 aboutToAppear(): void {

   this.getData()

 }

 // 请求电影列表功能单独封装

 getData() {

   getIndex(this.page).then((data:MoveData) => {

     // 替换图片地址

     data.films?.map((item: Films) => {

       item.poster = item.poster.replace("pic.maizuo.com", "static.maizuo.com/pc/v5")

       return item

     }) as Array<Films>

     if (this.page === 1) {

       this.moveData = data

     } else {

       (this.moveData as MoveData).films = (this.moveData as MoveData).films?.concat(data.films as Films[])

     }

   })

 }

 build() {

   Column() {

     Scroll(this.scroll) {

       Column() {

         // 轮播图

         Swiper() {

           Image($r('app.media.h5_01')).width('100%').height(180)

           Image($r('app.media.h5_02')).width('100%').height(180)

           Image($r('app.media.h5_03')).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

             })

         }

         if (this.flag) {

           // Text('正在热映内容')

           MovieList({ moveData: this.moveData, page: this.page })

         } else {

           Text('即将上映内容')

         }

       }

     }.onScrollEdge((size) => {

       if (size === 2) {

         this.page++

         this.getData()

       }

     })

     // 滚动停止时触发

     .onScrollStop(() => {

       //  this.scroll.currentOffset() 返回当前的滚动偏移量 yOffset是y轴的偏移量,赋值给len

       this.len = this.scroll.currentOffset().yOffset

     })

     if (this.len > 250) {

       Text('顶部')

         .fontWeight(FontWeight.Bold)

         .width(40)

         .height(40)

         .backgroundColor(Color.White)

         .position({ x: '80%', y: '80%' })

         .onClick(() => {

           this.scroll.scrollTo({ yOffset: 0, xOffset: 0 })

         })

     }

   }

 }

}

export default Home

电影列表内容和样式问题

将电影列表作为一个组件,写一个MovieList.ets组件

import { Actors, Films, MoveData } from "../api"

@Component

struct MovieList {

 // 定义moveData数据

 @Prop moveData: MoveData

 @Link page: number

 filter_actors(arr: Actors[]) {

   // 如果arr为空

   if (!arr){

     return '暂无主演'

   }

   return arr.map((item: Actors) => {

     return item.name

   }).join(' ') // 通过数组转为字符串,用空格进行拼接

 }

 aboutToAppear(): void {

 }

 build() {

   Column() {

     GridRow({

       gutter: { y: 20 }

     }) {

       ForEach(this.moveData.films, (item: Films) => {

         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.filter_actors(item.actors))

               Row(){

                 Text(item.nation+'|').fontSize(15)

                 Text(`${item.runtime}`).fontSize(15)

               }

             }.alignItems(HorizontalAlign.Start).width('80%')

           }.padding(10)

         }

       })

     }

   }

 }

}

export default MovieList

影院列表

页面内容

这里主要分为两块内容,影院列表和城市选择,城市选择这里我们使用CustomDialogController自定义一个弹窗,并实现影院列表的刷新

import { Cinemas, Cities, getCinema, getCity } from '../api/cinema'


// 创建一个选择城市的弹窗

@CustomDialog

struct CityDialog {

 controller: CustomDialogController = new CustomDialogController({

   builder: CityDialog({})

 })

 @State cities: Cities[] = []

 // 定义父组件传入的通信函数

 updateCity: (item: Cities) => void = () => {

 }


 aboutToAppear(): void {

   getCity().then(data => {

     this.cities = data.cities

   })

 }


 build() {

   Scroll() {

     Column() {

       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)

           })

       })

     }

   }

 }

}



@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) {

   console.info(JSON.stringify(city))

   this.cityName = city.name

   this.cityDialog.close()

   this.getData(city.cityId)

 }


 aboutToAppear(): void {

   this.getData(this.cityId)

 }


 getData(cityId: number) {

   getCinema(cityId).then(data => {

     console.info(JSON.stringify(data))

     this.cinemas = data.cinemas

   })

 }


 build() {

   Scroll() {

     Column() {

       Text(this.cityName).fontColor(Color.Red).fontSize(20).fontWeight(FontWeight.Bold)

         .onClick(() => {

           this.cityDialog.open()

         })


       ForEach(this.cinemas, (item: Cinemas) => {

         Column({ space: 10 }) {

           Text(item.name).fontSize(18).width('100%')

           Text(item.address)

             .fontSize(15)

             .fontColor(Color.Gray)

             .width('100%')

         }.margin(10).justifyContent(FlexAlign.Start)

       })

     }

   }

 }

}


export default Cinema

影院api定义

创建cinema.etsapi组件

import request from '../utils/request'



// 获取影院列表

export const getCinema = (cityId: number) => {

 return request<string, CinemasData>({

   url: `/gateway?cityId=${cityId}&ticketFlag=1&k=2500238`,

   method: 'GET',

   headers: {

     "X-Host": "mall.film-ticket.cinema.list",

   }

 })

}


export const getCity = () => {

 return request<string,CitiesData>({

   url: `/gateway?k=9628046`,

   method: 'GET',

   headers: {

     "X-Host": "mall.film-ticket.city.list",

   }

 })

}


// 影院数据

export interface CinemasData {

 cinemas: Array<Cinemas>;

}


export interface Cinemas {

 cinemaId: number;

 name: string;

 address: string;

 longitude: number;

 latitude: number;

 gpsAddress: string;

 cityId: number;

 cityName: string;

 districtId: number;

 districtName: string;

 district: District;

 phone: string;

 telephones: Array<string>;

 isVisited: number;

 lowPrice: number;

 Distance: number;

 eTicketFlag: number;

 seatFlag: number;

}


export interface District {

 districtId: number;

 name: string;

}


// 城市列表数据

export interface CitiesData {

 cities: Array<Cities>;

}


export interface Cities {

 cityId: number;

 name: string;

 pinyin: string;

 isHot: number;

}

到这里,影院的基本功能已经完成,还有很多地方需要优化,你可以根据自己的需求,进行完善。


公众号搜“Harry技术”,关注我,带你看不一样的人间烟火!回复“鸿蒙”获取源码地址。

目录
相关文章
|
5天前
|
Web App开发 前端开发 安全
语音交友app系统源码功能及技术研发流程剖析
语音交友App核心功能包括语音聊天(一对一、群聊、语音消息)、语音房间(直播、主题房、管理)、社交互动(好友、关注、打赏)、内容发现、音效美化、通知提醒及安全隐私等。开发流程涵盖需求分析、技术选型(前端、后端、数据库、实时通信)、UI/UX设计、前后端开发、实时通信集成、音效处理、测试优化、部署上线及运营维护,确保稳定高效运行并持续优化用户体验。
|
5天前
|
前端开发 安全 开发工具
【11】flutter进行了聊天页面的开发-增加了即时通讯聊天的整体页面和组件-切换-朋友-陌生人-vip开通详细页面-即时通讯sdk准备-直播sdk准备-即时通讯有无UI集成的区别介绍-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【11】flutter进行了聊天页面的开发-增加了即时通讯聊天的整体页面和组件-切换-朋友-陌生人-vip开通详细页面-即时通讯sdk准备-直播sdk准备-即时通讯有无UI集成的区别介绍-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
128 89
【11】flutter进行了聊天页面的开发-增加了即时通讯聊天的整体页面和组件-切换-朋友-陌生人-vip开通详细页面-即时通讯sdk准备-直播sdk准备-即时通讯有无UI集成的区别介绍-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
|
2天前
|
人工智能 搜索推荐 数据挖掘
原生鸿蒙版小艺APP接入DeepSeek-R1,为HarmonyOS应用开发注入新活力
原生鸿蒙版小艺APP成功接入DeepSeek-R1,为HarmonyOS用户带来更智能高效的交互体验。通过此次升级,用户在编程、学习和工作中的问题可迅速获得专业解答。同时,此举为HarmonyOS应用开发者提供了新的技术参考,激发了更多创新应用场景的开发,助力打造差异化竞争优势,推动HarmonyOS生态繁荣发展。
98 68
原生鸿蒙版小艺APP接入DeepSeek-R1,为HarmonyOS应用开发注入新活力
|
4天前
|
机器学习/深度学习 存储 人工智能
MNN-LLM App:在手机上离线运行大模型,阿里巴巴开源基于 MNN-LLM 框架开发的手机 AI 助手应用
MNN-LLM App 是阿里巴巴基于 MNN-LLM 框架开发的 Android 应用,支持多模态交互、多种主流模型选择、离线运行及性能优化。
445 13
MNN-LLM App:在手机上离线运行大模型,阿里巴巴开源基于 MNN-LLM 框架开发的手机 AI 助手应用
|
6天前
|
安全 网络安全 Android开发
APP-国内主流安卓商店-应用市场-鸿蒙商店上架之必备前提·全国公安安全信息评估报告如何申请-需要安全评估报告的资料是哪些-优雅草卓伊凡全程操作
APP-国内主流安卓商店-应用市场-鸿蒙商店上架之必备前提·全国公安安全信息评估报告如何申请-需要安全评估报告的资料是哪些-优雅草卓伊凡全程操作
45 11
APP-国内主流安卓商店-应用市场-鸿蒙商店上架之必备前提·全国公安安全信息评估报告如何申请-需要安全评估报告的资料是哪些-优雅草卓伊凡全程操作
|
6天前
|
供应链 数据挖掘 API
1688APP 原数据 API 接口的开发、应用与收益
1688作为阿里巴巴旗下的B2B平台,汇聚海量供应商和商品资源。其APP原数据API接口为开发者提供获取商品详细信息的强大工具,涵盖商品标题、价格、图片等。通过注册开放平台账号、申请API权限并调用接口,开发者可构建比价工具、供应链管理及自动化上架工具等应用,提升用户体验与运营效率,创造新的商业模式。示例代码展示了如何使用Python调用API并解析返回结果。
52 8
|
2天前
|
安全 JavaScript 前端开发
小游戏源码开发之可跨app软件对接是如何设计和开发的
小游戏开发团队常需应对跨平台需求,为此设计了成熟的解决方案。流程涵盖游戏设计、技术选型、接口设计等。首先明确游戏功能与特性,选择合适的技术架构和引擎(如Unity或Cocos2d-x)。接着设计通用接口,确保与不同App的无缝对接,并制定接口规范。开发过程中实现游戏逻辑和界面,完成登录、分享及数据对接功能。最后进行测试优化,确保兼容性和性能,发布后持续维护更新。
|
3天前
|
前端开发 Java 测试技术
语音app系统软件源码开发搭建新手启蒙篇
在移动互联网时代,语音App已成为生活和工作的重要工具。本文为新手开发者提供语音App系统软件源码开发的启蒙指南,涵盖需求分析、技术选型、界面设计、编码实现、测试部署等关键环节。通过明确需求、选择合适的技术框架、优化用户体验、严格测试及持续维护更新,帮助开发者掌握开发流程,快速搭建功能完善的语音App。
|
26天前
|
前端开发 Java 开发工具
【03】完整flutter的APP打包流程-以apk设置图标-包名-签名-APP名-打包流程为例—-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈 章节内容【03】
【03】完整flutter的APP打包流程-以apk设置图标-包名-签名-APP名-打包流程为例—-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈 章节内容【03】
70 18
【03】完整flutter的APP打包流程-以apk设置图标-包名-签名-APP名-打包流程为例—-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈 章节内容【03】

热门文章

最新文章

  • 1
    MNN-LLM App:在手机上离线运行大模型,阿里巴巴开源基于 MNN-LLM 框架开发的手机 AI 助手应用
  • 2
    【11】flutter进行了聊天页面的开发-增加了即时通讯聊天的整体页面和组件-切换-朋友-陌生人-vip开通详细页面-即时通讯sdk准备-直播sdk准备-即时通讯有无UI集成的区别介绍-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
  • 3
    【Azure App Service】基于Linux创建的App Service是否可以主动升级内置的Nginx版本呢?
  • 4
    微信小程序 app.json 配置文件解析与应用
  • 5
    【05】flutter完成注册页面完善样式bug-增加自定义可复用组件widgets-严格规划文件和目录结构-规范入口文件-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
  • 6
    【Azure Function】Function App出现System.IO.FileNotFoundException异常
  • 7
    【Azure Logic App】使用MySQL 新增行触发器遇见错误 :“Unknown column 'created_at' in 'order clause'”
  • 8
    轻松搭建婚恋交友系统源码,H5/小程序/APP自动适配,智能匹配恋爱交友平台快速落地
  • 9
    【01】vs-code如何配置flutter环境-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈-供大大的学习提升
  • 10
    阿里云APP备案流程图以及备案所需材料整理,跟着教程一步步操作