前言
综合运用本学期所学内容及个人自学知识,使用HarmonyOS 4.0及以上版本开发一款具有实用性和创新 性的移动应用软件。
一、项目介绍
黑马健康App是一款功能全面的健康管理应用,黑马健康App通过提供个性化的饮食记录、健康评估等功能,帮助用户轻松管理健康,改善饮食和生活习惯。 还提供个性化的推荐服务,确保用户能够获得最适合自己的健康和运动建议。总的来说,黑马健康App是一款功能全面、注重个性化推荐的健康管理应用,旨在为用户提供一站式的健康和运动解决方案。
二、具体页面实现
1.一次开发多端部署
(1)页面分析
多设备响应式布局,通过不同设备的屏幕大小来进行自定义样式布局。
(2)代码
//媒体查询工具类
import mediaQuery from '@ohos.mediaquery'
import BreakpointConstants from '../constants/BreakpointConstants'
export default class BreakpointSystem{
//监听器(小屏幕中屏幕大屏幕)(ets文件导入BreakpointConstants)
//监听器分别设置不同的查询条件
private smListener: mediaQuery.MediaQueryListener = mediaQuery.matchMediaSync(BreakpointConstants.RANGE_SM)
private mdListener: mediaQuery.MediaQueryListener = mediaQuery.matchMediaSync(BreakpointConstants.RANGE_MD)
private lgListener: mediaQuery.MediaQueryListener = mediaQuery.matchMediaSync(BreakpointConstants.RANGE_LG)
//回调函数,什么屏幕存什么标记
smListenerCallback(result: mediaQuery.MediaQueryResult){
if(result.matches){//匹配则存储
//调用updateCurrentBreakpoint函数,传标记
this.updateCurrentBreakpoint(BreakpointConstants.BREAKPOINT_SM)
}
}
mdListenerCallback(result: mediaQuery.MediaQueryResult){
if(result.matches){
this.updateCurrentBreakpoint(BreakpointConstants.BREAKPOINT_MD)
}
}
lgListenerCallback(result: mediaQuery.MediaQueryResult){
if(result.matches){
this.updateCurrentBreakpoint(BreakpointConstants.BREAKPOINT_LG)
}
}
//
updateCurrentBreakpoint(breakpoint: string){
//currentBreakpoint设置为全局变量,值为传入的标记
AppStorage.SetOrCreate(BreakpointConstants.CURRENT_BREAKPOINT, breakpoint)
}
//注册
register(){
this.smListener.on('change', this.smListenerCallback.bind(this))
this.mdListener.on('change', this.mdListenerCallback.bind(this))
this.lgListener.on('change', this.lgListenerCallback.bind(this))
}
//取消注册
unregister(){
this.smListener.off('change', this.smListenerCallback.bind(this))
this.mdListener.off('change', this.mdListenerCallback.bind(this))
this.lgListener.off('change', this.lgListenerCallback.bind(this))
}
}
declare interface BreakpointTypeOptions<T>{
//类型不确定(泛型)
sm?:T,
md?:T,
lg?:T
}
export default class BreakpointType<T>{
options: BreakpointTypeOptions<T>
constructor(options: BreakpointTypeOptions<T>) {
this.options = options
}
getValue(breakpoint: string): T{
return this.options[breakpoint]
}
}
//首页
import BreakpointType from '../common/bean/BreanpointType'
import BreakpointConstants from '../common/constants/BreakpointConstants'
import { CommonConstants } from '../common/constants/CommonConstants'
import BreakpointSystem from '../common/utils/BreakpointSystem'
import RecordIndex from '../view/record/RecordIndex'
@Entry
@Component
struct Index {
//状态变量,默认为0
@State currentIndex: number = 0
//
private breakpointSystem:BreakpointSystem=new BreakpointSystem()
@StorageProp('currentBreakpoint') currentBreakpoint:string=BreakpointConstants.BREAKPOINT_SM//只读且初始化为小屏幕
//自定义bar(参数:导航栏题目,图片,序号)
@Builder TabBarBuilder(title:ResourceStr, image:ResourceStr, index:number){
Column({space:CommonConstants.SPACE_8}){
Image(image)
.width(22)
.fillColor(this.selectColor(index))
Text(title)
.fontSize(14)
.fontColor(this.selectColor(index))
}
}
//生命周期函数
aboutToAppear(){
this.breakpointSystem.register()
}
aboutToDisappear(){
this.breakpointSystem.unregister()
}
//底部导航栏颜色
selectColor(index:number){
//状态变量与序号相同则变为主题色,不同则是灰的
return this.currentIndex===index?$r('app.color.primary_color'):$r('app.color.gray')
}
// chooseBarPosition(){
// return new BreakpointType({
// sm:BarPosition.End,
// md:BarPosition.Start,
// lg:BarPosition.Start,
// }).getValue(this.currentBreakpoint)
// }
build() {
Tabs({barPosition: BreakpointConstants.BAR_POSITION.getValue(this.currentBreakpoint)}) {//导航栏底部
TabContent(){
RecordIndex()//调用饮食记录页面
}
.tabBar(this.TabBarBuilder($r('app.string.tab_record'), $r('app.media.ic_calendar'), 0))
TabContent(){
Text('发现页面')
}
.tabBar(this.TabBarBuilder($r('app.string.tab_discover'), $r('app.media.discover'), 1))
TabContent(){
Text('我的主页')
}
.tabBar(this.TabBarBuilder($r('app.string.tab_user'), $r('app.media.ic_user_portrait'), 2))
}
.width('100%')
.height('100%')
.onChange(index=>this.currentIndex=index)//状态变量获取目前页面的序号
.vertical(new BreakpointType({
sm:false,
md:true,
lg:true
}).getValue(this.currentBreakpoint))//
}
}
//统计卡片(饮食记录页面)
import BreakpointType from '../../common/bean/BreanpointType'
import BreakpointConstants from '../../common/constants/BreakpointConstants'
import { CommonConstants } from '../../common/constants/CommonConstants'
import DateUtil from '../../common/utils/DateUtil'
import CalorieStats from './CalorieStats'
import DatePickDialog from './DatePickDialog'
import NutrientStats from './NutrientStats'
@Component
export default struct StatsCard {
//单向绑定prop,读取selectedDate(number类型且没有精确到时分秒)
@StorageProp('selectedDate') selectedDate:number=DateUtil.beginTimeOfDay(new Date())
@StorageProp('currentBreakpoint') currentBreakpoint:string=BreakpointConstants.BREAKPOINT_SM//只读且初始化为小屏幕
controller:CustomDialogController=new CustomDialogController({
builder:DatePickDialog({selectedDate:new Date(this.selectedDate)})//下次打开传入
})
build() {
Column(){
//1.日期信息
Row(){
Text(DateUtil.formatDate(this.selectedDate))
.fontColor($r('app.color.secondary_color'))
Image($r('app.media.ic_public_spinner'))
.width(20)
.fillColor($r('app.color.secondary_color'))
}
.padding(CommonConstants.SPACE_8)
.onClick(()=>this.controller.open())//打开弹窗
//2.统计信息
Swiper(){//滑动轮播
//2.1.热量统计
CalorieStats()
//2.2.营养素统计
NutrientStats()
}
.width('100%')
.backgroundColor(Color.White)
.borderRadius(CommonConstants.DEFAULT_18)
.indicatorStyle({selectedColor:$r('app.color.primary_color')})//穿梭框样式,被选中后的颜色
.displayCount(new BreakpointType({
sm:1,
md:1,
lg:2
}).getValue(this.currentBreakpoint))
}
.width(CommonConstants.THOUSANDTH_940)
.backgroundColor($r('app.color.stats_title_bgc'))
.borderRadius(CommonConstants.DEFAULT_18)
}
}
(3)运行结果
总结
通过本视频的学习,我学会了如何进行多设备响应式布局,即在不同的设备上的样式等也会随之响应发生变化。