鸿蒙应用开发之“一次开发,多端部署”

一、开发工具

DevEco Studio

知识点:

sm md lg
页签在底部 页签的图标和文字垂直布局 页签宽度均分 页签高度固定72vp 页签在底部 页签的图标和文字水平布局 页签宽度均分 页签高度固定56vp 页签在左边 页签的图标和文字垂直布局 页签宽度固定96vp 页签高度总占比'60%'后均分

二、开发步骤

创建项目

层级目录:

首页MainPage介绍:

首先创建布局:

scss 复制代码
Tabs({ barPosition: BarPosition.End }) {
        TabContent() {

          WeatherDetails()

        }.tabBar(this.tabBuilder('天气', 0))
        .backgroundColor('#9698CE')

        TabContent() {
          Mine()

        }.tabBar(this.tabBuilder('个人', 2))
        .backgroundColor('#9698CE')

      }
      .animationDuration(0)
      .onChange((index: number) => {
        this.currentIndex = index
      })
复制代码
使用Tabs分页导航,目前主界面最流行的布局和导航风格。

Tabs的底部tabBar使用@Builder修饰符创建自定义Bar:
scss 复制代码
@Builder tabBuilder(title: string, targetIndex: number) {
    Column() {
      Text(title)
        .fontColor(this.currentIndex === targetIndex ? '#1698CE' : '#6B6B6B')
    }
  }

然后在TabContent里面加入自定义界面:

复制代码
WeatherDetails()   //天气布局
复制代码
Mine()             //个人设置布局

天气布局(不同设备的展示)

使用GridRow布局不同设备不同排列展示:

php 复制代码
GridRow({
                columns: { sm: 4, md: 8, lg: this.showSideBar ? 8 : 12 },
                gutter: 10 ,
                breakpoints: { reference: BreakpointsReference.WindowSize } }) {
                // 天气概览
                GridCol({ span: { sm: 4, md: 8, lg: this.showSideBar ? 8 : 12 }, order: 1 }) {
                  WeatherState({weatherData:this.weatherData})
                    .opacity(this.headerOpacity)
                }
                // 每小时天气
                GridCol({ span: { sm: 4, md: 8, lg: 8 }, order: 2 }) {
                  HoursWeather().borderRadius(8)
                    .margin({left:10,right:10})
                    .backgroundColor('#55000000')
                }
                // 每日天气
                GridCol({ span: 4, order: {sm: 3, md: 3, lg: this.showSideBar ? 3 : 4} }) {
                  DayWeather().borderRadius(8)
                    .margin({left:10,right:10})
                    .backgroundColor('#55000000')
                }
                // 空气质量
                GridCol({ span: 4, order: {sm: 4, md: 4, lg: this.showSideBar ? 4 : 3} }) {
                  AirQuality().borderRadius(8)
                    .margin({left:10,right:10})
                    .backgroundColor('#55000000')
                }
                // 生活指数
                GridCol({ span: 4, order: 5 }) {
                  LifeIndex().borderRadius(8)
                    .margin({left:10,right:10})
                    .backgroundColor('#55000000')
                }
                // 日出日落
                GridCol({ span: 4, order: 6 }) {
                  SunCanvas()
                }
                // 应用信息
                GridCol({ span: { sm: 4, md: 8, lg: this.showSideBar ? 8 : 12 }, order: 7 }) {
                  WeatherEnd()
                }
              }

侧边栏使用SideBarContainer组件展示:

kotlin 复制代码
SideBarContainer(SideBarContainerType.Embed) {
        // 左侧侧边栏
        WeatherSide({ showSideBar:this.showSideBar,weatherDatas:this.weatherDatas, weatherData:this.weatherData})
        // 右侧内容区
        Flex({direction: FlexDirection.Column}) {
          // 基础区域1标题栏
          
        }

      }
      .height('100%')
      .sideBarWidth('33.3%')
      // 通过状态变量,控制不同设备下侧边栏的显隐状态
      .showSideBar($$this.showSideBar)

个人设置布局(不同设备的展示)

手机 1图 点击进入2图 手机 2图图

大屏设别图直接左右显示

使用Navigation导航不同设备展示不同界面:

scss 复制代码
Navigation(this.pagemodels){
      Column() {
        Image($r('app.media.ic_user_profile'))
          .width(100)
          .height(100)
          .objectFit(ImageFit.Contain)
          .margin({ top: 20 })

        Text('用户')
          .textAlign(TextAlign.Center)
          .fontWeight(500)
          .fontSize(15)
          .margin({ top: 20 })

        Text('积分:9999')
          .textAlign(TextAlign.Center)
          .fontWeight(500)
          .fontSize(15)
          .margin({ top: 20 })
        
        List(){
          ForEach(this.arr,(item:string,index:number)=>{
            ListItem(){
              Item({label: item,pageId:1, imageSrc: ''})
                //.background(this.setPageID===1 ? this.clickedBg : this.unClickedBg)
                .backgroundColor(this.setPageID===index&&this.isLandscape? '#0000FF' : '')
                .margin({left:10,top:10})
                .borderRadius(8)
                .onClick(()=>{
                  if (this.setPageID!==index) {
                    if (index == 3) {
                      router.replaceUrl({
                        url:'pages/LoginPage'
                      })

                    }else {
                      this.pagemodels.pushPath({ name: index.toString()})
                      this.setPageID = index;
                    }
                  }
                })
            }
          })
        }
      }
      .justifyContent(FlexAlign.Center)
      .width('100%')
      .height('80%')

    }
    .mode(NavigationMode.Auto)
    .navDestination(this.PageMap)
    .height('100%')
    .width('100%')
    .navBarWidth(250)
    .hideToolBar(true)

根据系统能力提前判断是什么设备:

ini 复制代码
aboutToAppear(): void {

    this.setPageID = -1;

    let deviceTypeInfo: string = deviceInfo.deviceType;

    let data: display.FoldStatus = display.getFoldStatus();
    console.info('Succeeded in obtaining fold status. Data: ' + JSON.stringify(data));

    let FoldStatus =  JSON.stringify(data)

    if (deviceTypeInfo !=='phone'||FoldStatus==='1') {
      this.pagemodels.pushPath({ name: '0'})
      this.setPageID = 0;
      this.isLandscape = true
    }

    display.on('foldDisplayModeChange', (data: display.FoldDisplayMode) => {
      let displayInfo: display.Display = display.getDefaultDisplaySync();
      if (data === display.FoldDisplayMode.FOLD_DISPLAY_MODE_FULL) {
        console.info('当前屏幕状态:全屏显示');
        this.pagemodels.pushPath({ name: '0'})
        this.setPageID = 0;
        this.isLandscape = true
      } else if (data === display.FoldDisplayMode.FOLD_DISPLAY_MODE_MAIN) {
        console.info('当前屏幕状态:主屏幕显示');
        this.isLandscape = false
        this.setPageID = -1;
      }
    });

  }

然后在.navDestination(this.PageMap)中自定义子布局:

kotlin 复制代码
@Builder
  PageMap(name: string) {
    if (name === "0") {
      CityDialog({setPageID:this.setPageID,isLandscape:this.isLandscape})
    }else if (name === "1") {
      EffectSetDialog({setPageID:this.setPageID,isLandscape:this.isLandscape})
    }else if (name === "2") {
      PrivacyDialog({setPageID:this.setPageID,isLandscape:this.isLandscape})
    }
  }

子布局里面用NavDestination()套住,界面才能正常显示:

scss 复制代码
NavDestination() {
      Column() {
        Row(){
          Text('退出').visibility(this.isLandscape?Visibility.None:Visibility.Visible)
            .onClick(()=>{
              this.pagemodels.clear()
            })
        }.width('100%')

        Text('城市选择').fontSize(20)

        Scroll(this.scroller) {
          Column() {
            ForEach(this.arr, (item?:string|undefined) => {
              if(item){
                Text(item.toString())
                  .width('90%')
                  .height(50)
                  .backgroundColor(0xFFFFFF)
                  .borderRadius(5)
                  .fontSize(16)
                  .textAlign(TextAlign.Center)
                  .margin({ top: 10 })
                  .onClick(()=>{

                  })
              }
            }, (item:string) => item.toString())
          }.width('100%')
          .margin({ bottom: 10 })
        }
        .height('80%')
        .backgroundColor(0xDCDCDC)
        .scrollable(ScrollDirection.Vertical) // 滚动方向为垂直方向
        .scrollBar(BarState.On) // 滚动条常驻显示
        .scrollBarColor(Color.Gray) // 滚动条颜色
        .scrollBarWidth(0) // 滚动条宽度
        .edgeEffect(EdgeEffect.Spring) // 滚动到边沿后回弹
      }.width('100%')
      .height('100%')
    }.hideTitleBar(true)

避坑指南:(这里有一个坑要特别提醒)

用了Navigation之后页面的生命就需要在Navigation的子界面里面监听:

比如onBackPressed()必须在NavDestination()下监听才有回调,如下图代码:

javascript 复制代码
NavDestination() {
      
    }.hideTitleBar(true)
    .onBackPressed(() => {
      // 获取当前时间戳
      let now = Date.now();
      // 判断两次点击返回按钮的时间间隔是否大于2秒
      if (now - this.firstBackTimestamp > 2000) {
        // 提示用户再次点击将退出应用
        promptAction.showToast({
          message: '再按一次退出应用',
          duration: 2000
        })
        // 更新第一次点击的时间戳
        this.firstBackTimestamp = now;
      }else {
        new process.ProcessManager().exit(0)
      }
      // 返回 true 表示页面自己处理返回逻辑
      return true;

    })

运行效果:

最后贴上Demo源码:gitee.com/stevenllv/M...

相关推荐
安妮的心动录5 小时前
安妮的2025 Q1 Review
程序员
摆烂工程师7 小时前
保姆教程:2025年 ChatGPT Plus 的订阅升级攻略和支付失败的解决方式
前端·后端·程序员
京东零售技术10 小时前
京东零售技术专家亮相多场峰会,诚邀伙伴们共探前沿!
程序员
Captaincc12 小时前
程序员究竟怎样用 AI?深度解读 WIRED 调查报告《How Software Engineers Actually Use AI》
程序员·ai编程
这里有鱼汤15 小时前
Python逆天改命,3分钟做出高颜值GUI程序
后端·python·程序员
摆烂工程师16 小时前
炸裂了~兄弟们,GPT4o出图效果太好了
前端·后端·程序员
马可奥勒留17 小时前
我的管理日记(1)
程序员
Goboy19 小时前
我曾经被腾讯云一条龙服务过
后端·程序员·架构
Goboy1 天前
腾讯混元T1正式版发布
后端·程序员·架构
郭顺发2 天前
记录一个模板项目:微信支付Java
程序员