1

2.6 ArkUI实现一次开发多端部署-开源基础软件社区-51CTO.COM

 3 months ago
source link: https://ost.51cto.com/posts/12710
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

2.6 ArkUI实现一次开发多端部署 原创 精华

本节通过栅格化布局、自适应布局、响应式布局和使用资源,从App的弹性布局和多态组件两个维度,讲解如何实现一次开发多端部署。接着,建立一个ArkUI eTS的开发框架,这个可以作为开发新App的脚手架。

当显示环境发生变化时(如,不同屏幕尺寸的设备切换、横竖屏切换、应用分屏),我们需要及时调整内容的布局方式以适应变化。通过栅格化布局、自适应布局和响应式布局,可以达到多设备下布局的一致性。

2.6.1 栅格化布局

1. 8vp网格系统

综合权衡各类设备的屏幕特征,基于 8vp 为网格的基本单位可以对界面上元素的大小、位置、对齐方式进行更好的规划,构建更有层次感、秩序感,以及多设备上一致的布局效果。一些更小的控件(例如图标)大小也可以对齐 4vp 的网格大小。栅格化布局中的Margin和Gutter的值参考8vp/4vp网格系统设置。如,手机设备上的栅格化设计,基础栅格Margin为24vp,等于3个8vp的宽度;卡片栅格Margin为12vp,等于一个8vp加上一个4vp的宽度。

2.计算栅格的宽度

栅格的宽度在不同设备、不同环境下是动态计算的,并非固定值。与栅格宽度计算相关的三个关键参数:

Margin:栅格布局内容区距离屏幕左、右边缘的间隔。

Gutter:相邻栅格之间的间隔。

栅格数:设备屏幕宽度内可容纳的栅格数量。不同屏幕宽度下的栅格数量有一个约定。

以竖屏手机的栅格宽度计算为例:

屏幕宽度:360vp

Margin: 24vp

Gutter: 24vp

栅格数: 4个(含3个Gutter)

计算公式如下:

1个栅格的宽度 = (屏幕宽度 - Margin x 2 - Gutter x 3)/4 = (360 - 24 x 2 - 24 x 3)/4 = 60(vp)

2个栅格的宽度 = 1个栅格的宽度 + Gutter + 1个栅格的宽度 = 60 + 24 + 60 = 144(vp)

3个栅格的宽度 = 2个栅格的宽度 + Gutter + 1个栅格的宽度 = 144 + 24 + 60 = 228(vp)

4个栅格的宽度 = 3个栅格的宽度 + Gutter + 1个栅格的宽度 = 228 + 24 + 60 = 312(vp)

各类设备的栅格数、Margin、Gutter的参数,如下表所示:

参数项 手机 折叠屏 平板 车机 智慧屏
竖屏栅格数(个) 4 8 8 - -
横屏栅格数(个) 8 8 12 12 12
基础栅格Margin(vp) 24 24 24 24 48
基础栅格Gutter(vp) 24 24 24 24 24
卡片栅格Margin(vp) 12 12 12 12 48
卡片栅格Gutter(vp) 12 12 12 12 24
3. 栅格布局组件

栅格化布局组件,主要使用GridContainer布局组件、Gird布局组件和GirdItem组件,本节暂不讲解这三个组件的用法,《第4章 布局组件》中会有详细讲解。

4. 栅格断点布局

根据设备实际的屏幕宽度范围,约定栅格的数量,称为栅格断点布局。屏幕宽度小于520vp,栅格数为4;屏幕宽度大于等于520vp,且小于840vp,栅格数为8;屏幕宽度大于等于840vp,栅格数为12。

注意:由于栅格的宽度是基于设备屏幕宽度,根据公式计算而来,在不同设备上宽度存在一定的差异。为了消除这个差异导致的布局效果变差,建议栅格内的子组件排版使用自适应布局的技巧。

2.6.2 自适应布局

1. 自适应拉伸

自适应拉伸是通过设置组件与父级容器的相对比例来实现的。比如,在设计稿上,竖屏手机的宽度是“360vp”,折叠屏的宽度是“600vp”。那么,在布局组件的宽度设置上,不要使用固定值“360vp”或“600vp”,而是用“100%”这种相对值。示例代码如下:

@Entry
@Component
struct Index {
  build() {
    Column({space:8}){ // 根容器(本例使用垂直布局容器作为根容器)
      Row() { // 水平布局容器
        Text('鸿蒙开发ArkUI最佳实践')
          .fontSize(20)
      }
      .width('100%') // 相对于父级容器Column的宽度(占满)
      .padding(10)
      .backgroundColor('#FFFFFF')
    }
    .width('100%') // 相对于手机屏幕的宽度(占满)
    .height('100%') // 相对于手机屏幕的高度(占满)
    .padding({top:48,bottom:24,left:24,right:24}) // 屏幕内边距
    .backgroundColor('#F1F3F5')
  }
}

在上述代码中,根容器“Column”的宽度和高度要占满整个手机屏幕,使用“100%”这个相对值实现。在根容器内部放了一个水平布局容器“Row”,相对于根容器“Column”的宽度也是“100%”。同时,因为根容器“Column”设置了屏幕内边距"padding",其中左边距和右边距都是“24vp”,所以,实际上“Row”容器的宽度在竖屏手机的值为“312vp”(360 - 24 -24 = 312),而在折叠屏的实际宽度为“552vp”(600 -24 -24 = 552)。效果如下图所示:

2.6 ArkUI实现一次开发多端部署-开源基础软件社区

注意:这个设置也能同时适配“平板/车机/智慧屏/智能穿戴”等设备,并且切换为横屏时也是适配的。

接下来,演示自适应拉伸布局下设置子组件大小和位置的两个技巧。

第一个技巧:子组件不刻意设置宽度或设置绝对宽度值,子组件间使用“Blank”组件填充,使Text组件和Button组件贴近左、右边缘。在前面代码的基础上加入如下代码:

      Row() {
        Text('鸿蒙开发ArkUI最佳实践')
          .fontSize(20)
        Blank() // 使Text组件和Button组件贴近左、右边缘
        Button('订阅')
      }
      .width('100%')
      .padding(10)
      .backgroundColor('#FFFFFF')

效果如下图所示:

2.6 ArkUI实现一次开发多端部署-开源基础软件社区

后面章节讲解“Flex”布局组件时,还会介绍使用“justifyContent”实现上述效果。

第二个技巧:子组件不设置宽度,但通过“layoutWeight”设置该组件在父级容器中的宽度权重比例。在前面代码的基础上加入如下代码:

      Row() {
        Column().layoutWeight(1).height('100%').backgroundColor('#564AF7')
        Column().layoutWeight(2).height('100%').backgroundColor('#46B1E3')
        Column().layoutWeight(1).height('100%').backgroundColor('#564AF7')
      }
      .width('100%')
      .height(100)

"Row"布局组件下三个子组件"Column"通过“1:2:1”的比例瓜分了父级容器的宽度。效果如下图所示:

2.6 ArkUI实现一次开发多端部署-开源基础软件社区

注意:"layoutWeight"仅适用于“Flex/Row/Column”布局组件下的子组件。

2. 自适应缩放

对于图片的展示,可以锁定宽高比例,同时将宽设置为百分比的值,实现自适应缩放,示例代码如下:

Image('/common/images/cover.png')
        .width('100%')
        .aspectRatio(1.5) // 指定当前组件的宽高比

效果如下图:

2.6 ArkUI实现一次开发多端部署-开源基础软件社区

注意:上述锁定图片的宽高比时建议使用“.aspectRatio(1.5)”这个属性,其中宽高比值“1.5”要根据具体图片的实际宽高比确定。暂时不要使用“.objectFit(ImageFit.Contain)”这个属性来保持图片组件的宽高比。经测试,在本场景下会在图片组件下产生不可控的间距。可能是一个Bug,希望ArkUI eTS正式版时鸿蒙官方能修复这个问题。代码如下:

      Image('/common/images/cover.png')
        .width('100%')
        .objectFit(ImageFit.Contain) // 保持宽高比进行缩小或者放大,使得图片完全显示在显示边界内

效果如下图所示:

2.6 ArkUI实现一次开发多端部署-开源基础软件社区

注意:为了图片在缩放状态下都显示清晰,图片尺寸优先考虑在较大屏幕时能显示清晰。避免小屏清晰,大屏马赛克。

3. 自适应延伸

自适应延伸的要点在于不设置父级容器宽度,由子组件将父容器撑开。当不同设备的屏幕宽度发生变化时,组件随之发生自适应延伸显示更多数量。示例代码如下:

 Row(){
        Column().width(120).height('100%').backgroundColor('#564AF7')
        Column().width(120).height('100%').backgroundColor('#46B1E3')
        Column().width(120).height('100%').backgroundColor('#564AF7')
        Column().width(120).height('100%').backgroundColor('#46B1E3')
        Column().width(120).height('100%').backgroundColor('#564AF7')
        Column().width(120).height('100%').backgroundColor('#46B1E3')
        Column().width(120).height('100%').backgroundColor('#564AF7')
      }.height(40) // 父级容器不设置宽度,被子组件撑开

效果如下图所示:

2.6 ArkUI实现一次开发多端部署-开源基础软件社区

实际项目中,我们会配合“Scroll” 可滚动容器组件,实现被遮挡子组件的显示。本页面的代码在横屏状态下,会有部分内容被遮挡,也可以通过"Scroll"纵向滚动解决。“Scroll”组件的用法在后面章节演示,非本节讲解重点,暂时不实现。

本小节代码位于“index.ets”页面,完整代码如下:

@Entry
@Component
struct Index {
  build() {
    Column({space:8}){ // 根容器(本例使用垂直布局容器作为根容器)
      Row() { // 水平布局容器
        Text('鸿蒙开发ArkUI最佳实践')
          .fontSize(20)
      }
      .width('100%') // 相对于父级容器Column的宽度(占满)
      .padding(10)
      .backgroundColor('#FFFFFF')

      Row() {
        Text('鸿蒙开发ArkUI最佳实践')
          .fontSize(20)
        Blank() // 使Text组件和Button组件贴近左、右边缘
        Button('订阅')
      }
      .width('100%')
      .padding(10)
      .backgroundColor('#FFFFFF')

      Row() {
        Column().layoutWeight(1).height('100%').backgroundColor('#564AF7')
        Column().layoutWeight(2).height('100%').backgroundColor('#46B1E3')
        Column().layoutWeight(1).height('100%').backgroundColor('#564AF7')
      }
      .width('100%')
      .height(100)

      Image('/common/images/cover.png')
        .width('100%')
        .aspectRatio(1.5) // 指定当前组件的宽高比

      Row(){
        Column().width(120).height('100%').backgroundColor('#564AF7')
        Column().width(120).height('100%').backgroundColor('#46B1E3')
        Column().width(120).height('100%').backgroundColor('#564AF7')
        Column().width(120).height('100%').backgroundColor('#46B1E3')
        Column().width(120).height('100%').backgroundColor('#564AF7')
        Column().width(120).height('100%').backgroundColor('#46B1E3')
        Column().width(120).height('100%').backgroundColor('#564AF7')
      }.height(40) // 父级容器不设置宽度,被子组件撑开
    }
    .width('100%') // 相对于手机屏幕的宽度(占满)
    .height('100%') // 相对于手机屏幕的高度(占满)
    .padding({top:48,bottom:24,left:24,right:24}) // 屏幕内边距
    .backgroundColor('#F1F3F5')
  }
}

2.6.3 响应式布局

当基本的自适应布局无法满足多终端上屏幕的体验要求时,我们需要针对不同终端的屏幕特点进行响应式的布局。常见的响应式布局样式有:分栏布局、重复布局、挪移布局和缩进布局。

1. 分栏布局

利用屏幕的宽度优势,将有上下级层级的界面同时左右显示。 比如,新闻列表页和新闻详情页。在手机上,屏幕宽度有限,那么在新闻列表页点击某条新闻后,页面跳转到新页面展示新闻详情。但是在宽屏幕设备上,可以不用跳转页面,直接在左侧展示新闻列表,在屏幕右侧展示新闻详情。完成这个案例演示,需要如下代码。初学的同学如果有一些代码看不懂,可以先不用纠结,后面章节会讲解,这里先关注分栏布局的效果:

1) 新闻列表页(/pages/news/list.ets)
/**
 * 新闻列表
 */
import {NewsDetail} from './detail'
import {NewsData, getDefaultNews, getNewsList} from '../../model/NewsDataModel'
import router from '@system.router'
import mediaquery from '@system.mediaquery'
let portraitFunc = null

@Entry
@Component
struct Index {
  @Provide newsData: NewsData = getDefaultNews()
  @Provide isLandscape: boolean = false
  orientationListener = mediaquery.matchMediaSync('screen and (1500 < width) and (orientation: landscape)')

  onPortrait(mediaQueryResult) {
    console.log("isLandscape:" + mediaQueryResult.matches);
    this.isLandscape = mediaQueryResult.matches;
  }

  aboutToAppear() {
    portraitFunc = this.onPortrait.bind(this)
    this.orientationListener.on('change', portraitFunc)
  }

  build() {
    Row(){
      Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
        Column(){
          NewsList()
        }
        .width('100%').height('100%').padding({top:40,left: 24, right: 24}).backgroundColor('#F1F3F5')
      }.layoutWeight(2).height('100%')

      if (this.isLandscape && this.newsData.newsId > 0) {
        Column() {
          NewsDetail()
        }.layoutWeight(3)
      }
    }
  }
}

/**
 * 主页新闻列表组件
 */
@Component
export struct NewsList {
  private newsList: NewsData[] = getNewsList()
  @Consume newsData: NewsData
  @Consume isLandscape:boolean

  aboutToAppear() {
    this.newsData = this.newsList[0]
  }

  build() {
    Column() {
      List() {
        ForEach(this.newsList, item => {
          ListItem() {
            Column() {
              Row() {
                Text(item.title)
                  .fontSize(17)
                  .width("55%")
                  .height(80)
                  .maxLines(4)
                  .margin(10)
                Image(item.imgUrl).width("35%").aspectRatio(1.5).margin(10)
              }
              Divider()
                .vertical(false)
                .color("#dbd8db")
                .strokeWidth(1)
            }
            .height(101)
            .width("100%")
            .onClick(() => {
              if (this.isLandscape) {
                // 如果为横屏则切换右侧详情页数据
                this.newsData = item
              } else {
                // 如果为竖屏则跳转到详情页
                router.push(
                  {
                    // 跳转到指定页面
                    uri: 'pages/news/detail',
                    params: {
                      // 跳转传递的参数
                      newsItem: item
                    }
                  })
              }
            })
          }
        }, item => item.newsId.toString())
      }
      .listDirection(Axis.Vertical) // 排列方向
    }
  }
}
2) 新闻详情页(/pages/news/detail.ets)
/**
 * 新闻详情页
 */
import router from '@system.router'
import {NewsData, getDefaultNews} from '../../model/NewsDataModel'

@Entry
@Component
struct Index {
  @Provide newsData: NewsData = getDefaultNews()

  aboutToAppear() {
    if (router.getParams()) {
      this.newsData = router.getParams().newsItem
    }
  }

  build() {
    Column() {
      NewsDetail()
    }
  }
}

/**
 * 新闻详情组件
 */
@Component
export struct NewsDetail {
  @Consume newsData: NewsData
  build() {
    Stack({ alignContent: Alignment.TopStart }){
      Scroll() {
        Column({space:10}) {
          Text(this.newsData.title)
            .fontSize(25)
          Text("reads:" + this.newsData.reads + "  likes:" + this.newsData.likes)
            .fontSize(16)
          Image(this.newsData.imgUrl).width("100%").aspectRatio(1.5)
          Text(this.newsData.content).fontSize(18)
        }.padding({left: 24, right: 24}).alignItems(HorizontalAlign.Start)
      }
    }.width('100%').height('100%').backgroundColor('#F1F3F5').padding({top:48, bottom:24})
  }
}
3) 新闻数据结构及模拟(/model/NewsDataModel.ets)
/**
 * 新闻数据
 * @param newsId 新闻ID
 * @param title 标题
 * @param newsType 新闻类型
 * @param imgUrl 图片
 * @param reads 浏览数
 * @param likes 点赞数
 * @param content 内容
 */
export class NewsData {
  newsId: number;
  title: string;
  newsType: string;
  imgUrl: string;
  reads: string;
  likes: string;
  content: string;

  constructor(newsId: number, title: string, newsType: string, imgUrl: string, reads: string, likes: string, content: string) {
    this.newsId = newsId;
    this.title = title;
    this.newsType = newsType;
    this.imgUrl = imgUrl;
    this.reads = reads;
    this.likes = likes;
    this.content = content;
  }
}

/**
 * 获取默认新闻(第一条新闻数据)
 */
export function getDefaultNews(): NewsData {
  return new NewsData(0, '', '', '', '', '', '')
}

/**
 * 新闻模拟数据
 */
const List: any[] = [
  {
    "newsId": 1,
    "title": "鸿蒙3.0开发ArkUI(eTS)最佳实践",
    "newsType": "Health",
    "imgUrl": "common/images/cover.png",
    "reads": "81",
    "likes": "54",
    "content": "本教程由浅入深阐述了鸿蒙3.0方舟开发框架最新编程语言ArkUI(eTS)的基础知识、设计标准、UI排版技巧、自定义多态组件开发、数据模拟,以及分布式全场景开发和异步编程等高级知识。全书共分为4篇:第一篇为拥抱鸿蒙3.0(第1章第4章),第二篇为鸿蒙开发进阶(第5章第9章),第三篇为鸿蒙开发高级(第10章第15章),第四篇为项目实战(第16章第18章)。书中主要内容包括:鸿蒙3.0真的来了、揭开方舟开发框架ArkUI(eTS)的神秘面纱、基础组件、布局组件、事件与手势、绘制组件、让用户界面生动而流畅、基于TS扩展的声明式开发规范、为ArkUI量身打造的多态组件库HUI、访问远程数据、使用mock模拟数据、全场景开发之分布式、多媒体、资源管理、其他高级技术、分布式新闻系统开发准备、App UI快速开发和运行前后端项目。书中包含大量应用示例,不仅可以学会理论知识还可以灵活应用。书中示例基于DevEco Studio 3.0 Beta2 for HarmonyOS环境开发,读者在学习到ArkUI(eTS)语言知识的同时还可学会方舟开发框架技术。书中通过接近商业的一个实战案例详细阐述了如何使用ArkUI(eTS)开发App,内容完整、步骤清晰,提供了工程化的解决方案。本书可作为方舟开发框架ArkUI(eTS)初学者的入门书籍,也可作为从事鸿蒙3.0App开发的技术人员的开发速查手册及培训机构的参考书籍。"
  },
  {
    "newsId": 2,
    "title": "1.1 鸿蒙3.0 App开发技术选型",
    "newsType": "Health",
    "imgUrl": "common/images/ets1.png",
    "reads": "354",
    "likes": "100",
    "content": "目前HarmonyOS 3.0最新版本为Beta2,主要支持Java UI和ArkUI(方舟开发框架)进行鸿蒙App开发,而ArkUI支持基于JS扩展的类Web开发范式和基于TS扩展的声明式开发范式(即eTS)。鸿蒙开源版本OpenHarmony在2022年3月31日已正式发布3.1 release版,仅支持Javascript和eTS两种方式。本节先基于最简单的Hello World案例,增加一个按钮,点击按钮改变文字内容。直观对比感受下这三种开发方式的差异。"
  },
  {
    "newsId": 3,
    "title": "1.2 DevEco Studio 3.0 Beta2 for HarmonyOS下载与安装",
    "newsType": "Finance",
    "imgUrl": "common/images/ets2.png",
    "reads": "91",
    "likes": "74",
    "content": "目前最新版本为“3.0 Beta2”,在这个版本支持尝鲜ArkUI(eTS)的项目开发。本节演示Windows版本的下载和安装。如上图所示,点击下载链接,弹窗中勾选“我已经阅读并同意HUAWEI DevEco Studio Beta试用协议”,点击“同意”按钮,即可开始下载。安装包有951M,请耐心等待下载完毕。"
  },
  {
    "newsId": 4,
    "title": "1.3 完成开发者认证",
    "newsType": "Finance",
    "imgUrl": "common/images/ets3.png",
    "reads": "82",
    "likes": "66",
    "content": "在基于ArkUI(eTS)开发鸿蒙应用的过程中,DevEco Stuidio为开发者提供了预览器的功能,可以查看应用的UI界面效果。预览器支持布局代码的实时预览,只需要将开发的源代码进行保存,就可以通过预览器实时查看应用/服务运行效果,方便开发者随时调整代码。需要注意的是,由于Windows系统和真机设备的字体库存在差异,可能会出现预览器界面中的字体与真机运行效果的字体存在差异。"
  },
  {
    "newsId": 5,
    "title": "1.4 调试代码、本地预览和远程模拟器",
    "newsType": "Technology",
    "imgUrl": "common/images/ets4.png",
    "reads": "703",
    "likes": "622",
    "content": "点击DevEco Studio的右侧栏“预览器”选项卡,默认预览App在手机上的运行效果。可以如下图箭头所示,点击“多设备预览”按钮,可以选择MateX2(折叠屏)、MatePadPro(平板电脑)和Car(车机),针对性查看在某类型设备上的布局效果。也可以打开“Multi-profile preview”开关,同时显示鸿蒙应用在这四种设备上的布局效果,这在开发多设备弹性布局适配时比较有用。不过,提醒一点,打开这个开关同时预览多设备效果,更新预览结果时系统开销会大一些。所以,刚开始布局时,可以先只展示一种设备的布局效果,等基本完成布局效果后再打开这个开关做多设备适配优化。这样开发效率会更高一些。"
  },
  {
    "newsId": 6,
    "title": "1.5 真机调试与应用发布",
    "newsType": "Technology",
    "imgUrl": "common/images/ets5.png",
    "reads": "354",
    "likes": "100",
    "content": "准备一部华为智能手机,确保已升级到HarmonyOS 2.0。以下是我以华为P40 Pro激活调试模式的过程,大家可以参考。如下图顺序,在手机上依次点击“设置” > 点击“关于手机” > 在版本号上快速连续点击,直到提示“您正处于开发者模式!” > 返回“设置”界面,点击“系统和更新” > 点击“开发人员选项” > 打开USB调试的开关 > 点击“确认”按钮,允许USB调试。"
  },
  {
    "newsId": 7,
    "title": "1.6 总结与回顾",
    "newsType": "Sport",
    "imgUrl": "common/images/ets6.png",
    "reads": "911",
    "likes": "543",
    "content": "俗话说“选择大于努力”,在我们准备努力掌握鸿蒙应用开发能力之前,有必要慎重选择采用的开发语言。在本章第一节,我们直观对比感受了Java UI、Js UI和eTS ArkUI的开发过程,作为一个新崛起的手机操作系统,鸿蒙需要大量开发者共同完善它的生态,作为承前启后的稳妥考量,华为首先支持了适应传统安卓开发习惯的Java UI和适应类Web开发的Js UI,让传统App低成本转换为鸿蒙App,这就是所谓“承前”。而华为最新推出的eTS ArkUI,代码简洁,开发高效,是华为未来主推的鸿蒙App开发语言,这就是“启后”。如果之前已学习过Java UI或Js UI,那么可以在学习eTS ArkUI的过程中对比加深理解;如果是刚刚接触鸿蒙开发的同学,那么,强烈建议直接从eTS ArkUI开始!"
  },
  {
    "newsId": 8,
    "title": "2.1 eTS物种起源",
    "newsType": "Sport",
    "imgUrl": "common/images/ets7.png",
    "reads": "754",
    "likes": "149",
    "content": "JavaScript (简称“JS”) 是一种轻量级解释型的编程语言(代码不进行预编译)。 JavaScript最初受Java启发而开始设计的,目的之一就是“看上去像Java”,因此语法上有类似之处,一些名称和命名规范也借自Java。JavaScript是一种属于网络的高级脚本语言,已经被广泛用于Web应用开发,常用来为网页添加各式各样的动态功能,为用户提供更流畅美观的浏览效果。通常JavaScript脚本是通过嵌入在HTML中来实现自身的功能的。JavaScript也可以用于其他场合,如服务器端编程(Node.js)。"
  },
  {
    "newsId": 9,
    "title": "2.2 基于eTS的ArkUI有什么优势",
    "newsType": "Internet",
    "imgUrl": "common/images/ets8.png",
    "reads": "714",
    "likes": "630",
    "content": "ArkUI采用极简的声明式UI描述界面语法,您只需用几行简单直观的声明式代码,即可完成界面功能, 提升HarmonyOS应用界面开发效率30%。UI开发更接近自然语义的编程方式,让开发者直观地描述UI界面 , 允许开发者以优雅的链式调用语法调用的方式配置UI结构及其属性、事件等。"
  },
  {
    "newsId": 10,
    "title": "2.3 ArkUI App设计规范",
    "newsType": "Internet",
    "imgUrl": "common/images/ets9.png",
    "reads": "854",
    "likes": "388",
    "content": "本节内容不仅适用于鸿蒙UI设计师,也是鸿蒙App开发工程师的必修课。掌握了本节阐述的关键设计规范,为开发出标准、优质的鸿蒙App打下必要的理论基础。本节涉及的很多参数,不用记忆,只要理解、留下印象即可。后面会提供封装好的框架环境,直接调用即可。"
  }
]

/**
 * 新闻列表数据
 */
export function getNewsList(): Array<NewsData> {
  let result: Array<NewsData> = []
  List.forEach(item => {
    result.push(new NewsData(item.newsId, item.title, item.newsType, item.imgUrl, item.reads, item.likes, item.content))
  })
  return result;
}

本案例涉及识别手机屏幕横竖屏,所以,需要在远程模拟器体验真实效果。最终,本案例在手机竖屏下的效果如下图所示:

2.6 ArkUI实现一次开发多端部署-开源基础软件社区

在手机横屏模式、折叠屏、平板和车机下,新闻列表和新闻详情同时显示。效果如下图所示:

2.6 ArkUI实现一次开发多端部署-开源基础软件社区
2. 重复布局

利用屏幕的宽度优势,将相同属性的组件横向并列排布。 一般用于卡片陈列。我的实现思路是:先通过"display"接口拿到设备的显示属性,通过"display.getDefaultDisplay()"获得当前设备的"Display"对象,然后通过该对象的“width”属性拿到显示设备的宽度(单位为像素),最后判断不同屏幕宽度时该显示几列。配合设置栅格组件"Gird"的“columnsTemplate”属性来控制显示的列数。示例代码(/pages/index2.ets)如下:

import display from '@ohos.display'

@Entry
@Component
struct Index2 {
  @State colTemplate: string = '1fr'

  aboutToAppear() {
    var displayClass = null;
    display.getDefaultDisplay((err, data) => {
      if (err) {
        return;
      }
      displayClass = data;// 获得设备的屏幕信息,displayClass.width是设备的宽度

      // 栅格断点系统
      if(displayClass.width < vp2px(520)){
        this.colTemplate = '1fr'
      }else if(displayClass.width >= vp2px(520) && displayClass.width < vp2px(840)){
        this.colTemplate = '1fr 1fr'
      }else{
        this.colTemplate = '1fr 1fr 1fr'
      }
    });
  }

  build() {
    Flex({ direction: FlexDirection.Column }) {
      Column() {
        Grid() {
          GridItem() {
            Column(){
              // 这里放置每个卡片里的组件
            }.width('100%').height(150).borderRadius(16).backgroundColor('#FFFFFF')
          }
          GridItem() {
            Column(){
              // 这里放置每个卡片里的组件
            }.width('100%').height(150).borderRadius(16).backgroundColor('#FFFFFF')
          }
          GridItem() {
            Column(){
              // 这里放置每个卡片里的组件
            }.width('100%').height(150).borderRadius(16).backgroundColor('#FFFFFF')
          }
          GridItem() {
            Column(){
              // 这里放置每个卡片里的组件
            }.width('100%').height(150).borderRadius(16).backgroundColor('#FFFFFF')
          }
          GridItem() {
            Column(){
              // 这里放置每个卡片里的组件
            }.width('100%').height(150).borderRadius(16).backgroundColor('#FFFFFF')
          }
          GridItem() {
            Column(){
              // 这里放置每个卡片里的组件
            }.width('100%').height(150).borderRadius(16).backgroundColor('#FFFFFF')
          }
        }.width('100%').columnsGap(12).rowsGap(12).columnsTemplate(this.colTemplate)
      }.alignItems(HorizontalAlign.Start)
    }.width('100%').backgroundColor('#F1F3F5').padding({top:48, bottom:24,left: 24, right: 24})
  }
}

该效果需要通过远程模拟器查看效果,本地预览器无法获得设备属性。由于远程模拟器目前仅有手机设备,下图展示在手机横竖屏时的效果,如下图所示:

2.6 ArkUI实现一次开发多端部署-开源基础软件社区

该代码在折叠屏和平板上分别显示两列和三列,以下效果为我模拟而来,供参考,效果如下图:

2.6 ArkUI实现一次开发多端部署-开源基础软件社区
3. 挪移布局

光明顶至高武学“乾坤大挪移”。本案例以手机横竖屏切换时通过挪移组件位置,实现在竖屏和横屏状态下充分利用手机屏幕空间展示信息。实现思路:先判断设备的横竖屏状态,然后根据状态决定组件布局方式,示例代码(/pages/index3.ets)如下:

import mediaquery from '@system.mediaquery'
let portraitFunc = null

@Entry
@Component
struct Index3 {
  @State isLandscape: boolean = false
  orientationListener = mediaquery.matchMediaSync('screen and (orientation: landscape)')

  onPortrait(mediaQueryResult) {
    this.isLandscape = mediaQueryResult.matches;
  }

  aboutToAppear() {
    portraitFunc = this.onPortrait.bind(this)
    this.orientationListener.on('change', portraitFunc)
  }

  build() {
    Flex({ direction: (this.isLandscape ? FlexDirection.Row : FlexDirection.Column) }) {
      Column() {
        Image('/common/images/cover.png').width("100%").aspectRatio(1.5)
      }
      Column() {
        Text('本教程由浅入深阐述了鸿蒙3.0方舟开发框架最新编程语言ArkUI(eTS)的基础知识、设计标准、UI排版技巧、自定义多态组件开发、数据模拟,以及分布式全场景开发和异步编程等高级知识。全书共分为4篇:第一篇为拥抱鸿蒙3.0(第1章第4章),第二篇为鸿蒙开发进阶(第5章第9章),第三篇为鸿蒙开发高级(第10章第15章),第四篇为项目实战(第16章第18章)。')
          .fontSize(18)
      }.padding(10).layoutWeight(1)
    }.width('100%').height('100%').backgroundColor('#F1F3F5').padding({top:48, bottom:24,left: 24, right: 24})
  }
}

在远程模拟器上手机竖屏和横屏的效果如下图所示:

2.6 ArkUI实现一次开发多端部署-开源基础软件社区
4. 缩进布局

为了有更好的内容显示效果,可在不同屏幕宽度的设备上进行相应的缩进处理。 比如,卡片在手机竖屏时为4个栅格的宽度;而在手机横屏状态或折叠屏设备上居中显示,且设置为6个栅格的宽度;在平板设备上居中显示,且设置为8个栅格的宽度。示例代码(/pages/index4.ets)如下:

import display from '@ohos.display'

@Entry
@Component
struct Index4 {
  @State girdNum: number = 4 // 栅格数

  aboutToAppear() {
    var displayClass = null;
    display.getDefaultDisplay((err, data) => {
      if (err) {
        return;
      }
      displayClass = data;// 获得设备的屏幕信息,displayClass.width是设备的宽度

      // 栅格断点系统
      if(displayClass.width < vp2px(520)){
        this.girdNum = 4
      }else if(displayClass.width >= vp2px(520) && displayClass.width < vp2px(840)){
        this.girdNum = 8
      }else{
        this.girdNum = 12
      }
    });
  }

  build() {
    Stack({ alignContent: Alignment.Top }){
      Scroll() {
        Column() {
          Image('/common/images/cover.png').width("100%").aspectRatio(1.5)
          Text('本教程由浅入深阐述了鸿蒙3.0方舟开发框架最新编程语言ArkUI(eTS)的基础知识、设计标准、UI排版技巧、自定义多态组件开发、数据模拟,以及分布式全场景开发和异步编程等高级知识。全书共分为4篇:第一篇为拥抱鸿蒙3.0(第1章第4章),第二篇为鸿蒙开发进阶(第5章第9章),第三篇为鸿蒙开发高级(第10章第15章),第四篇为项目实战(第16章第18章)。书中主要内容包括:鸿蒙3.0真的来了、揭开方舟开发框架ArkUI(eTS)的神秘面纱、基础组件、布局组件、事件与手势、绘制组件、让用户界面生动而流畅、基于TS扩展的声明式开发规范、为ArkUI量身打造的多态组件库HUI、访问远程数据、使用mock模拟数据、全场景开发之分布式、多媒体、资源管理、其他高级技术、分布式新闻系统开发准备、App UI快速开发和运行前后端项目。书中包含大量应用示例,不仅可以学会理论知识还可以灵活应用。书中示例基于DevEco Studio 3.0 Beta2 for HarmonyOS环境开发,读者在学习到ArkUI(eTS)语言知识的同时还可学会方舟开发框架技术。书中通过接近商业的一个实战案例详细阐述了如何使用ArkUI(eTS)开发App,内容完整、步骤清晰,提供了工程化的解决方案。本书可作为方舟开发框架ArkUI(eTS)初学者的入门书籍,也可作为从事鸿蒙3.0App开发的技术人员的开发速查手册及培训机构的参考书籍。')
            .fontSize('20fp')
        }.width(this.girdNum==4 ? '100%' : (this.girdNum==8 ? '75%' : '67%'))
      }
    }.width('100%').height('100%').backgroundColor('#F1F3F5').padding({top:48, bottom:24,left: 24, right: 24})
  }
}

在远程模拟器上手机竖屏和横屏的效果如下图所示:

2.6 ArkUI实现一次开发多端部署-开源基础软件社区

前面三小节的代码可到码云下载:

【源码地址:https://gitee.com/cloudev/harmonyos3/tree/master/2.6

2.6.4 使用资源实现组件多态

前面讲解的技巧实现了多设备下布局的“一致性”,使同一份代码的App在不同设备上的布局均有良好表现。同时,适配了竖屏模式和横屏模式的差异。

与追求布局一致性的目标相反,在组件开发中,追求组件在多设备、多语言及“深色模式/浅色模式”的“差异性”。让组件在不同环境中呈现差异化的表现,称之为“多态”。

实现组件“多态”的关键技巧在于使用资源。

1. 资源定义

应用资源由开发者在工程的resources目录中定义,resources目录按照两级目录的形式来组织。

一级目录为base目录、限定词目录以及rawfile目录 。如下图所示:

2.6 ArkUI实现一次开发多端部署-开源基础软件社区

base目录:默认存在的目录。当应用的resources资源目录中没有与设备状态匹配的限定词目录时,会自动引用该目录中的资源文件。

限定词目录:开发者自行创建的目录。可以由一个或多个表征应用场景或设备特征的限定词组合而成,包括移动国家码和移动网络码、语言、文字、国家或地区、横竖屏、设备类型、颜色模式和屏幕密度等维度。App运行时,优先从限定词目录寻找与当前设备状态匹配的资源(如,中文语言、横屏模式、深色模式),找不到合适资源才会使用base目录中的资源。限定词目录也是实现组件多态的关键。

rawfile目录: 不会根据系统的状态去匹配,rawfile目录中可以直接存放资源文件。

二级目录为资源目录,用于存放字符串、颜色、浮点数等基础元素,以及媒体等资源文件。如下图所示:

2.6 ArkUI实现一次开发多端部署-开源基础软件社区

boolean.json:存放布尔型资源。

color.json:存放颜色资源。在“/base/element/color.json”目录下存放手机浅色模式和透明模式的各种颜色值。在限定词目录下的相应“color.json”中存放特定场景时生效的颜色资源。如下图所示:

2.6 ArkUI实现一次开发多端部署-开源基础软件社区

float.json:存放间距、圆角、字体等资源。 限定词目录下的“float.json”如下图所示:

2.6 ArkUI实现一次开发多端部署-开源基础软件社区

string.json:存放字符串资源。 原则上,除了从远程服务端数据库里取出的文字外,App中用到的文字显示,尽量设置到“string.json”中,即使是广告图片中存在文字,也建议将图片和文字分离。这样为App实现多语言版减少不必要的麻烦。默认情况下,除了默认的字符串资源,建议增加中文字符串资源和英文字符串资源。如下图所示:

2.6 ArkUI实现一次开发多端部署-开源基础软件社区

meida目录:媒体资源。如下图所示:

2.6 ArkUI实现一次开发多端部署-开源基础软件社区
2. 创建资源

刚创建一个ArkUI eTS项目时,并没有上图中的限定词目录,那么,它们是怎样创建出来的呢?步骤如下:

在resources目录上点击鼠标右键,选择“新建”,然后选择“资源目录”,如下图所示:

2.6 ArkUI实现一次开发多端部署-开源基础软件社区

在左侧的“Available qualifiers”下针对语言、横竖屏、设备、颜色模式(深色/浅色)、屏幕密度等提供各种可选限定词类型,本例以添加对车机的支持为例。选择Device,然后点击向右箭头按钮,如下图所示:

2.6 ArkUI实现一次开发多端部署-开源基础软件社区

Resource type的选项中,一般我们用得最多的是Element。如果是建立媒体资源,则选择Media。其它几种主要是用于Java UI的资源,我们不用关心。如下图所示:

2.6 ArkUI实现一次开发多端部署-开源基础软件社区

然后在Device下选择Car(车机),点击“确认”按钮,如下图所示:

2.6 ArkUI实现一次开发多端部署-开源基础软件社区

此时,在resources目录就建立了“/car/element”的子目录,DevEco Studio中显示为"car.element"。在该目录上使用鼠标右键,选择“新建”,然后选择Element Resource File,如下图所示:

2.6 ArkUI实现一次开发多端部署-开源基础软件社区

在Root element下拉选项中,选择需要的资源文件模板,如果需要建立颜色资源,就选color,文件名也输入"color",如下图所示:

2.6 ArkUI实现一次开发多端部署-开源基础软件社区

点击“确认”按钮,如下图所示:

2.6 ArkUI实现一次开发多端部署-开源基础软件社区

现在,适用于车机的颜色资源“color.json”就成功创建了。如下图所示:

2.6 ArkUI实现一次开发多端部署-开源基础软件社区

建议同学们利用一个Hello World项目,参照上面的步骤,多尝试下不同资源组合,看看都能建立什么样的限定词资源。

3. 资源引用

在工程中,通过“r(′app.type.name′)”的形式引用应用资源。“r('app.type.name')”的形式引用应用资源。“r(′app.type.name′)”的形式引用应用资源。“r”代表resources目录,“app”代表应用内定义的资源,“type”代表资源类型(或资源的存放位置),可以取“color”、“float”、“string”、“media”等,name代表资源命名,由开发者定义资源时确定,如上图中使用"color_1"这个name定义了一个“红色”资源。如果要设置一个文字的颜色为红色,那么可以使用如下代码:

Text('Hello World')
    .fontSize('20fp')
    .fontColor($r("app.color.color_1"))

引用rawfile下资源时使用“rawfile(′filename′)”的形式,当前rawfile('filename')”的形式,当前rawfile(′filename′)”的形式,当前rawfile仅支持Image控件引用图片资源,filename需要表示为rawfile目录下的文件相对路径,文件名需要包含后缀,路径开头不可以以"/"开头。

2.6.5 ArkUI eTS开发框架

虽然目前ArkUI eTS暂时仅支持手机、折叠屏、平板和车机,但是,通过观察鸿蒙官方的eTS API参考文档,对于每个组件的描述中都有一个支持设备的说明,全部标记对智慧屏和智能穿戴“不支持”,参考下图:

2.6 ArkUI实现一次开发多端部署-开源基础软件社区

可以解读出一个信息:对智慧屏和穿戴设备的支持是迟早的事情,否则根本没必要在每个文档中多此一举。所以,在本eTS开发框架中提前提供对智慧屏和智能穿戴设备的支持。

该UI框架命名为“HUI”,“H”是指HarmonyOS。随着后续课程的推进节奏,会持续丰富此框架。第一次提交的项目代码已配置好沉浸式体验的状态栏,同时,根据“2.3 ArkUI App设计规范”建立了配套的应用资源。

【源码地址:https://gitee.com/cloudev/HUI

2.6.6 小试牛刀

基于初始化Hello World页面,使用资源改造页面,代码如下:

@Entry
@Component
struct Index {
  build() {
    Column({space:8}) {
      Text($r("app.string.entry_MainAbility")) // 使用字符串资源输入文字
        .fontColor($r("app.color.fgLevel1")) // 文字颜色,适配深色模式/浅色模式
        .fontSize($r("app.float.fontSizeH6")) // 设置字号为6号标题
        .fontWeight(Number($r("app.float.fontWeightH6"))) // 设置6号标题的字重

      Text($r("app.string.mainability_description")) // 设置正文文本
        .fontColor($r("app.color.fgLevel2")) // 子标题采用辅助色
        .fontSize($r("app.float.fontSizeSubTitle1")) // 设置子标题字号
        .fontWeight(Number($r("app.float.fontWeightSubTitle1"))) // 设置子标题字重

      Image($r("app.media.cover")) // 使用媒体资源
        .width("100%")
        .aspectRatio(1.5)
        .borderRadius($r("app.float.radius_L")) // 图片圆角

      Text($r("app.string.specialColumn")) // 设置正文文本
        .fontColor($r("app.color.fgLevel1")) // 文字颜色
        .fontSize($r("app.float.fontSizeBody1")) // 设置正文字号
        .fontWeight(Number($r("app.float.fontWeightBody1"))) // 设置正文字重
    }
    .width('100%')
    .height('100%')
    .padding({top: $r("app.float.spaceTop"), bottom:$r("app.float.spaceBottom"), left:$r("app.float.spaceLeft"), right: $r("app.float.spaceRight")}) // 屏幕边缘间隔
    .backgroundColor($r("app.color.appBg")) // App背景颜色
  }
}

竖屏手机,中文语言环境,浅色模式:效果如下图所示:

2.6 ArkUI实现一次开发多端部署-开源基础软件社区

竖屏手机,英文语言环境,浅色模式:效果如下图所示:

2.6 ArkUI实现一次开发多端部署-开源基础软件社区

竖屏手机,中文语言环境,深色模式:效果如下图所示:

2.6 ArkUI实现一次开发多端部署-开源基础软件社区

手机横屏,深色模式,如下图所示:

2.6 ArkUI实现一次开发多端部署-开源基础软件社区

折叠屏,竖屏,浅色模式,如下图所示:

2.6 ArkUI实现一次开发多端部署-开源基础软件社区

折叠屏,横屏,深色模式,如下图所示:

2.6 ArkUI实现一次开发多端部署-开源基础软件社区

平板,横屏,浅色模式,如下图所示:

2.6 ArkUI实现一次开发多端部署-开源基础软件社区

平板,竖屏,深色模式,如下图所示:

2.6 ArkUI实现一次开发多端部署-开源基础软件社区

车机显示效果,如下图所示:

2.6 ArkUI实现一次开发多端部署-开源基础软件社区

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK