前言
今天是1024,祝各位程序员们,钱多事少离家近,不秃也强bug黄。在上一篇文章中,我们了解了DevEco Studio的主推开发语言ArkTS
,并写了一个简单的例子,本文我们将学习另外一个例子来加深我们对于鸿蒙应用开发的理解。
正文
本文的例子同样来源于HarmonyOS学堂,根据源码内容我们来反推开发过程,看开发过程中能学到那些知识点。
一、创建工程
首先我们在DevEco Studio中创建一个名为MyCenter的功能,如下图所示:
点击Finish
创建项目,项目创建好之后,通过预览就能看到Hello World
,下面来说明一下这次开发的个人中心App包含的内容,首先是一个登录页面,登录进去之后可以通过底部导航切换页面内容,分别是首页和个人的内容,下面我们首先来写登录页面。
二、登录
在创建工程时会自带一个页面,就是我们所看到的Index.ets,那么现在我们需要创建一个登录页面,鼠标右键pages
→New
→Page
。
会出现一个弹窗,我们输入页面的名称为Login。
点击Finish完成页面的创建,创建完成之后你可以在resource/main/base/profile/main_pages.json
中看到我们增加的登录页面的配置。
① 更换启动页面
当前有两个页面,Index和Login,因为我们一打开App就进入Login页面,所以我们将两个内容的位置换一下,代码如下所示:
{ "src": [ "pages/Login", "pages/Index" ] }
这样当App运行的时候第一个页面就是Login了,是不是很简单,下面我们要做的就是写这个登录页面的UI和功能,先来看看这个页面的UI效果
根据这个UI,我们可以得出,页面整体效果为纵向布局,中间两处文字为蓝色文字和底部三个登录方式为横向,为了方便使用,首先我们增加文字的颜色和图标,首先修改resources/base/element/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" } ] }
颜色弄好了,然后将源码中resources/base/element/meida
下的图标复制到你的项目中即可。
下面我们可以开始写登录页面了,首先我们修改build()
函数中的内容,定义一个纵向布局,然后设置颜色,内容大小和内容填充,代码如下所示:
@Entry @Component struct Login { build() { // 页面纵向布局 Column() { .backgroundColor($r('app.color.background')) .height('100%') .width('100%') .padding({ left: 12, right: 12, bottom: 24 }) } }
此时保存一下预览效果是一片空白,下面我们在Column()
中增加一个图标和两段文字,代码如下所示:
//Logo Image($r('app.media.logo')) .width(78) .height(78) .margin({ top: 100, bottom: 16 }) Text('登录') .fontSize(24) .fontWeight(FontWeight.Medium) .fontColor($r('app.color.title_text_color')) Text('登录帐号以使用更多服务') .fontSize(16) .fontColor($r('app.color.login_more_text_color')) .margin({ top: 16, bottom: 30 })
然后再预览一下效果如图所示:
下面我们来写账号和密码的输入框,添加如下所示代码:
//账号输入框 TextInput({ placeholder: '账号' }) .maxLength(11) .type(InputType.Number) .placeholderColor($r('app.color.placeholder_color')) .height(45) .fontSize(18) .backgroundColor($r('app.color.background')) .width('100%') .padding({ left: 0 }) .margin({ top: 12 }) .onChange((value: string) => { //获取输入的内容 }) //下划线 Line().width('100%') .height(2) .backgroundColor($r('app.color.line_color')) //密码输入框 TextInput({ placeholder: '密码' }) .maxLength(8) .type(InputType.Password) .placeholderColor($r('app.color.placeholder_color')) .height(45) .fontSize(18) .backgroundColor($r('app.color.background')) .width('100%') .padding({ left: 0 }) .margin({ top: 12 }) .onChange((value: string) => { //获取输入的内容 }) //下划线 Line().width('100%') .height(2) .backgroundColor($r('app.color.line_color'))
我们首先预览一下:
接下来我们说一下可以说明的点,首先就是TextInput的输入类型,如果为Password,则会自带一个按钮,点击可以查看密码的明文内容,这一点我还是很喜欢的,不用自己写了,同时我们看到还有一个onChange()
,里面会实时同步你输入的内容,因此如果我们想要获取输入框的内容,就需要定义变量来接收,可以在Login中定义变量,代码如下所示:
@State account: string = ''; @State password: string = '';
这是账号和密码的输入值,依次为内容赋值,将账号输入框下的内容赋值给account
, 密码输入框下的内容赋值给password
,代码如下所示:
//账号输入框 TextInput({ placeholder: '账号' }) ... .onChange((value: string) => { //获取输入的内容 this.account = value; }) ... //密码输入框 TextInput({ placeholder: '密码' }) ... .onChange((value: string) => { //获取输入的内容 this.password = value; }) ...
② 拓展修饰符
下面我们介绍一下拓展修饰符,首先我们看一下刚才所写的代码,如下图所示:
可以看到我标注的部分代码一致,但是确实是输入框和线所需要的设置,那么我们可以将重复的内容样式通过拓展修饰符封装一个样式函数,供同类样式使用,下面我们在Login{}
外面增加一个inputStyle()
函数,代码如下所示:
/** * 输入框的通用样式 */ @Extend(TextInput) function inputStyle() { .placeholderColor($r('app.color.placeholder_color')) .height(45) .fontSize(18) .backgroundColor($r('app.color.background')) .width('100%') .padding({ left: 0 }) .margin({ top: 12 }) }
这里我们用到@Extend
修饰符,它里面是所修饰的类型,这里输入的是TextInput
组件,里面的代码就是刚才的标注的代码,挪过来而已,然后我们再写一个lineStyle()
函数,代码如下所示:
@Extend(Line) function lineStyle() { .width('100%') .height(2) .backgroundColor($r('app.color.line_color')) }
注意这两个函数添加的位置,如下图所示:
如果你写在组件内部会报错的,然后我们再更新一下刚才的输入框和线的代码,如下图所示:
相比上面的原始写法就简洁很多了,减少重复的代码,因为输入框下方的两个蓝色文字也是一样的样式,所以再增加一个扩展样式,代码如下所示:
@Extend(Text) function blueTextStyle() { .fontColor($r('app.color.login_blue_text_color')) .fontSize(14) .fontWeight(FontWeight.Medium) }
在下划线后面增加代码,如下所示:
Row() { Text('短信验证码登录').blueTextStyle() Text('忘记密码').blueTextStyle() } .justifyContent(FlexAlign.SpaceBetween) .width('100%') .margin({ top: 8 })
通过一个横向布局,装载两个Text,通过justifyContent(FlexAlign.SpaceBetween)
设置布局中的内容,左右都靠边,看一下预览效果:
下面我们写登录按钮和注册的UI,在上述代码后面继续增加代码,如下所示:
//登录按钮 Button('登录', { type: ButtonType.Capsule }) .width('90%') .height(40) .fontSize(16) .fontWeight(FontWeight.Medium) .backgroundColor($r('app.color.login_button_color')) .margin({ top: 80, bottom: 12 }) .onClick(() => { // 登录 }) Text('注册账号') .fontColor($r('app.color.login_blue_text_color')) .fontSize(16) .fontWeight(FontWeight.Medium)
这里的代码其实就没有什么好解释的,一目了然,重点是点击登录按钮之后在onClick()
函数中执行的代码。
③ 页面跳转
下面我们在Login{}
里面增加一个登录的函数,代码如下所示:
/** * 登录 */ login(): void { if (this.account === '' || this.password === '') { //显示Toast promptAction.showToast({ message: '账号或密码为空' }) return } router.replaceUrl({ url: 'pages/Index' }); }
这里的代码用到了promptAction
和router
,两个都是鸿蒙内部的插件,你输入后如果发现有红色下划波浪线,那么就需要导包,鼠标放在波浪线上,使用快捷键,Alt + Enter会出现一个弹窗,
选择第一项就会将所需要的插件导入到当前的组件中,导入后就不会报错了,导入内容如下图所示:
然后在登录按钮的点击事件中调用登录函数,如下图所示:
重新预览一下,点击登录按钮试试看,如下图所示:
随便输入账号和密码再点击登录,就会跳转到Index页面,你可以试试看呀,不过我们当前登录页面的内容其实还没有写完,因为有一些细节还需要处理,首先要做的就是页面完整,在注册账号的Text后面再添加如下代码:
//空白填充组件,具有自动填充容器空余部分的能力。仅当父组件为Row/Column时生效。 Blank() Text('其他登录方式') .fontColor($r('app.color.other_login_text_color')) .fontSize(12) .fontWeight(FontWeight.Medium) .margin({ top: 48, bottom: 12 }) Row({ space: 44 }) { this.imageButton($r('app.media.login_method1')) this.imageButton($r('app.media.login_method2')) this.imageButton($r('app.media.login_method3')) }
这里因为3个按钮样式一致,只是图片资源不同,因此我们可以通过@Builder
修饰器构建一个按钮,在Login{}
中添加如下代码:
/** * 其他登录方式按钮 * @param src */ @Builder imageButton(src: Resource) { Button({ type: ButtonType.Circle, stateEffect: true }) { Image(src) } .height(48) .width(48) .backgroundColor($r('app.color.background')) }
预览效果如下图所示:
④ 等待进度条
通常来说登录不会是一下子就成功的,涉及到登录之后的一些数据处理,网络延时,通常我们会在点击登录之后显示一个等待进度,告诉用户正在登录中,稍安勿躁,因为如果你登陆的时候没有什么变化,然后登录有很久,用户会以为你的App卡死了,直接就给你卸载。
那我们同样可以定义几个变量,在Login{}
中添加如下代码:
//是否显示加载条 @State isShowProgress: boolean = false; //超时标识 private timeOutId: number = -1;
我们通过这个isShowProgress
变量来设置是否显示加载进度,这个timeOutId
用来做超时处理,比如登录多久可以进入主页面,简单模拟一下,首先要做的就是修改这个isShowProgress
变量为true
,修改login()
函数,代码如下所示:
login(): void { if (this.account === '' || this.password === '') { //显示Toast promptAction.showToast({ message: '账号或密码为空' }) return } //内容不为空则显示加载进度条 this.isShowProgress = true; if (this.timeOutId === -1) { //设置超时处理,两秒后执行页面跳转到主页 this.timeOutId = setTimeout(() => { this.isShowProgress = false; this.timeOutId = -1; router.replaceUrl({ url: 'pages/Index' }); }, 2000); } }
这里我们将isShowProgress
赋值为true
,那么就会触发UI刷新,因此我们需要在build()函数中,合适的位置判断这个isShowProgress
值,注意这个加载进度不是一个弹窗,它会占用页面的空间,所以还记得我们之前所写的那个Blank()
组件吗?我们在它的上面添加如下代码:
//是否显示等待进度条 if (this.isShowProgress) { LoadingProgress() .color($r('app.color.loading_color')) .width(30) .height(30) .margin({ top: 20 }) }
添加位置如下图所示:
这里通过判断isShowProgress
来显示等待进度条,那么就还有取消显示的地方,我们再回到login()
函数,观察下面这段代码:
if (this.timeOutId === -1) { //设置超时处理,两秒后执行页面跳转到主页 this.timeOutId = setTimeout(() => { this.isShowProgress = false; this.timeOutId = -1; router.replaceUrl({ url: 'pages/Index' }); }, 2000); }
首先判断这个参数是否为-1,是的话则增加一个2秒超时处理,在超时结束时我们将isShowProgress
设置为false,那么刷新UI的时候就会去掉之前所显示的加载进度条,同时将timeOutId
再设置为-1,最后跳转页面。我们还可以增加一个生命周期的处理,在Login{}
中增加如下代码:
/** * 组件的生命周期,组件销毁时执行 */ aboutToDisappear() { clearTimeout(this.timeOutId); this.timeOutId = -1; }
因为当你跳转页面时,当前的组件就会销毁,就会触发这个生命周期函数,所以我们在这里进行销毁超时处理,下面我们来看一下运行的效果。
Harmony 个人中心(页面交互、跳转、导航、容器组件)(下)https://developer.aliyun.com/article/1407920