摘要:
卡片导航(A_CardNav):可设置导航数据(含文本、图标、路由),可设置为透明模式(适用于背景容器设置了颜色的情形),学习在鸿蒙中使用Material Design Icons图标库。
一、组件调用方式
1.非透明模式
卡片导航组件的调用非常简单,只需要输入A_CardNav ,然后给属性 data (导航数据)赋值即可。在 data 当中需要给文字、图标和跳转的路由赋值,如下图所示:
页面调用代码如下:
import { A_TitleBar } from '../aui/basic/A_TitleBar'
import { A_CardNav } from '../aui/navigation/A_CardNav'
import { ColorConstants } from '../constants/ColorConstants'
import { FloatConstants } from '../constants/FloatConstants'
import { GirdConstants } from '../constants/GirdConstants'
import { WindowUtils } from '../utils/WindowUtils'
@Component
export struct CardNav {
@StorageLink('pageInfo') pageInfo: NavPathStack = new NavPathStack()
@StorageLink('primaryColor') primaryColor: ResourceStr = ColorConstants.INFO
@StorageLink('deviceType') deviceType: string = GirdConstants.DEVICE_SM
aboutToAppear() {
WindowUtils.getInstance()?.setFullScreen()
}
build() {
Row() {
Column() {
// 标题栏
A_TitleBar({ text: '调用示例一' })
// 下拉容器
Scroll() {
GridRow({
columns: { sm: GirdConstants.GRID_COLUMNS[0], md: GirdConstants.GRID_COLUMNS[1], lg: GirdConstants.GRID_COLUMNS[2] },
gutter: { x: GirdConstants.GRID_GUTTER, y: GirdConstants.GRID_GUTTER },
breakpoints: { reference: BreakpointsReference.WindowSize } }) {
GridCol({ span: 12 }) {
A_CardNav({
data: [
{ text: 'Java', icon: 'mdi-language-java', router: '' },
{ text: 'Python', icon: 'mdi-language-python', router: '' },
{ text: 'Vuetify', icon: 'cast-variant', router: 'OrderDetail/2' },
{ text: '纯血鸿蒙', icon: 'mdi-tablet-cellphone', router: 'AccountPage' }
]
})
}
.margin({
top: FloatConstants.SPACE_TOP
})
}
.padding({ bottom:this.deviceType === GirdConstants.DEVICE_LG ? GirdConstants.ZERO : FloatConstants.SPACE_S })
}
.scrollBar(BarState.Off)
}
.height(GirdConstants.FULL_PERCENT)
}
.alignItems(VerticalAlign.Top)
.padding({
top: FloatConstants.SPACE_TOP,
left:FloatConstants.SPACE_LEFT,
right:FloatConstants.SPACE_RIGHT,
bottom:this.deviceType === GirdConstants.DEVICE_LG ? GirdConstants.GRID_BOTTOM : FloatConstants.SPACE_BOTTOM
})
.width(GirdConstants.FULL_PERCENT)
.backgroundColor(ColorConstants.APP_BG)
}
}
本地模拟器下浅色模式和深色模式运行效果如下:
2.透明模式
卡片导航组件放在一个有颜色背景上面,此时,无论深色模式还是浅色模式,文字和图标均始终显示为白色。只需要增加一个属性 alphaMode: true 即可,如下图所示:
二、在线排版
在会员页面或管理后台的页面当中选择一个页面,比如“首页”,然后点击“页面设计”。如下图所示:
现在“首页”是一个空页面,还没有任何组件。展开左侧组件库中的“导航组件”分组,将“卡片导航组件”拖拽到页面中,如下图所示:
默认会有一个初始化的配置数据,在页面设计预览器下平板、折叠屏和手机的浅色模式的效果如下:
切换到深色模式,效果如下:
选择右侧“组件设置”下的“卡片导航”组件,点击“配置数据”按钮,可以根据自己的业务需要,新增、删除或修改每一项配置,如下图所示:
可以设置导航文字和导航的图标,其中导航图标支持使用 mdi 图标库,可点击“图标库”图标,进入 Material Design Icons 官方的图标库,使用这里的图标库,将来生成纯血鸿蒙代码或者Vuetify3网页端代码的时候,都是兼容的。如下图所示:
搜索或选择需要的图标,在弹出的窗口中拷贝图标名称,如下图所示:
然后在导航图标这个地方保留“mdi-”的前缀,粘贴新的图标名称,就能应用新的图标,如下图所示:
路由这个地方我们可以保持为空(这个时候,点击的时候不会跳转页面);但是,如果路由不为空,那么我们一定要绑定一个真实存在的页面,如下图所示,绑定“发现”页,配置好数据后,点击“确认”按钮:
现在,切换到代码魔法页面,我们可以在侧栏菜单这里选择“纯血鸿蒙”,(可以切换为深色模式,看代码更舒适),然后点击“生成代码”,如下图所示:
在生成的鸿蒙项目中,展开特性层(features),选择 home 模块,找到 /src/main/ets/view 目录下的 HomeView.ets 页面,查看生成的代码,如下图所示:
如果希望卡片导航组件运行在有特殊颜色的背景容器之上,可以使用透明模式。现在我们再次进入到页面设计预览器页面,将透明模式的选项改为“是”,点击“保存设置”,如下图所示:
再次生成鸿蒙代码,发现 A_CardNav 组件里增加了一个“alphaMode: true”的属性,效果如下:
三、源码解析
卡片导航组件支持两个属性,一个是 data(导航数据),另外一个是 alphaMode(是否是透明模式,默认值是false):
卡片导航数据 data 是一个数组 Array<NavigationModel>,其中导航Model的数据结构如下,含三个字段:导航的文字(text)、导航图标(icon)和跳转页面的路由(router)。
继续分析卡片导航组件 A_CardNav 的源码,在 aboutToAppear() 这个生命周期当中,对设备为折叠屏、平板和手机时的 padding、margin和行高做了差异化的处理,以实现“一多”适配,如下图所示:
继续分析卡片导航组件 A_CardNav 的源码,在 aboutToAppear() 这个生命周期当中,对设备为折叠屏、平板和手机时的 padding、margin和行高做了差异化的处理,以实现“一多”适配,如下图所示:
this.compData.forEach(element => {
element.icon = 'app.media.' + (element.icon.startsWith('mdi-') || element.icon.startsWith('mdi_') ? '' : 'mdi_') + element.icon.replaceAll('-', '_')
})
在build函数中,通过 ForEach 循环遍历导航数据。当设备为手机的类型时,图标和文字是上下排列(手机屏幕宽度有限,上下排列比较合理);当设备为折叠屏或平板的时候,图标和文字是左右排列。如下图所示:
当设备为手机时(this.deviceType === GirdConstants.DEVICE_SM),使用列组件(Column)包裹图标和文字,实现上下排列的效果:
点击跳转路由的代码如下:
.onClick(() => {
if (item.router) {
// 跳转到新页面
const router = item.router
if (router.includes("/")) {
this.pageInfo.pushPathByName(router.substring(0, router.indexOf("/")),
router.substring(router.indexOf("/") + 1))
} else {
this.pageInfo.pushPathByName(router, null)
}
}
})
当设备为折叠屏或平板的时候,图标和文字通过行组件(Row)包裹,实现左右排列的效果,如下图所示:
卡片导航组件的完整源码如下:
/*
* Copyright (c) 2024 AIGCoder.com(AI极客)
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
调用示例一:
A_CardNav({
data: [
{ text: 'Java', icon: 'mdi-language-java', router: '' },
{ text: 'Python', icon: 'mdi-language-python', router: '' },
{ text: 'Vuetify', icon: 'cast-variant', router: 'OrderDetail/2' },
{ text: '纯血鸿蒙', icon: 'mdi-tablet-cellphone', router: 'AccountPage' }
]
})
调用示例二:透明模式
A_CardNav({
data: [
{ text: 'Java', icon: 'mdi-language-java', router: '' },
{ text: 'Python', icon: 'mdi-language-python', router: '' },
{ text: 'Vuetify', icon: 'cast-variant', router: 'OrderDetail/2' },
{ text: '纯血鸿蒙', icon: 'mdi-tablet-cellphone', router: 'AccountPage' }
],
alphaMode: true
})
*/
import { ColorConstants } from "../../constants/ColorConstants"
import { FloatConstants } from "../../constants/FloatConstants"
import { GirdConstants } from "../../constants/GirdConstants"
import { NavigationModel } from "../../models/NavigationModel"
/**
* 【卡片导航】
* data:导航数据
* alphaMode:透明模式(适用于背景容器设置了颜色的情形)
*/
@Component
export struct A_CardNav {
@Prop data: Array<NavigationModel>
@Prop alphaMode?: boolean = false
@StorageLink('pageInfo') pageInfo: NavPathStack = new NavPathStack()
@StorageLink('deviceType') deviceType: string = GirdConstants.DEVICE_SM
@State compPadding: Length = '0vp'
@State compMargin: Length = '12vp'
@State compRowHeight: Length = '56vp'
private compData: Array<NavigationModel> = this.data
aboutToAppear(): void {
switch (this.deviceType){
case GirdConstants.DEVICE_MD:
this.compPadding = '21vp'
this.compMargin = '12vp'
this.compRowHeight = '56vp'
break
case GirdConstants.DEVICE_LG:
this.compPadding = '29vp'
this.compMargin = '30vp'
this.compRowHeight = '80vp'
break
case GirdConstants.DEVICE_SM:
default:
this.compPadding = '0vp'
break
}
this.compData.forEach(element => {
element.icon = 'app.media.' + (element.icon.startsWith('mdi-') || element.icon.startsWith('mdi_') ? '' : 'mdi_') + element.icon.replaceAll('-', '_')
})
}
build() {
Row() {
ForEach(this.compData, (item: NavigationModel) => {
if (this.deviceType === GirdConstants.DEVICE_SM) {//手机:上下排列
Column({ space: GirdConstants.EIGHT }) {
Row() {
Image($r(item.icon))
.width(FloatConstants.IMAGE_SIZE6)
.height(FloatConstants.IMAGE_SIZE6)
.fillColor(this.alphaMode ? Color.White : ColorConstants.FG_LEVEL1)
}
.width(FloatConstants.IMAGE_SIZE10)
.height(FloatConstants.IMAGE_SIZE10)
.alignSelf(ItemAlign.Center)
.justifyContent(FlexAlign.Center)
.borderRadius(FloatConstants.IMAGE_SIZE10)
.backgroundColor(ColorConstants.CONTROL_BG_ALPHA)
Text(item.text)
.fontSize(FloatConstants.FONT_SIZE_BODY_L)
.fontColor(this.alphaMode ? Color.White : ColorConstants.FG_LEVEL1)
}
.onClick(() => {
if (item.router) {
// 跳转到新页面
const router = item.router
if (router.includes("/")) {
this.pageInfo.pushPathByName(router.substring(0, router.indexOf("/")),
router.substring(router.indexOf("/") + 1))
} else {
this.pageInfo.pushPathByName(router, null)
}
}
})
} else {// 平板:左右排列
Row() {
Image($r(item.icon))
.width(FloatConstants.IMAGE_SIZE9)
.height(FloatConstants.IMAGE_SIZE9)
.fillColor(this.alphaMode ? Color.White : ColorConstants.FG_LEVEL1)
Text(item.text)
.fontSize(FloatConstants.FONT_SIZE_BODY_L)
.fontColor(this.alphaMode ? Color.White : ColorConstants.FG_LEVEL1)
.margin({
left: this.compMargin
})
}
.width(GirdConstants.TWENTY_PERCENT)
.height(this.compRowHeight)
.alignSelf(ItemAlign.Center)
.justifyContent(FlexAlign.Center)
.borderRadius(FloatConstants.RADIUS_CARD)
.backgroundColor(ColorConstants.CONTROL_BG_ALPHA)
.onClick(() => {
if (item.router) {
// 跳转到新页面
const router = item.router
if (router.includes("/")) {
this.pageInfo.pushPathByName(router.substring(0, router.indexOf("/")),
router.substring(router.indexOf("/") + 1))
} else {
this.pageInfo.pushPathByName(router, null)
}
}
})
}
}, (item: NavigationModel, index: number) => index + JSON.stringify(item))
}
.justifyContent(FlexAlign.SpaceBetween)
.padding({
left: this.compPadding,
right: this.compPadding
})
.width(GirdConstants.FULL_PERCENT)
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。