头图
摘要:
卡片导航(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)
  }
}

华哥的全栈次元舱
1 声望0 粉丝