零基础快速上手HarmonyOS ArkTS开发4---从简单的页面开始
接着上一次https://www.cnblogs.com/webor2006/p/17865848.html的继续往下。
常用基础组件:
概述:
关于组件的一些基础概念就里就不多说了,官方有很详细的说明,而在HarmonyOS按功能分有如下几大类组件:基础组件、容器组件、媒体组件、绘制组件、画布组件。而这里先从最基础的组件开始学习,有Text、Image、TextInput、Button、LoadingProgress等,以一个最常见的登录界面来说:
界面中的是由以下几个基础组件搭建而成:
所以下面先来学习这几个基础组件。
Image:
用来渲染展示图片,支持加载本地和网络图片,
基本使用:
对应登录界面的这个LOGO:
其中是设置了图片数据源和图片的大小:
所以下面来看一下图片相关的设置。
设置图片数据源:
它的接口形式如下:
Image(src: string | PixelMap | Resource)
其中src是图片数据源,支持以下三种类型:
1、使用string数据加载图片:
其中需要注意的是加载网络图片时需要在module.json5文件中申明网络访问权限:
它不止能加载网络图片,还可以支持本地图片,官方API对其的描述下面贴一下:
当使用相对路径引用本地图片时,例如Image("common/test.jpg"),不支持跨包/跨模块调用该Image组件,建议使用Resource格式来管理需全局使用的图片资源。
- 支持Base64字符串。格式data:image/[png|jpeg|bmp|webp];base64,[base64 data], 其中[base64 data]为Base64字符串数据。
- 支持file://路径前缀的字符串。用于读取本应用安装目录下files文件夹下的图片资源。需要保证目录包路径下的文件有可读权限。
2、PixelMap格式为像素图,常用于图片编辑的场景。
3、使用Resource数据加载图片:
可以跨包/跨模块访问资源文件,是访问本地图片的推荐方式。
也就是需要将图片添加到resources的media/rawfile目录下:
设置图片大小:
有三种设置方式。
1、使用number数据设置图片大小:
其中默认单位使用的是“vp”,类似于Android的sp,屏幕密度相关像素,在HarmonyOS中的单位有如下几种:
2、使用string数据设置图片大小:
如果是这种形式则需要显示的加上单位。
3、使用Resource数据设置图片大小: 【推荐】
此时则需要在float.json文件中定义图片宽高:
这种方式是最推荐的,统一放到Resource中进行维护管理。
设置缩放类型:
关于图片通常还会有缩放类型的设置,这里其实官网已经介绍得非常详细了,这里过一下,比如有这么一张源图:
而设置缩放类型是如下设置的:
具体不同的缩放类型其对应的效果如下:
1、Contain:保持宽高比进行缩小或者放大,使得图片完全显示在显示边界内。
2、Cover(默认值):保持宽高比进行缩小或者放大,使得图片两边都大于或等于显示边界。
3、Auto:自适应显示。
4、Fill:不保持宽高比进行放大缩小,使得图片充满显示边界。
5、ScaleDown:保持宽高比显示,图片缩小或者保持不变
6、None:保持原有尺寸显示。
Text:
基本使用:
对应登录界面的这个文本:
下面具体来看一下相关的设置:
设置文本内容:
Text(content?: string | Resource)
其中入参:content是文本内容。
1、使用string数据设置文本内容:
2、使用Resource数据设置文本内容:【推荐】
其中文本内容定义在resources下的string.json中:
设置文本大小:fontSize
1、使用number数据设置文本大小:
其默认单位是fp。
2、使用string数据设置文本大小:
此时就需要显示的指定像素单位了。
3、使用Resource数据设置大小:【推荐】
此时则需要在float.json文件中定义文本大小:
设置文本精细:fontWeight
1、使用number数据设置文本精细:
设置文本的字体粗细,number类型取值[100, 900],取值间隔为100,默认为400,取值越大,字体越粗。
2、使用FontWeight枚举类型设置文本精细:
它可以取下面这些枚举类型:
设置文本颜色:
1、使用Color枚举设置文本颜色:
2、使用number数据设置文本颜色:
其number类型值为16进制整数。
3、使用string数据设置文本颜色:
其数据格式为rgb或rgba颜色。
4、使用Resource设置文本颜色:【推荐】
同样也需要在color.json中定义文本颜色:
TextInput:
可以输入单行文本并支持响应输入事件,比如在登录界面中的账号输入框:
基本使用:
1、设置提示文本:
这种提示功能使用placeholder属性,另外还可以使用placeholderColor和placeholderFont分别设置提示文本的颜色和样式,示例代码如下:
TextInput({ placeholder: '请输入帐号' }) .placeholderColor(0x999999) .placeholderFont({ size: 20, weight: FontWeight.Medium, family: 'cursive', style: FontStyle.Italic })
其效果:
2、设置最大输入字符数:
使用maxLength属性来进行设置。
3、设置文本框输入类型:
其类型有:
Button:
基本使用:
1、设置按钮样式:
其中说一下按钮的样式,有如下几种:
容器组件:
在Android中有比较经典的四大布局组件(LinearLayout、RelativeLayout、FrameLayout、ConstraintLayout),这些都是父布局,类似有在Harmony中也有这样的容器组件,提供了Column和Row容器来实现线性布局,还是以登录界面为例,以控件的角度来将页面的布局可以表示为:
可以看到整体的布局是按着从上到下的顺序来排列的,所以此时可以由Column容器来进行布局:
而有些元素是需要水平排列的,此时就可以使用Row容器来进行布局:
布局基础知识:
主轴和交叉轴概念:
在布局容器中,默认存在两根轴,分别是主轴和交叉轴,这两个轴始终是相互垂直的。不同的容器中主轴的方向不一样的。
主轴:
在Column容器中的子组件是按照从上到下的垂直方向布局的,其主轴的方向是垂直方向;在Row容器中的组件是按照从左到右的水平方向布局的,其主轴的方向是水平方向。
交叉轴:
与主轴垂直相交的轴线,如果主轴是垂直方向,则交叉轴就是水平方向;如果主轴是水平方向,则交叉轴是垂直方向。
属性介绍:
了解了布局容器的主轴和交叉轴概念之后,有助于我们更好地理解子组件在主轴和交叉轴的排列方式。这里需要学习两个属性:justifyContent和alignItems,其具体的含义如下:
如果不理解主轴和交叉轴的概念,那么对于这两个属性的运用就会很迷糊。 对于这两个属性的使用官方其实已经解释得非常详细了,这里照着官方的整体再来学习一遍。
1、主轴方向的对齐(justifyContent):
对于justifyContent属性,它接收的参数类型为FlexAlign,有如下几个值:
光看这些文字对于这些值的设置效果还是非常抽象的,所以下面看一下每一种参数类型设置之后的效果,这样就比较容易理解了:
1、Start:元素在主轴方向首端对齐,第一个元素与行首对齐,同时后续的元素与前一个对齐。
2、Center:元素在主轴方向中心对齐,第一个元素与行首的距离以及最后一个元素与行尾距离相同。
3、End:元素在主轴方向尾部对齐,最后一个元素与行尾对齐,其他元素与后一个对齐。
4、SpaceBetween:元素在主轴方向均匀分配弹性元素,相邻元素之间距离相同。 第一个元素与行首对齐,最后一个元素与行尾对齐。
5、SpaceAround:元素在主轴方向均匀分配弹性元素,相邻元素之间距离相同。 第一个元素到行首的距离和最后一个元素到行尾的距离是相邻元素之间距离的一半。
6、SpaceEvenly:元素在主轴方向等间距布局,无论是相邻元素还是边界元素到容器的间距都一样。
2、交叉轴方向的对齐(alignItems):
对于Column容器它的交叉轴是水平方向,所以参数类型为HorizontalAlign(水平对齐),有如下几种对齐方式:
1、Start:设置子组件在水平方向上按照起始端对齐。
2、Center(默认值):设置子组件在水平方向上居中对齐。
3、End:设置子组件在水平方向上按照末端对齐。
而对于Row容器的交叉轴是垂直方向,所以参数为VerticalAlign(垂直对齐),它有如下几种对齐方式:
1、Top:设置子组件在垂直方向上居顶部对齐。
2、Center(默认值):设置子组件在竖直方向上居中对齐。
3、Bottom:设置子组件在竖直方向上居底部对齐。
接口介绍:
对于Column和Row容器都有一个可选参数space,表示子组件在主轴方向上的间距:
其效果如下:
实践:构建登录页面:
接下来咱们则用学过的布局来实现开篇所示的这么一个登录静态界面,实操一把:
1、新建工程:
其项目名称直接校仿官方的codelabs的https://gitee.com/harmonyos/codelabs/tree/master/ArkTSComponents
2、新建登录页面文件:
@Entry @Component struct LoginPage { @State message: string = 'Login Page' build() { Row() { Column() { Text(this.message) .fontSize(50) .fontWeight(FontWeight.Bold) } .width('100%') } .height('100%') } }
先让其可以显示出来,此时需要修改这里的加载页面:
运行:
界面构建:
接下来则来进行界面的构建:
1、构建Logo区域:
整个是一个上下布局,所以就需要用到这次所学的Column容器组件了:
其中会用到一些常量,这里为了省事,直接从官方的源码中拷过来了:
CommonConstants.ets:
/** * Common constants for all features. */ export default class CommonConstants { /** * Input length of the account. */ static readonly INPUT_ACCOUNT_LENGTH = 11; /** * Input length of the password. */ static readonly INPUT_PASSWORD_LENGTH = 8; /** * Left padding of the input box */ static readonly INPUT_PADDING_LEFT = 0; /** * Delay time of simulated login */ static readonly LOGIN_DELAY_TIME = 2000; /** * Common Spacing of Components */ static readonly COMMON_SPACE = 12; /** * Title text of the home page */ static readonly HOME_TITLE = '首页'; /** * Title text of the setting page */ static readonly MINE_TITLE = '我的'; /** * Spacing of other login methods */ static readonly LOGIN_METHODS_SPACE = 44; /** * The width or height of the component is spread across the parent component. */ static readonly FULL_PARENT = '100%'; /** * The width of button */ static readonly BUTTON_WIDTH = '90%'; /** * The width of setting list */ static readonly SET_LIST_WIDTH = '42%'; /** * Home tab index */ static readonly HOME_TAB_INDEX = 0; /** * Mine tab index */ static readonly MINE_TAB_INDEX = 1; }
float.json:
{ "float": [ { "name": "logo_image_size", "value": "78vp" }, { "name": "logo_margin_top", "value": "100vp" }, { "name": "logo_margin_bottom", "value": "8vp" }, { "name": "page_title_text_size", "value": "24fp" }, { "name": "normal_text_size", "value": "16fp" }, { "name": "big_text_size", "value": "18fp" }, { "name": "small_text_size", "value": "14fp" }, { "name": "little_text_size", "value": "12fp" }, { "name": "login_more_margin_bottom", "value": "30vp" }, { "name": "login_more_margin_top", "value": "8vp" }, { "name": "login_input_height", "value": "45vp" }, { "name": "forgot_margin_top", "value": "8vp" }, { "name": "input_margin_top", "value": "12vp" }, { "name": "line_height", "value": "1vp" }, { "name": "login_button_height", "value": "40vp" }, { "name": "login_button_margin_top", "value": "87vp" }, { "name": "login_button_margin_bottom", "value": "12vp" }, { "name": "vertical_line_margin", "value": "15vp" }, { "name": "login_progress_size", "value": "30vp" }, { "name": "login_progress_margin_top", "value": "20vp" }, { "name": "other_login_margin_top", "value": "50vp" }, { "name": "other_login_margin_bottom", "value": "12vp" }, { "name": "login_page_padding_bottom", "value": "24vp" }, { "name": "other_login_image_size", "value": "48vp" }, { "name": "page_padding_hor", "value": "12vp" }, { "name": "mainPage_baseTab_top", "value": "4vp" }, { "name": "mainPage_baseTab_margin", "value": "8vp" }, { "name": "mainPage_padding", "value": "12vp" }, { "name": "mainPage_barHeight", "value": "56vp" }, { "name": "mainPage_baseTab_size", "value": "25vp" }, { "name": "main_tab_fontSize", "value": "10fp" }, { "name": "mainPage_tabTitles_margin", "value": "12vp" }, { "name": "mainPage_tabTitles_padding", "value": "12vp" }, { "name": "home_swiper_borderRadius", "value": "16vp" }, { "name": "home_swiper_margin", "value": "24vp" }, { "name": "home_grid_columnsGap", "value": "8vp" }, { "name": "home_grid_rowGap", "value": "12vp" }, { "name": "home_grid_padding", "value": "12vp" }, { "name": "home_grid_height", "value": "124vp" }, { "name": "home_grid_margin", "value": "10vp" }, { "name": "home_list_margin", "value": "4vp" }, { "name": "home_list_padding", "value": "8vp" }, { "name": "home_grid_borderRadius", "value": "24vp" }, { "name": "home_text_margin", "value": "12vp" }, { "name": "home_backgroundImage_borderRadius", "value": "12vp" }, { "name": "home_secondGrid_height", "value": "260vp" }, { "name": "home_secondGrid_margin", "value": "10vp" }, { "name": "home_homeCell_size", "value": "24vp" }, { "name": "home_homeCell_margin", "value": "4vp" }, { "name": "home_homeCell_width", "value": "80vp" }, { "name": "home_homeCell_height", "value": "80vp" }, { "name": "setting_name_margin", "value": "4vp" }, { "name": "setting_account_margin", "value": "24vp" }, { "name": "setting_account_size", "value": "48vp" }, { "name": "setting_account_fontSize", "value": "20fp" }, { "name": "setting_account_text_height", "value": "28vp" }, { "name": "setting_account_height", "value": "96vp" }, { "name": "setting_account_padding", "value": "24vp" }, { "name": "setting_account_borderRadius", "value": "16vp" }, { "name": "setting_list_height", "value": "48vp" }, { "name": "setting_list_padding", "value": "4vp" }, { "name": "setting_list_borderRadius", "value": "16vp" }, { "name": "setting_list_startMargin", "value": "42vp" }, { "name": "setting_list_endMargin", "value": "24vp" }, { "name": "setting_list_strokeWidth", "value": "0.25vp" }, { "name": "setting_button_bottom", "value": "55vp" }, { "name": "setting_size", "value": "22vp" }, { "name": "setting_padding", "value": "10vp" }, { "name": "setting_jump_width", "value": "12vp" }, { "name": "setting_jump_height", "value": "24vp" }, { "name": "setting_settingCell_left", "value": "8vp" }, { "name": "setting_settingCell_right", "value": "22vp" } ] }
color.json:
{ "color": [ { "name": "start_window_background", "value": "#FFFFFF" }, { "name": "white", "value": "#FFFFFF" }, { "name": "background", "value": "#F1F3F5" }, { "name": "title_text_color", "value": "#182431" }, { "name": "login_more_text_color", "value": "#99182431" }, { "name": "placeholder_color", "value": "#99182431" }, { "name": "line_color", "value": "#33182431" }, { "name": "login_button_color", "value": "#007DFF" }, { "name": "login_blue_text_color", "value": "#007DFF" }, { "name": "other_login_text_color", "value": "#838D97" }, { "name": "loading_color", "value": "#182431" }, { "name": "mainPage_selected", "value": "#1698CE" }, { "name": "mainPage_normal", "value": "#6B6B6B" }, { "name": "mainPage_backgroundColor", "value": "#F1F3F5" }, { "name": "home_grid_fontColor", "value": "#99182431" }, { "name": "setting_button_backgroundColor", "value": "#E5E8EA" }, { "name": "setting_button_fontColor", "value": "#FA2A2D" } ] }
此时效果如下:
接下来修改布局:
Image($r('app.media.logo')) .width($r('app.float.logo_image_size')) .height($r('app.float.logo_image_size')) .margin({ top: $r('app.float.logo_margin_top'), bottom: $r('app.float.logo_margin_bottom') }) Text($r('app.string.login_page')) .fontSize($r('app.float.page_title_text_size')) .fontWeight(FontWeight.Medium) .fontColor($r('app.color.title_text_color')) Text($r('app.string.login_more')) .fontSize($r('app.float.normal_text_size')) .fontColor($r('app.color.login_more_text_color')) .margin({ bottom: $r('app.float.login_more_margin_bottom'), top: $r('app.float.login_more_margin_top') })
其效果如下:
2、构建用户名密码输入框区域:
/* 用户名、密码输入框 */ TextInput({ placeholder: $r('app.string.account') }) .maxLength(CommonConstants.INPUT_ACCOUNT_LENGTH) .type(InputType.Number) .placeholderColor($r('app.color.placeholder_color')) .height($r('app.float.login_input_height')) .fontSize($r('app.float.big_text_size')) .backgroundColor($r('app.color.background')) .width(CommonConstants.FULL_PARENT) .padding({ left: CommonConstants.INPUT_PADDING_LEFT }) .margin({ top: $r('app.float.input_margin_top') }) Line() .width(CommonConstants.FULL_PARENT) .height($r('app.float.line_height')) .backgroundColor($r('app.color.line_color')) TextInput({ placeholder: $r('app.string.password') }) .maxLength(CommonConstants.INPUT_PASSWORD_LENGTH) .type(InputType.Password) .placeholderColor($r('app.color.placeholder_color')) .height($r('app.float.login_input_height')) .fontSize($r('app.float.big_text_size')) .backgroundColor($r('app.color.background')) .width(CommonConstants.FULL_PARENT) .padding({ left: CommonConstants.INPUT_PADDING_LEFT }) .margin({ top: $r('app.float.input_margin_top') }) Line() .width(CommonConstants.FULL_PARENT) .height($r('app.float.line_height')) .backgroundColor($r('app.color.line_color'))
其效果如下:
这里面是纯熟能生巧的代码,也非常好理解,所以就不过多解释了,照着官方的demo撸一撸就成,不过这里跟官网的源码有如下区别:
官方的源码写得非常的精练,咱们来学习一下这样的写法,其核心就是对相同的属性做了一个封装,所以我这边故意写得这么臃肿,好来看一下整个的优化过程,先来看目前代码存在什么问题?
所以有必要对其公共的属性进行一个抽取,具体如何抽取呢?看下官方的代码便知,先来将TextInput中的公共属性抽取一下:
把它抽取到一个函数中:
此时函数报错了:
找不到相关的属性,这个也容易理解,本身这些属性是来自于控件当中的,怎么可以在一个全新的函数中知道这些属性呢?那如何解决呢?这里就需要用到这么一个装饰器了@Extend:
先来看一下它的定义:
关于它更加详细的使用可以参考:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/arkts-extend-V5,好此时对于TextInput的代码就可以精简成这样了:
同样的,对于Line也可以照此进行封装:
3、构建短信验证码及忘记密码区域:
接下来则来构建这个区域了:
这里则需要使用到水平布局Row容器了,如下:
此时运行看一下:
接下来则需要让这俩元素分散在两端,此时就需要用到上面所学的主轴方向的元素对齐了,如下:
此时运行如下:
4、构建登录注册按钮区域:
接下来则来构建这个区域:
/* 登录注册按钮区域 */ Button($r('app.string.login'), { type: ButtonType.Capsule }) .width(CommonConstants.BUTTON_WIDTH) .height($r('app.float.login_button_height')) .fontSize($r('app.float.normal_text_size')) .fontWeight(FontWeight.Medium) .backgroundColor($r('app.color.login_button_color')) .margin({ top: $r('app.float.login_button_margin_top'), bottom: $r('app.float.login_button_margin_bottom') }) Text($r('app.string.register_account')) .fontColor($r('app.color.login_blue_text_color')) .fontSize($r('app.float.normal_text_size')) .fontWeight(FontWeight.Medium)
运行:
这块没有什么好解释的。
5、构建其他登录方式区域:
还剩最后一个底部区域木有构建:
其中涉及到一些资源图,这里可以直接从官方源码中拷至工程中:
此时可以看到是长这样:
貌似跟预期的有一点区域,应该是整个区域是靠底部,如何让它靠在最底部呢?这里需要用到空白填充组件Blank,如下:
此时再运行就如预期了,这里来了解一下该填充组件:https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/ts-basic-components-blank-V5
看文字是不是还不太好理解,这里再看一下官方文档提供的示例就明白了:
另外此代码还有一个可以优化的点,就是:
代码都一样,除了按钮的图片资源不一样:
代码可以进行一次封装么?可以的,可以看下官方的写法,相当的精练:
所以照着优化一下:
此时代码就可以这样写了:
此时又有一个知识点,那@Builder这是啥装饰器呢?这里可以参考它:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-builder-0000001524176981-V2,看一下它的说明:
看一下它的简单使用示例:
也就是如果在界面中有重复使用的元素,就可以使用该装饰器对其进行一个封装,提高代码的维护性。
总结:
至此,这里通过所学完成了官方登录页面的搭建,按着视频的总结一下此次所学:
1、垂直布局使用Column容器;
2、水平布局使用Row容器;
3、space参数设置子组件在主轴方向上的对齐;
4、justContent属性设置子组件在主轴方向上的对齐;
5、alignItems属性设置子组件在交叉轴方向上的对齐;
好长一段时间没有更新了,人越来越懒~~