鸿蒙应用开发从入门到入行 - 篇3:ArkUI布局基础与制作可交互页面

导读:在本篇文章里,您将掌握事件、装饰器、双向绑定等相关知识,并利用所学知识做一个待办列表的案例。

练手案例:登录界面

  • 开始之前,先说些题外话

  • 猫林老师发现不少同学可以独立写出来,我很欣慰。说明行动力、悟性、之前前端留下的布局思想都还在。希望各位同学和更多的朋友们都能参与进来。大家以后写完可以把自己的代码或者效果贴到评论区相互讨论。讨论的人越多越有学习氛围,在这里大家都可以找到志同道合的人。并且,每一次热烈的讨论都能激励猫林老师更认真迅速地去写下一篇文章。

  • 而且写教程文真的很需要花费额外时间,那就会挤占猫林老师想做爱做的事的时间。而且纯爱心发电其实动力并不足,所以也真的很需要各位读者提供热烈的正向回馈来激励猫林老师,所以大家请一定要多点赞、收藏、评论。要是可以,也分享给你周围想学鸿蒙的朋友。猫林老师保证把系列文章更新下去,让大家从文章里就能学到真东西,并且具备找工作能力。

  • 好了,话不多说,开始说回上次的作业案例,让我们先回顾一下作业的效果图:

  • 从上图分析可以发现整体上所有内容是从上往下布局,所以用Column作为根容器非常合适。

  • 然后里面可以分为8行元素,分别为:Image、Text、Text、TextInput、TextInput、Row、Button、Text,如图

  • 这些都是比较容易看出来的布局,主要是给大家解释下 短信验证码登录忘记密码那一行,为什么还要用一个Row包起来呢?因为如果这两个文字不被Row包起来的话,那么父组件是Column,那 短信验证码登录忘记密码就会变成一行一个。所以用一个Row包起来,因为Row有从左到右布局子组件的能力,而这两个文字就需要从左到右,只不过一个在起点,一个在终点(即在首尾),所以这里到时候还可以给它做一个主轴上的布局为SpaceBetween

  • 其他的无非记得要让根容器Column铺满屏幕,也即宽高百分百,图片给宽度、登录界面给文字大小和加粗,登录帐号以使用更多服务改文字颜色、文字大小。两个TextInput给占位符,其中第二个TextInput记得要把type设置为password。其他剩余的三个label都是改文字颜色、字体大小。登录按钮给宽度铺满。然后整个页面是灰色,所以给Column设置背景色,再给TextInput设置背景色为白色。

  • 根据上述分析,代码如下

        Column() {
    Image($r('app.media.app_icon'))
    .width(100) Text('登录界面')
    .fontSize(24)
    .fontWeight(700)
    Text('登录账号以使用更多服务')
    .fontColor(Color.Gray)
    .fontSize(14) TextInput({ placeholder: '账号' })
    .backgroundColor('#fefefe') TextInput({ placeholder: '密码' })
    .backgroundColor('#fefefe')
    .type(InputType.Password) Row() {
    Text('短信验证码登录')
    .fontColor('#3172f3')
    Text('忘记密码')
    .fontColor('#3172f3')
    }
    .justifyContent(FlexAlign.SpaceBetween) Button('登录')
    .width('100%')
    Text('注册账号')
    .fontColor('#3172f3')
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
    .backgroundColor('#eff1f3')
  • 对应效果如下:

  • 此时大家发现两个问题:

    1. 明明短信验证码那一行的Row给了 justifyContent(FlexAlign.SpaceBetween),但没生效,看着还是居中。为什么呢?
    2. 所有内容行与行之间没有间距,导致挨的太紧。
  • 这两个问题都很好很解决,分别如下

    1. 给了FlexAlign.SpaceBetween也没生效,是因为猫林老师上节课就说过大部分组件不给宽高就是靠内容撑开宽高,也即内容有大,Row就只有多大。所以你设置首尾对齐实际上它已经是首尾了,只不过因为Row就那么大,所以效果不动

      所以解决办法很简答:给Row一个width('100%')即可

    2. 行与行之间要设置间距可以给Column加space

  • 因此,改良代码如下(仅写出本次改动部分)

        Column({ space: 10 }) {
    ...... Row() {
    Text('短信验证码登录')
    .fontColor('#3172f3')
    Text('忘记密码')
    .fontColor('#3172f3')
    }
    .width('100%')
    .justifyContent(FlexAlign.SpaceBetween) ........
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
    .backgroundColor('#eff1f3')
  • 此时效果如下

  • 发现间距是有了,但是中间的账号、密码、短信验证码登录这歌区域跟上面和下面太近,需要把这部分跟上下加一些间距

  • 如上图所示,发现这个时候我们应该把中间这一部分作为一个整体,再统一设置整体的上下外间距即可。因此需要给中间的2个TextInput和它下面包住两个文字的Row再套一个父容器。这里请大家思考下,他们的父容器,用Column好还是Row好呢?

    • 没错,用Column好!因为用Row他们会从左往右排列,而我们依然要它从上到下,只不过多个父容器而已,所以用Column
  • 那分析完加父容器后,还有个问题。怎么设置这个父容器的距离外部的间距呢?会css的同学知道,是margin,没错,ArkTS里也是margin(如果不懂什么叫margin的请挑战到本文最后的附录:外间距与内间距,再回此继续观看),语法如下

    组件() {
    
    }
    .margin(间距数) // 例
    Column() { }
    .margin(20) // 代表这个 Column 具体上下左右间距都是20
  • 如果你不需要上下左右间距都是同一个值,则可以传入一个对象,分别设置不同的margin,例

    Column() {
    
    }
    .margin({ left: 20, right: 10, top: 15, bottom: 30 } ) // 代表这个Column左间距20,右间距10,上间距15,下间距30
  • 也可以仅设置几个方向的间距,没设置方向的间距代表为0,例

    Column() {
    
    }
    .margin({ top: 10, bottom: 20 } ) // 代表这个Column上间距10,下间距20,左右间距因为没设置,那么则代表0也即没有间距
  • 因此,案例代码里先给中间部分加Column,且设置外间距。并且因为中间部分包了Colum后,他们各自之间也没间距了,因此给包住验证码登录文字的Row再加一个上间距,TextInput不用加,因为他们本身就要挨在一起,改动代码如下

        Column({ space: 10 }) {
    ......... Column() {
    TextInput({ placeholder: '账号' })
    .backgroundColor('#fefefe') TextInput({ placeholder: '密码' })
    .backgroundColor('#fefefe')
    .type(InputType.Password) Row() {
    Text('短信验证码登录')
    .fontColor('#3172f3')
    Text('忘记密码')
    .fontColor('#3172f3')
    }
    .margin({ top: 10 })
    }
    .margin({ top: 20, bottom: 20 }) .......
    }
  • 此时效果如下

  • 没错,现在跟最终效果图已经差不多了,但是发现左右两边都挨到边边了,而效果图需要左右两边都有点间距。这时候有两种解决办法:

    1. 给两个TextInput、Row、Button这四行设置左右外间距
    2. 给他们共同的父组件设置内间距
  • 很明显,用第二种给共同的父组件设置内间距办法更方便。但是ArkTS里如何设置呢?其实还是用padding,并且用法跟margin是一样的,例

    Column() {
    
    }
    .padding(20) // 上下左右四个方向内间距都是20 Column() { }
    .padding({ top: 20, bottom: 10, left: 20, right: 25 }) // 上内间距20,下内间距10,左内间距20,右内间距25 Column() { }
    .padding({ left: 20, right: 20 }) // 左内间距和右内间距都是20,上下没写则默认是0
  • 因此,再给根组件Column设置padding,且只需要左右间距即可

        Column({ space: 10 }) {
    ........
    }
    .......
    .padding({ left: 10, right: 10 })
  • 至此,一个完整的登录界面就写完了,你学废了吗?

  • 本案例新知识:

    • margin: 外间距
    • padding :内间距

事件

  • 上面的登录案例中,我们目前点登录按钮是没有任何反应的。要想让它有反应,必须添加事件,在ArkTS中如何添加事件呢?

  • 语法

    组件() {
    
    }
    .on事件名( e => {
    // 事件处理代码
    } )
  • Button('登录')
    .onClick(e => {
    // 处理代码
    })
  • 注意:上面的Click里的C大写。后面的事件名如无特殊情况,都是要首字母大写,例如change事件,写的时候要加onChange,这跟前端里的全小写不一样,大家要注意。

  • e则是事件对象,但用的略少,不需要时,可以不写e,替换为小括号

    Button('登录')
    .onClick(() => {
    // 处理代码
    })

提示框

  • 如果现在,我希望点击按钮后弹出登录成功的提示框怎么办呢?

  • ArkUI里提供了PromptAction对象,专门用来做弹窗

  • 用法:

    • 先导入,再调用PromptAction对象的showToast方法,传入对象,配置提示信息,例
    import { promptAction } from '@kit.ArkUI'
    
    promptAction.showToast({
    message: '提示消息', // 提示的文字
    duration: 2000 // 显示时长,不填则默认为1500
    });

技巧:可以在写代码时,直接写promptAction,然后出提示后按回车,DevEco会自动帮你生成导入的代码,如下图

  • 注意:

    • duration为提示框多久后消失(也即显示时长),可以不填,不填则默认为1500,并且最小值也是1500,最大值是10000。如果填写的数字小于1500,也按1500来显示,如果大于10000,也按10000来显示。
    • 单位是毫秒,1500即1.5秒
  • 例:

    promptAction.showToast({ message: '猫林老师教程真好!' })
    • 效果如下

  • 更多弹窗的用法可阅读官方文档:点我跳转至官方文档

声明组件内成员变量

  • 很多时候我们这个页面(组件)需要声明一些变量用来保存数据,和对其处理。那么怎么声明呢?

  • 一般会写在build函数的上面,struct关键子下面,即下图位置

  • 语法为:

    变量名: 类型 = 初始值
  • 例:

    userId: string = '' // 声明了一个名为 userId 的变量,它是字符串类型,初始值为空字符串
  • 变量声明好了,如何在代码中使用呢?一律前面加this访问,例

    this.userId

双向绑定

  • 学会声明成员变量后,我们在登录案例 里,声明两个变量,分别叫userIduserPwd,专门用来跟账号、密码输入框分别做双向绑定

    struct Index {
    // 成员变量列表
    userId: string = 'admin'
    userPwd: string = '123' build() {
    ........
    }
    }

插播双向绑定: 即数据一旦改变,界面跟着变。 界面输入内容有变化,数据也跟着变。

  • 那么ArkTS里如何让数据跟输入框做双向绑定呢?(Next版本后新增的语法)

    TextInput({ text: $$成员变量 })
    
    // 例
    TextInput( text: $$this.userId )
  • 接下来让我们把声明的userIduserPwd分别绑定到账号框和密码框

    TextInput({ placeholder: '账号', text: $$this.userId })
    .backgroundColor('#fefefe') TextInput({ placeholder: '密码', text: $$this.userPwd })
    .backgroundColor('#fefefe')
    .type(InputType.Password)
  • 此时保存代码会看到预览器里界面已经能显示绑定的数据了,如图

  • 那我们说双向绑定是:数据 -> 界面, 同样,界面的输入变化也会影响数据,那是否能呢?带着这个疑问,我们先给登录按钮加一个点击事件,点击事件里用console.log输出这两个变量的值。

    Button('登录')
    .width('100%')
    .onClick(() => {
    console.log(`账号:${this.userId}, 密码:${this.userPwd}`)
    })

注意:这里用到了模板字符串,一些同学可能不太理解这种字符串。这里说明一下:首先是用`这个符号包起来,跟单引号双引号都表示字符串,但区别在于模板字符串能很方便做字符串拼接,例如上面的代码,相当于是 '账号:' + this.userId + '密码:' + this.userPwd

  • 然后我们去预览起的界面上重新输入,再点按钮输出,看显示什么(具体看截图,可以看到在哪看console.log输出的内容)

  • 小结:

    • 在输入框里,使用成员变量前加 $$ 即可双向绑定
  • 需注意:

    • 目前$$仅能用在基本数据类型且绑定给内置组件

装饰器 - @State

  • 从上面的效果可以看到,已经实现了双向绑定,但此时存在一个问题:数据无法再触发界面更新

  • 例:修改登录的点击事件,在里面我修改userId的值,看界面是否能更新

          Button('登录')
    .width('100%')
    .onClick(() => {
    this.userId = '我要变'
    console.log('新值:' + this.userId)
    })
    • 结果如图

  • 原因:默认声明的成员变量不具备数据改变触发界面更新渲染的功能

  • 解决办法:需要使用装饰器

  • 装饰器:

    • 修饰某些数据、函数,使其具有特殊作用

    • 装饰器有很多种,本次学的叫 @State,注意首字母大写

    • @State作用:

      • 当被@State修饰的变量数据改变时,UI会发生对应的重新渲染。
    • 用法

      @State 变量: 类型 = '初始值'
  • 让我们测试一下,来到登录案例里找到userId,给它加@State试试

    @State userId: string = 'admin'
  • 效果如下图:

  • 但是同样的,加了装饰器后会有轻微的性能开销,即使这种开销甚至可以忽略不计。但是对于对性能优化有要求的App而言,则建议。如果数据仅仅只是用来内部参与运算或临时接收界面输入,不需要将来重新更新UI,就不加@State

  • 装饰器不光只有@State,后续还有很多,学一个记一个。

  • 小结:

    • 装饰器

      • 修饰数据、函数等,使其具有特殊作用
    • @State
      • 被@State修饰的变量能当它数据改变时,UI会发生对应的重新渲染

实现登录功能

  • 最后,我们给登录案例收个尾,当用户点击登录按钮时,如果输入的账号是admin,密码是123,则提示登录成功,否则登录失败(将来学发送请求,如今暂时写死硬判断)

  • 代码如下

         Button('登录')
    .width('100%')
    .onClick(() => {
    if (this.userId === 'admin' && this.userPwd === '123') {
    promptAction.showToast({ message: '登录成功' })
    } else {
    promptAction.showToast({ message: '账号或密码错误' })
    }
    })
  • 效果如图:

总结内容

  • 本文中我们学了事件、提示框、成员变量声明、双向绑定、装饰器。我们回顾一下

  • 事件:

    • on事件名,事件名首字母大写,例如:onClick、onChange
  • 提示框:

    • 需要先导入

      import { promptAction } from '@kit.ArkUI'
    • 然后使用

      promptAction.showToast( { message: '提示信息', duration:时长  } )
    • 技巧:可以直接输入promptAction,出提示后,按回车,DevEco会自动导入

  • 声明成员变量

    变量: 类型 = 初始值
  • 默认情况下,变量改变不会触发界面重新渲染,因此需要装饰器:@State

  • 双向绑定

    $$this.变量名

课后练习

  • 判断题:请回答对或者错
    1. promptAction.showToast方法,传入duration属性,值为1000,代表提示框在1秒后消失

    2. 成员变量与输入框双向绑定时,成员变量前面不用加this

    3. 数据如果不加@State,就不能进行计算

练习答案

  1. 错 2. 错 3. 错

    ​ (错错错,是我的错。热恋的时候怎么不说,生活的无奈我已好困惑,你能不能不要再啰嗦)--- 请唱出来

附加练习

  • 如上图所示,做一个年度待办目标的列表。
  • 本案例功能比较丰富,各位能做多少做多少。本案例也会贯穿后面好几天的教学,所以涉及非常多新知识,做不出来也正常。
  • 提示:打勾部份可以用Image也可以用Checkbox,如需要做出布局,需要自行根据文档预习ProgressStackList

互动环节

  • 你觉得鸿蒙开发跟你以前会的开发,区别大吗?欢迎留下你的观点。
  • 最后,创作不易,请不要吝啬您的点赞、关注、收藏、转发!

交流群

  • 建了个鸿蒙交流群,帮助希望从业的人员方便交流技术以及获取鸿蒙认证。需要的请添加猫林老师微信(llybf365),拉你进群

鸿蒙应用开发从入门到入行 - 篇3:ArkUI布局基础与制作可交互页面的更多相关文章

  1. Python Web自动化测试入门与实战,从入门到入行

    Python Web自动化测试入门与实战 购买地址 · 京东:https://item.jd.com/69239480564.html   天猫:https://detail.tmall.com/it ...

  2. LARK BOARD开发板入门学习-第2篇

    1. 本次主要研究下HDMI接口,使用芯片是CH7033,这个芯片可以接VGA和HDMI两种接口,和FPGA的接口是地址数据总线 2. 值得注意的地方,下图的D1,双二极管BAT54S在电路中一般用于 ...

  3. Python全栈开发之路 【第六篇】:Python基础之常用模块

    本节内容 模块分类: 好处: 标准库: help("modules") 查看所有python自带模块列表 第三方开源模块: 自定义模块: 模块调用: import module f ...

  4. Python全栈开发之路 【第四篇】:Python基础之函数

    本节内容 函数def: 1.位置参数,默认参数 2.位置参数,关键参数 3.如果参数中出现 *users,传递的参数就可以不再是固定的个数, 传过来的所有元素进行打包成元组 *args,**kwarg ...

  5. Python全栈开发之路 【第三篇】:Python基础之字符编码和文件操作

    本节内容 一.三元运算 三元运算又称三目运算,是对简单的条件语句的简写,如: 简单条件语句: if 条件成立: val = 1 else: val = 2 改成三元运算: val = 1 if 条件成 ...

  6. Python全栈开发之路 【第五篇】:Python基础之函数进阶(装饰器、生成器&迭代器)

    本节内容 一.名称空间 又名name space,就是存放名字的地方.举例说明,若变量x=1,1存放于内存中,那名字x存放在哪里呢?名称空间正是存放名字x与1绑定关系的地方. 名称空间共3种,分别如下 ...

  7. 「Android 开发」入门笔记

    「Android 开发」入门笔记(界面编程篇) ------每日摘要------ DAY-1: 学习笔记: Android应用结构分析 界面编程与视图(View)组件 布局管理器 问题整理: Andr ...

  8. 浅谈入行Qt桌面端开发程序员-从毕业到上岗(1):当我们说到桌面端开发时,我们在谈论什么?

    谈谈我自己 大家好,我是轩先生,是一个刚入行的Qt桌面端开发程序员.我的本科是双非一本的数学专业,22年毕业,只是部分课程与计算机之间有所交叉,其实在我毕业的时候并没有想过会成为一名程序员,也没有想过 ...

  9. PHP开发入行真功夫 三扬科技

    前言与目录 PHP开发入行真功夫 前言 PHP开发入行真功夫 目录   第2章 基本语法 2.1.1 判断闰年程序 2.1.2 我们现在能做的…… 2.2.1 PHP的语言概貌 2.2.2 为我们的程 ...

  10. 《开发专家 Visual C 开发入行真功夫》笔记

    智能感知的功能,输入 is 后,同时按下Alt + →这两个键就出现了供选择变量.方法.宏等的列表,继续输入 in 后,isInit就出来了. stdafx.h预编译头文件,.h应用程序主头文件,do ...

随机推荐

  1. 【Wing】背后的插件们

    wing 作为我们日常开发的命令行开发工具,项目开源以来,陆陆续续接入了多个插件,在这里集中分享给大家. ☞ Github ☜ ☞ Gitee ☜ 01. wing -screen 作为Android ...

  2. find_sys_call_table和kallsysms_lookup_name的区别

    find_sys_call_table 和 kallsyms_lookup_name 都可以用于查找内核符号,但它们的具体作用和使用场景有所不同.以下是两者的详细对比: 1. find_sys_cal ...

  3. python处理nii文件

    第一步安装nibabel,可以使用命令:pip install nibabel 之后: from nibabel.viewers import OrthoSlicer3D as osdimport n ...

  4. js 中必须加分号的位置集合

    1. 匿名函数(自执行函数)(function (){}()) 2. 解析赋值    2 个变量交换位置

  5. 存储事件 storage

    // 去手动删除本地存储触发存储事件 window.addEventListener('storage', function () { console.log('存储事件触发了') }) const ...

  6. yarn serve 不能开启vue项目 the project seem to require yarn but isnot install

    error: answer: 删除 yarn.lock 或者使用 npm run serve 替换 ; ps: yarn.lock 是锁定第三方包版本的文件:

  7. 43.v-if和v-for的优先级

    v-for 的优先级高 延申问题:v-for 和 v-if 为什么不能在一起使用 ? 会造成性能的浪费,因为v-for 的优先级高,所以每次渲染都会执行v-if 判断条件,浪费时间 :比如 渲染 10 ...

  8. dp线段树优化

    题目:Potted Flower Description The little cat takes over the management of a new park. There is a larg ...

  9. Exchange限制邮箱用户每天/每分钟的发送邮件数量和速率

    Exchange限制邮箱用户每天/每分钟的发送邮件数量和速率 近期遇到部分Exchange客服反馈内部邮箱账号密码被盗,给内部其他同事和外部邮箱发送大量钓鱼和诈骗邮件:对公司造成很大负面影响和经济损失 ...

  10. DRF-Parser解析器组件源码分析和应用

    1. 解析器源码分析 注意:以下源码为了方便理解已进行简化,只保留了解析器相关的代码 # 视图函数: class MyView(APIView): def post(self, request): p ...