HarmonyOS笔记8:开发基于MVI的HarmonyOS的移动应用

MVI模式是Model-View-Intent(模型-视图-意图)的简称。是在移动应用开发常见的一种软件架构模式,如图1所示。其中:
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/7de577003e2c409d99a80f620f847105.png

模型(Model)

表示应用的状态。通常是一个不可变的数据类。

视图(View)

用户界面,用于显示状态并发送用户动作。

意图(Intent)

表示用户意图(或事件),这些意图会触发状态更新。

在HarmonyOS Next中,可以使用ArkTS进行开发基于MVI架构的移动应用。下面将通过展示一个简单生成密码应用来了解MVI架构。

一、定义状态(数据模型)

在简易密码生成的应用中,需要展示原始字符串数据和将原始字符串生成密码的数据。因此,在移动应用状态的定义中需要包含这两种属性。定义如下状态数据类CryptState的代码如下:

export class CryptState{
  //原始数据
  content:string = ""
  //加密数据
  crypt:string = ""

  constructor(content:string) {
    this.content = content
  }
}

因为将原始数据加密生成加密的数据,所以在构造方法中,只传递了一个元素数据content的属性值。

二、定义意图(Intent)

在密码生成的应用中,需要完成意图非常简单,只有两种:一种是生成密码的意图,另外一种是重置数据的意图。基于此,定义一个意图接口CryptIntent,代码如下:

export interface CryptIntent{
  type:"GenerateCrypt"|"ResetCrypt"
}

在CryptIntent中,用:

(1). “GenerateCrypt”:表示生成密码意图;
(2). “ResetCrypt”:表示重置原始数据为空的意图;

三、创建ViewModel来处理意图并管理状态

ViewModel也是MVI架构的重要成员,它作为UI层(View)与业务逻辑层(Model)之间的桥梁,负责处理用户意图(Intent)并管理应用状态(State)。通过处理不同的意图,使得应用的状态也发生相应的变化。在密码生成的实例中,定义CryptViewModel类来实现这个桥梁的作用,通过处理不同的意图,达到修改状态的目的。

import { CryptState } from "../model/CryptState"
import { cryptoFramework } from "@kit.CryptoArchitectureKit"
import {CryptIntent} from "../intent/CryptIntent"
@Observed
export class CryptViewModel{
  private chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*';

  @Track state:CryptState = new CryptState("")

  processIntent(intent:CryptIntent){
    let newState = new CryptState(this.state.content)

    switch(intent.type){
      case "GenerateCrypt":{
        this.generate(newState)
        break;
      }
      case "ResetCrypt":{
        this.reset(newState)
        break;
      }
    }
    this.state = newState
  }
  /**
   * 使用安全随机数生成密码
   * @param content
   * @returns
   */
  async generate(state:CryptState){
    //定义生成的密码串
    let result = ""
    //生成随机数
    let rand = cryptoFramework.createRandom()
    //生成随机字节数组
    let randData = await rand.generateRandom(state.content.length)
    let byteArr = new Uint8Array(randData.data.buffer)
    //将字节映射到字符集
    for(let i = 0; i<byteArr.length;i++){
      result+=this.chars.charAt(byteArr[i]%this.chars.length)
    }
    state.crypt = result
  }

  reset(state:CryptState){
    state.content = ""
    state.crypt = ""
  }
}

在上述代码中,有几点说明:
(1)@Observed修饰器来修饰CryptViewModel类,这表示标记该类是可观察的;
(2)@Track装饰器用于标记类中的属性,当该属性被修改时,会触发UI更新。@Track往往和@Observed配合使用;
(3)在CryptViewModel类的processIntent方法的定义中,首先创建新的状态值newState,然后处理不同的意图,是的新的状态值进行变更,然后再将@Track追踪的应用状态值state变更为newState;
(2)在CryptViewModel类的generate方法定义成一个包含CryptState参数的异步方法。这是因为在生成密码中调用的rand.generateRandom(state.content.length)方法返回一个Promise对象,必须在异步方法中调用。对于该方法可以通过await用于等待一个 Promise 完成,实现异步调用。因此将该类的generate方法定义为async异步方法。在generate方法中,最后生成的密文赋值给状态state对象的crypt属性。
(3)reset方法,也是接受一个状态值对象,将状态的两个属性值content和crypt分别重置为空字符串。

四、定义界面

4.1 定义文本输入的UI自定义控件 TextInput

因为界面中需要通过不同的输入,进行处理,因此自定义一个TextInput的UI控件实现原始数据的输入,代码如下:

import { CryptViewModel } from "../vm/CryptViewModel"

@Component
export struct InputTxt{
  @ObjectLink viewModel:CryptViewModel
  build(){
    Row(){
      TextInput({placeholder:"输入字符串",text:this.viewModel.state.content})
        .onChange((e)=>{
          this.viewModel.state.content = e
        })

    }.alignItems(VerticalAlign.Center)
    .justifyContent(FlexAlign.Center)
    .width("80%")
    .height(LayoutPolicy.wrapContent)
    .border({width:1,color:Color.Blue})
  }
}

在上述代码中,使用@ObjectLink装饰器修饰viewModel对象。在这里,通过@ObjectLink实现嵌套对象双向数据绑定,必须与 @Observed 配合使用。在主界面Index的CryptViewModel对象的任何属性的变化,与InputTxt组件同步修改。

4.2 定义主页面

主页面的代码如下:

import { CryptViewModel } from '../vm/CryptViewModel'
import {CryptIntent} from "../intent/CryptIntent"
import { InputTxt } from './InputText'

@Entry
@Component
@Component
struct Index {
  @State viewModel:CryptViewModel = new CryptViewModel()

  build() {
    Column() {
      Text("密码生成应用")
        .fontSize(40)
        .fontColor(Color.Green)
        .margin({bottom:30})

      InputTxt({viewModel:this.viewModel})

      Text(this.viewModel.state.crypt)
        .fontSize(28)
        .fontColor(Color.Black)
        .margin({bottom:20})

      Row(){
        Button("生成密码串")
          .onClick(()=>{
            this.viewModel.processIntent({type:"GenerateCrypt"})
          })
          .margin({right:20})

        Button("重置输入")
          .onClick(()=>{
           this.viewModel.processIntent({type:"ResetCrypt"})
          })
          .margin({left:20})
      }.width("100%")
      .alignItems(VerticalAlign.Center)
      .justifyContent(FlexAlign.Center)

    }.justifyContent(FlexAlign.Center)
    .alignItems(HorizontalAlign.Center)
    .height('100%')
    .width('100%')
  }
}

最后的运行结果如下图所示:
在这里插入图片描述

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值