Harmony 个人中心(页面交互、跳转、导航、容器组件)(上)

简介: Harmony 个人中心(页面交互、跳转、导航、容器组件)(上)

前言


  今天是1024,祝各位程序员们,钱多事少离家近,不秃也强bug黄。在上一篇文章中,我们了解了DevEco Studio的主推开发语言ArkTS,并写了一个简单的例子,本文我们将学习另外一个例子来加深我们对于鸿蒙应用开发的理解。

9e747a440d4f428d8354d537d54d08d6.gif


正文


  本文的例子同样来源于HarmonyOS学堂,根据源码内容我们来反推开发过程,看开发过程中能学到那些知识点。


一、创建工程


  首先我们在DevEco Studio中创建一个名为MyCenter的功能,如下图所示:

6cdd66f39b8f46ff835e7039fc29c751.png

点击Finish创建项目,项目创建好之后,通过预览就能看到Hello World,下面来说明一下这次开发的个人中心App包含的内容,首先是一个登录页面,登录进去之后可以通过底部导航切换页面内容,分别是首页和个人的内容,下面我们首先来写登录页面。


二、登录


在创建工程时会自带一个页面,就是我们所看到的Index.ets,那么现在我们需要创建一个登录页面,鼠标右键pagesNewPage

837a7bf2d4474d5f905d43149d814263.png

会出现一个弹窗,我们输入页面的名称为Login。

28893118bc11479b8c41d00762f8dff5.png

点击Finish完成页面的创建,创建完成之后你可以在resource/main/base/profile/main_pages.json中看到我们增加的登录页面的配置。

8a94e5f6a1e54634b007a3c49fd645ba.png

① 更换启动页面

当前有两个页面,Index和Login,因为我们一打开App就进入Login页面,所以我们将两个内容的位置换一下,代码如下所示:

{
  "src": [
    "pages/Login",
    "pages/Index"
  ]
}


这样当App运行的时候第一个页面就是Login了,是不是很简单,下面我们要做的就是写这个登录页面的UI和功能,先来看看这个页面的UI效果

087388a25591476eab65c837ab30e47c.png

  根据这个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下的图标复制到你的项目中即可。

0f33aa3444134714891d842e5c932ed3.png

  下面我们可以开始写登录页面了,首先我们修改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 })


然后再预览一下效果如图所示:

31aebf28ce1a4718a031a91fda8458b5.png

下面我们来写账号和密码的输入框,添加如下所示代码:

    //账号输入框
      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'))


我们首先预览一下:

48fd083094dc41afa6d75f5132141eea.png

接下来我们说一下可以说明的点,首先就是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;
        })
      ...


② 拓展修饰符

  下面我们介绍一下拓展修饰符,首先我们看一下刚才所写的代码,如下图所示:

f176daf37284432aa8a881e2f18577e3.png

可以看到我标注的部分代码一致,但是确实是输入框和线所需要的设置,那么我们可以将重复的内容样式通过拓展修饰符封装一个样式函数,供同类样式使用,下面我们在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'))
}


注意这两个函数添加的位置,如下图所示:

818d3f0b88764edd9006d34dfb9d9991.png

如果你写在组件内部会报错的,然后我们再更新一下刚才的输入框和线的代码,如下图所示:

dfddf2da5b2b4272a327b77b2506cf99.png

相比上面的原始写法就简洁很多了,减少重复的代码,因为输入框下方的两个蓝色文字也是一样的样式,所以再增加一个扩展样式,代码如下所示:

@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)设置布局中的内容,左右都靠边,看一下预览效果:

3f00d3567e4249a1aa53b6615d411210.png

下面我们写登录按钮和注册的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' });
  }


这里的代码用到了promptActionrouter,两个都是鸿蒙内部的插件,你输入后如果发现有红色下划波浪线,那么就需要导包,鼠标放在波浪线上,使用快捷键,Alt + Enter会出现一个弹窗,

e49a4ae288be411ab79e3f3781ba87c2.png

选择第一项就会将所需要的插件导入到当前的组件中,导入后就不会报错了,导入内容如下图所示:

76b271d9aef849e29e5ef673c2cfb95b.png

然后在登录按钮的点击事件中调用登录函数,如下图所示:

8a5d512c55b54d33846b727c1bcb7105.png

重新预览一下,点击登录按钮试试看,如下图所示:

b780ae3ce58f4db3b07de4f530bd0e82.png

随便输入账号和密码再点击登录,就会跳转到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'))
  }


预览效果如下图所示:

3d2d94bef69845c795c0c5ee3ab28973.png


④ 等待进度条

  通常来说登录不会是一下子就成功的,涉及到登录之后的一些数据处理,网络延时,通常我们会在点击登录之后显示一个等待进度,告诉用户正在登录中,稍安勿躁,因为如果你登陆的时候没有什么变化,然后登录有很久,用户会以为你的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 })
      }


添加位置如下图所示:

c503ca2b59cb4df28c92c85a7a328359.png


这里通过判断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;
  }


因为当你跳转页面时,当前的组件就会销毁,就会触发这个生命周期函数,所以我们在这里进行销毁超时处理,下面我们来看一下运行的效果。

56993f2b0b6541f196b4aee23d77d6d9.gif


Harmony 个人中心(页面交互、跳转、导航、容器组件)(下)https://developer.aliyun.com/article/1407920

相关文章
|
4月前
|
存储 消息中间件 容器
当一个 Pod 中包含多个容器时,容器间共享一些重要的资源和环境,这使得它们能够更有效地协同工作和交互。
当一个 Pod 中包含多个容器时,容器间共享一些重要的资源和环境,这使得它们能够更有效地协同工作和交互。
|
4月前
|
缓存 开发者 Docker
Dockerfile是Docker容器化过程中的核心组件,它允许开发者以一种可重复、可移植的方式自动化地构建Docker镜像
【8月更文挑战第19天】Dockerfile是构建Docker镜像的脚本文件,含一系列指令定义镜像构建步骤。每条大写指令后跟至少一个参数,按序执行,每执行一条指令即生成新的镜像层。常用指令包括:FROM指定基础镜像;RUN执行构建命令;EXPOSE开放端口;CMD指定容器启动行为等。优化策略涉及减少镜像层数、选择轻量基础镜像、利用缓存及清理冗余文件。示例:基于Python应用的Dockerfile包括设置工作目录、复制文件、安装依赖等步骤。掌握Dockerfile有助于高效自动化构建镜像,加速应用部署。
36 1
|
4月前
|
前端开发 JavaScript 数据处理
React 中展示组件和容器组件
【8月更文挑战第31天】
98 0
|
4月前
|
域名解析 Kubernetes 负载均衡
在K8S中,外部访问容器服务,比如说提供了一个域名,链路怎么走?数据经过哪些组件?
在K8S中,外部访问容器服务,比如说提供了一个域名,链路怎么走?数据经过哪些组件?
|
5月前
|
JavaScript 前端开发 容器
vue组件封装——固定宽高比的容器(2种方法:纯CSS实现 + JS实现)
vue组件封装——固定宽高比的容器(2种方法:纯CSS实现 + JS实现)
203 2
|
6月前
|
监控 Linux 数据处理
Linux中的nsenter命令:深入容器内部,实现无缝交互
`nsenter`是Linux工具,用于进入容器的命名空间,实现与容器内环境的交互。它基于Linux内核的命名空间功能,支持网络、PID等多类型隔离。`nsenter`允许在不停止容器的情况下调试和操作,如 `-t` 指定PID进入命名空间,`-n` 进入网络命名空间。示例包括使用`nsenter`查看容器进程或网络配置。使用时注意目标进程状态,理解命名空间类型,并谨慎操作。
|
5月前
|
容器
Could not autowire No beans of ‘UserSerice,这样的bug,主要是idea检测到你没有往页面中,没有往容器中注入一个userService,容器注入UserSe
Could not autowire No beans of ‘UserSerice,这样的bug,主要是idea检测到你没有往页面中,没有往容器中注入一个userService,容器注入UserSe
|
5月前
|
Java 数据安全/隐私保护 容器
Java详解:GUI容器组件 | 功能组件
Java详解:GUI容器组件 | 功能组件
|
5月前
|
Java 容器
Java详解:GUI图形用户界面设计—容器组件及面板布局方式
Java详解:GUI图形用户界面设计—容器组件及面板布局方式
118 0
|
6月前
|
前端开发 程序员 UED
全面解析layui:掌握基础知识与实用技能(1. 核心组件与模块 2. 布局与容器 3. 弹出层与提示框;1. 数据表格与数据表单 2. 表单验证与提交 3. 图片轮播与导航菜单)
全面解析layui:掌握基础知识与实用技能(1. 核心组件与模块 2. 布局与容器 3. 弹出层与提示框;1. 数据表格与数据表单 2. 表单验证与提交 3. 图片轮播与导航菜单)
79 0