快速入门HarmonyOS应用开发(三)
目录
前言
1、应用沉浸式
2、主页框架搭建
3、实现效果
前言
本系列案例完整代码已上传GitHub:HarmonyDemos
------觉得还行的麻烦动动发财的小手点个小星星啦^_^
今天咱们接着上一篇中的功能来继续实现案例Demo,做完闪屏页之后肯定是要到主页了,那今天咱们就来搭建一下APP的主页框架,实现底部的Tab和对应页面的切换,话不多说,开干!
1、应用沉浸式
首先来给咱们的应用实现沉浸式效果,参考官网中的实现方式:
开发应用沉浸式效果
我们选择的方案是窗口全屏布局方案,整体上分为以下几个步骤:
①、调用setWindowLayoutFullScreen()接口设置窗口全屏
②、使用getWindowAvoidArea()接口获取当前布局遮挡区域
③、注册监听函数,动态获取避让区域的实时数据
④、布局中的UI元素需要避让状态栏和导航区域
实现代码:在EntryAbility的onWindowStageCreate()的loadContent()回调中添加代码
// 使用同步方式获取应用主窗口let windowClass: window.Window = windowStage.getMainWindowSync()// 设置状态栏字体颜色windowClass.setWindowSystemBarProperties({statusBarContentColor: '#FFFFFF'})// 设置窗口全屏windowClass.setWindowLayoutFullScreen(true)// 获取布局避让遮挡的区域let topType = window.AvoidAreaType.TYPE_SYSTEMlet topArea = windowClass.getWindowAvoidArea(topType)let topRectHeight = topArea.topRect.heightAppStorage.setOrCreate('topRectHeight', topRectHeight)let bottomType = window.AvoidAreaType.TYPE_NAVIGATION_INDICATORlet bottomArea = windowClass.getWindowAvoidArea(bottomType)let bottomRectHeight = bottomArea.bottomRect.heightAppStorage.setOrCreate('bottomRectHeight', bottomRectHeight)// 注册监听函数,动态获取避让区域的实时数据windowClass.on('avoidAreaChange', (data) => {if (data.type === window.AvoidAreaType.TYPE_SYSTEM) {let topRectHeight = data.area.topRect.heightAppStorage.setOrCreate('topRectHeight', topRectHeight)} else if (data.type == window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR) {let bottomRectHeight = data.area.bottomRect.heightAppStorage.setOrCreate('bottomRectHeight', bottomRectHeight)}})
关于第4步需要在实际的页面中设置布局的底部Padding:
NavDestination() {
}.hideTitleBar(true).padding({bottom:DisplayUtil.getBottomRectHeight(this.getUIContext())})
这里用到了一个工具方法,其实就是对之前存储的数据进行获取之后封装的一个方法,代码如下:
export class DisplayUtil {static getTopRectHeight(context: UIContext): number {return context.px2vp(AppStorage.get<number>('topRectHeight') as number)}static getBottomRectHeight(context: UIContext): number {return context.px2vp(AppStorage.get<number>('bottomRectHeight') as number)}
}
从上面的代码中可以看到咱们有使用AppStorage进行存储和获取数据,关于AppStorage的使用,大家可以参考:
AppStorage:应用全局的UI状态存储
它是HarmonyOS中提供的用于全局内存缓存的机制,与应用进程绑定,需要注意的是它并不是持久化的存储,是内存缓存。
2、主页框架搭建
想要实现底部Tab之间的切换效果,我们需要使用到一个新的系统提供的组件Tabs组件,参考:
选项卡 (Tabs)
打开MainPage页面,我们需要的整体结构是上下结构,上面是每个Tab的页面,下面是Tab按钮。
首先,我们来自定义底部的Tab按钮,这里放置5个按钮,先来定义每个按钮的样式:
@State currentTabIndex: number = CommonConstant.TAB_INDEX
@BuilderTabItem(tabInfo: TabInfo) {Column() {Image(this.currentTabIndex === tabInfo.index ? tabInfo.selectImg : tabInfo.img).objectFit(ImageFit.Contain).width($r('app.float.vp_24')).height($r('app.float.vp_24')).margin({ bottom: $r('app.float.vp_5') })Text(tabInfo.title).width(CommonConstant.FULL_PERCENT).height($r('app.float.vp_16')).fontSize($r('app.float.fp_12')).textAlign(TextAlign.Center).fontColor(this.currentTabIndex === tabInfo.index ? $r("app.color.color_primary") :$r('app.color.color_unselect'))}.width(CommonConstant.FULL_PERCENT).height(CommonConstant.FULL_PERCENT).justifyContent(FlexAlign.Center).width(CommonConstant.PERCENT_20).onClick(() => {this.currentTabIndex = tabInfo.index})}
这里我们用到了@Builder这个装饰器,它是自定义的构建函数,关于它的使用,大家可以参考:
@Builder装饰器:自定义构建函数
在点击这个按钮的时候,通过对上面定义的currentTabIndex变量值的改变来切换不同的下标。
接着,通过循环来实现5个Tab按钮的创建:
@BuilderBottomTabView() {Column() {Divider().width(CommonConstant.FULL_PERCENT).color(Color.Black).opacity(CommonConstant.OPACITY_01).strokeWidth(CommonConstant.WEIGHT_1)Row() {ForEach(tabInfos, (button: TabInfo) => {this.TabItem(button)})}.width(CommonConstant.FULL_PERCENT).height($r('app.float.vp_50')).alignItems(VerticalAlign.Center).backgroundColor(Color.White)}.width(CommonConstant.FULL_PERCENT)}
这里我们使用了ForEach来进行循环渲染,关于ForEach的详细使用,大家可以参考:
ForEach:循环渲染
这里ForEach的第一个参数是我们提前准备好的Tabs的数据,我们把它放在了src/main/ets/model下面:
export class TabInfo {index: number = 0img: Resource = $r('app.media.tab_index')selectImg: Resource = $r('app.media.tab_indexed')title: Resource = $r('app.string.tab_index')
}const tabInfos: TabInfo[] = [{index: 0,img: $r('app.media.tab_index'),selectImg: $r('app.media.tab_indexed'),title: $r('app.string.tab_index')},{index: 1,img: $r('app.media.tab_financial'),selectImg: $r('app.media.tab_financialed'),title: $r('app.string.tab_financial')},{index: 2,img: $r('app.media.tab_video'),selectImg: $r('app.media.tab_videoed'),title: $r('app.string.tab_video')},{index: 3,img: $r('app.media.tab_message'),selectImg: $r('app.media.tab_messaged'),title: $r('app.string.tab_msg')},{index: 4,img: $r('app.media.tab_mine'),selectImg: $r('app.media.tab_mined'),title: $r('app.string.tab_mine')}
]export { tabInfos }
做完上面这些之后,我们就可以来实现整个页面的结构了,在MainPage的NavDestination中通过Column组件来完成上下结构的布局,上面使用Tabs组件,下面使用自定义的BottomTabView():
build() {NavDestination() {Column() {Tabs({ index: this.currentTabIndex }) {TabContent() {HomePage()}TabContent() {FinancialPage()}TabContent() {VideoPage()}TabContent() {MessagePage()}TabContent() {MinePage()}}.barHeight(CommonConstant.TAB_BAR_HEIGHT).scrollable(false).layoutWeight(CommonConstant.WEIGHT_1).onChange((index) => {this.currentTabIndex = index})this.BottomTabView()}}.hideTitleBar(true).padding({bottom:DisplayUtil.getBottomRectHeight(this.getUIContext())})}
可以看到我们一共创建了5个TabContent(),每个里面就是对应Tab承载的内容区域了,简单理解可以称它为页面,实际上它们都是自定义组件,比如,这里第一个TabContent()里面我们创建了HomePage()组件,依此类推我们又创建了余下的四个页面。
下面来看一下HomePage()里面都有些什么吧?由于目前我们只是为了实现页面的整体结构,所以这里咱们就简单放置了一个文本进行展示,代码如下:
@Component
export struct HomePage {build() {Column() {StatusBarComponent()Column() {Text($r('app.string.tab_index')).fontSize($r('app.float.fp_20')).fontColor($r('app.color.color_primary')).fontWeight(FontWeight.Bold)}.layoutWeight(CommonConstant.WEIGHT_1).justifyContent(FlexAlign.Center)}.width(CommonConstant.FULL_PERCENT).height(CommonConstant.FULL_PERCENT)}
}
细心的大家会发现这不是还有一个StatusBarComponent()吗?它是我们自定义的一个状态栏横条,里面也很简单,只有一个跟顶部状态栏高度一致的View,代码如下:
@Component
export struct StatusBarComponent {build() {Blank().height(DisplayUtil.getTopRectHeight(this.getUIContext())).backgroundColor($r('app.color.color_primary'))}
}
通过上面的步骤我们已经完成了首页整体结构的搭建。
3、实现效果
下面,让我们一起来看一下最终的实现效果吧:
主页面结构录屏
OK,今天的内容就这么多啦,下期再会!
祝:工作顺利,生活愉快!