HarmonyOS应用开发(组件库)--组件模块化开发、工具包、设计模式(持续更新)


在这里插入图片描述

常量格式

export  class CommonConstants {
  // 首选项名称
  static  readonly  PRE_USER:string= "user"
  // 全局应用
  static  readonly  STORE_USER:string= "user"
  // 列表查询行数
  static  readonly  LIST_SIZE:number = 10
}

枚举enum格式

// 数值可以为number、string
export enum Status {
  Enable=0, //启用
  Disable=1 // 禁用
}

正则表达式

…手机号校验

  isValidPhoneNumber(phoneNumber: string): boolean {
    const regex = /^(13[0-9]|14[5|7|9]|15[0-3|5-9]|16[2|5|6|7]|17[0-8]|18[0-9]|19[8|9])\d{8}$/;
    return regex.test(phoneNumber)
  }

…邮箱校验

  isValidEmail(email: string): boolean {
    let regEmail = /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(\.[a-zA-Z0-9_-])+/;
    return regEmail.test(email)
  }

文件

导包: import fs from ‘@ohos.file.fs’;

判断文件是否存在

 // 判断文件是否存在?
 let imgFile = filesDir + '/myImg.png'; // 路径
 let res = fs.accessSync(imgFile);
 if (res) {
   //如存在,就删除
   fs.unlinkSync(imgFile);
 }

网络下载

导包:
import request from ‘@ohos.request’;
import fs from ‘@ohos.file.fs’;

下载图片

request.downloadFile(getContext(), {
      url: netFile, filePath: imgFile // 网络路径,本地路径
    }).then((task) => {
      task.on('complete', function callback() {
      	let file:fileIo.File;
          file = fs.openSync(imgFile) // 本地路径
        prompt.showToast({
          message: "文件下载成功:" + imgFile
        })
      })
      task.on('fail', function callBack(err) {
        prompt.showToast({
          message: "文件下载失败,err:" + JSON.stringify(err)
        })
      });

从沙箱中图片转为Base64格式

// 工具包:
// 其中当转为这个后,可能会与原文件有差异,需要修改配置base64ImgStr
// 获取沙箱路径:this.context.__Dir
async toBase64(image_uri: string) {
  if (image_uri) {
    let selectImage = fs.openSync(image_uri, fs.OpenMode.READ_WRITE) // openSync() 根据文件file://路径 转文件
    const imageSource = image.createImageSource(selectImage.fd);
    const imagePackerApi: image.ImagePacker = image.createImagePacker();
    let packOpts: image.PackingOption = { format: 'image/jpeg', quality: 100 };
    let readBuffer = await imagePackerApi.packing(imageSource, packOpts);
    let bufferArr = new Uint8Array(readBuffer)
    let help = new util.Base64Helper
    let base = help.encodeToStringSync(bufferArr)
    let base64ImgStr = 'data:image/png;base64,' + base; // 回显需要有前面的前缀才可以
    return base64ImgStr;
  }
  return '';
}

从资源文件中读取图片转Base64

 async toBase64StrFromResource(img_name: string) {
    // 1. 获取图片所对应的PixelMap对象
    let pixelMap: PixelMap = await this.toPixelMapFromResource(img_name); // 自己封装函数,见下
    // 2. 获取pixelMap总字节数
    let pixelBytesNumber: number = pixelMap.getPixelBytesNumber();
    // 3. 创建了一个ImagePacker对象,用于图像编码。(ImagePacker是HarmonyOS中用于将图像(PixelMap格式)编码为不同存档格式的工具)
    const imagePackageApi: image.ImagePacker = image.createImagePacker();
    // 4. 定义了一个PackingOption对象,用于设置图像编码的选项。
    let packOpts: image.PackingOption = {
      format: 'image/jpeg', //表示编码的图像格式为jpeg格式
      quality: 100, //表示图像质量为最高质量(取值为0~100之间)
    }
    // 5. 将pixelMap对象按照packOpts中设置的选项进行编码。编码的结果被存储在readBuffer中
    const readBuffer = await imagePackageApi.packing(pixelMap, packOpts);
    // 6. 创建了一个Base64Helper对象,用于进行Base64编码和解码操作。
    let base64Helper = new util.Base64Helper();
    // 7. 将readBuffer转换为一个Uint8Array对象(Uint8Array是一种JavaScript内置对象,表示一个8位无符号整数的数组,适合表示二进制数据。)
    let uint8Arr = new Uint8Array(readBuffer);
    // 8. 将uint8Arr中的二进制数据同步地编码为Base64字符串
    let pixelStr = base64Helper.encodeToStringSync(uint8Arr);
    // 9. 加入base64编码协议(用于显示在Image组件中)(以下前缀形成一个可以在Web上直接使用的Data URL。这个Data URL可以被用于在HTML或CSS中嵌入图像数据,而不需要额外的图像文件)
    let base64ImgStr = 'data:image/png;base64,' + pixelStr;
    Log.MyLog("'hmlog-->',base64 str : " + base64ImgStr);
    // 10. 返回编码后并转换为Data URL的Base64字符中
    return base64ImgStr;
  }


  async toPixelMapFromResource(img_name: string) {
    // 1. 获取stage模型下的Context上下文对象
    const context = getContext(this);
    // 2. 获取ResourceManager资源管理类
    const resourceMgr = context.resourceManager;
    // 3. 获取resource/rawfile/face.png
    const fileData = await resourceMgr.getRawFileContent(img_name)
    // 4. 获取图片的ArrayBuffer
    const buffer = fileData.buffer;
    // 5. 创建ImageSource实例
    const imageSource = image.createImageSource(buffer);
    // 6. 定义解码参数DecodingOptions
    let decodingOptions = {
      editable: true, // editable设置为true表示图片是可编辑的
      desiredPixelFormat: 3 // desiredPixelFormat设置为3表示解码的像素格式为RGBA_8888
    }
    // 7. 根据解码参数DecodingOptions获取PixelMap图片对象
    const pixelMap = await imageSource.createPixelMap(decodingOptions)
    // 8. 返回PixelMap图片对象
    return pixelMap;
  }

组件

(图片样式+代码) : 封装的组件则放在工具包下,可以使用 CTRL+F进行搜索 如: TextInputRectangleView

输入框

…矩形输入框

若有其他变动,可以修改参数即可

在这里插入图片描述

TextInputRectangleView({
        placeholder: '请输入手机号/邮箱',
        onClickCallBack: (value: string) => { // 回调函数
          this.name = value
        }
      })
…输入框堆叠效果(用于登录使用)

在这里插入图片描述

Stack({ alignContent: Alignment.End }) { // 布局
        TextInputRectangleView({
          placeholder: '请输入验证码',
          inPutType: InputType.Number,
          onClickCallBack: (value: string) => {
            this.code = value
          }
        })

        Text(this.message)
          .fontColor(Color.Red)
          .padding({ right: 20 })
          .enabled(this.isEnable)
          .opacity(this.isEnable ? 1 : 0.5)
          .onClick(() => {
          // 点击字体时的业务处理
            this.wait() // 延时
            this.getVaCode() // 校验代码
          })
      }.width("100%")


// 延时函数点击重新获取触发
  wait() {
    let time = 60
    let id = setInterval(() => {
      time--
      this.isEnable = false // 禁用的状态
      this.message = '重新获取(' + time + ")s"
      if (time <= 0) {
        this.isEnable = true
        this.message = '重新获取'
      }
    }
      , 1 * 1000)
  }

文本

…Text公用组件:TextView
@Component
export default  struct TextView {
  text:ResourceStr|number= ''
  fontColor:ResourceStr|Color = Color.Black
  fontSize:ResourceStr|number = 16
  fontWeight:number | FontWeight | string = FontWeight.Normal
  backgroundColor2:ResourceColor = Color.White
  decoration:TextDecorationType= TextDecorationType.None
  onClickCallBack ?:()=>void=()=>{} // 设置默认否则会导致,使用组件不调用导致闪退
  build() {
    Row(){
      Text(''+this.text)
        .fontColor(this.fontColor)
        .fontSize(this.fontSize)
        .fontWeight(this.fontWeight)
        .decoration({ type: this.decoration})
        .backgroundColor(this.backgroundColor2)
        .onClick(()=>{
          this.onClickCallBack()
        })
    }
  }
}

使用方式:

:
TextView({
          text: "详细信息",
          fontColor: "#ff2a24e5",
          fontSize: 25,
          fontWeight: FontWeight.Bold,
        })

按钮

…Button公用组件:ButtonView
@Component
export  default struct ButtonView {
  text:ResourceStr
  comWidth:number|string|Resource = "100%"
  comHeight:number|string|Resource = 50
  onClickCallback ?:()=>void=()=>{} // 回调函数
  type:ButtonType =ButtonType.Capsule // 默认胶囊
  backgroundColor2:ResourceColor = Color.Blue
  isEnabled:boolean = true // 启用
  build() {
    Column(){
      Button(this.text,{type:this.type})
        .width(this.comWidth)
        .height(this.comHeight)
        .onClick(()=>{
          this.onClickCallback()
        })
        .backgroundColor(this.backgroundColor2)
        .enabled(this.isEnabled)
    }
  }
}

使用方式:

例:
ButtonView({
                text: "修改",
                comWidth: 50,
                comHeight: 30,
                backgroundColor2: item.getUserType() === UserType.Admin ? Color.Grey : "#ff0f9107",
                type: ButtonType.Normal,
                isEnabled: item.getUserType() === UserType.Admin ? false : true,
                onClickCallback: () => {
         this.changUserState(Operate.UpdateUser, item)
                }
              })
…单选按钮公用组件:RadioView
import { Status } from '../entity/User'
@Component
export default struct RadioView {
  group:string='group'
  text: string
  radiosValue: Array<number>
  radiosStr: Array<string>
  checked: number = -1
  onClickCallBack ?:(value?:number)=>void=()=>{}
  isEnabled:Status = Status.Enable
  build() {
    Row({ space: 10 }) {
      if (this.text) {
        Text(this.text+':')
      }
      ForEach(this.radiosValue, (item: number, index: number) => {
        Row(){
        Radio({ value: item.toString(), group: this.group }) // 枚举值
          .onClick(() => {
            this.onClickCallBack(item)
          })
          .checked(this.checked == item)
          .enabled(!this.isEnabled) // false: 禁用
        Text(this.radiosStr[index]) // 对应的文本
        }.margin({right:10})
        .width("80")
      })
    }
  }
}

使用方式:(需要自己定义枚举)

:
Row() {
        RadioView(
          {
            group: 'sex',
            text: '性别',
            isEnabled: Status.Enable,
            radiosValue: [UserSex.Girl, UserSex.Boy],
            radiosStr: ['女', '男'],
            onClickCallBack: (val: number) => {
              this.user.setUserSex(val)
            }
          }
        )
      }.width("100%")

组件工具包

(图片样式+代码) ,结合组件UI进行使用

…输入框(矩形)

在这里插入图片描述

@Preview
@Component
  // 文本输入框矩形
export default struct TextInputRectangleView {
  text ?: string
  placeholder?: string
  inPutType ?: InputType = InputType.Normal
  onClickCallBack : (value: string) => void = () => { // 回调函数
  }
  width2?: ResourceStr | number = "100%"
  height2?: ResourceStr | number = 50
  backgroundColor2?: ResourceColor = "#ddd"
  borderRadius2 ?: number = 10

  build() {
    Row() {
      TextInput({ placeholder: this.placeholder, text: this.text })
        .type(this.inPutType)
        .onChange((val: string) => {
          this.onClickCallBack(val)
        })
        .backgroundColor(this.backgroundColor2)
        .placeholderColor(Color.Grey) // 提示信息颜色
    }.width(this.width2)
    .height(this.height2)
    .backgroundColor(this.backgroundColor2)
    .borderRadius(this.borderRadius2)
  }
}
…登录延时效果

在这里插入图片描述

  wait() {
    let time = 60
    let id = setInterval(() => {
      time--
      this.isEnable = false // 禁用的状态
      this.message = '重新获取(' + time + ")s"
      if (time <= 0) {
        this.isEnable = true
        this.message = '重新获取'
      }
    }
      , 1 * 1000)
  }
…UI分页查询的实现
1.UI渲染思路:(定义所需变量)
	@State page: number = 1 // 默认当前页
    @State totalPage: number = 1 // 总页数
    @State size: number = 10 // 显示业务---一般可以定义至常量: static  readonly  LIST_SIZE:number = 10
    
2.初始化数据:根据声明周期,获取数据和查询总数并计算总页数
	this.totalPage = 数据总数/ 每页显示数量size
	
3.UI和分页按钮的组建: (见例3)

4.UI加载页面优化: 为了提升用户体验,应显示加载动画,或者当没有数据时,提示暂无数据:  (见例4)

5.数据库查询分页公式:
	需要参数:
	const sizes = CommonConstants.LIST_SIZE // 每页显示数量
    const pages = (page - 1) * sizes // 计算查询列表数量在limit时使用
    格式:
   select * from tableName where ..... limit pages ,sizes  // 从第几个开始,显示多少sizes数量
:3

// 数据列表
ForEach(this.list, (item: User, index: number) => {
          // 仅能创建UI
          Row() {
            TextView({ text: item.getAccountName(),
              fontColor: Color.Blue,
              onClickCallBack: () => {
                // 自定义弹框页面
                // 存放位置目的主要获取item传参
                this.dialog = new CustomDialogController(
                  {
                    // 自定义组件 使用@CustomDialog装饰器 和  controller:CustomDialogController
                    builder: UserDetailDialog({ item: item }), //自定义组件或者函数,并可以传入想要的值
                    autoCancel: false, // 是否任意点击取消弹出框
                    alignment: DialogAlignment.Center, // 样式
                    cancel: () => {
                      // 自定义弹框页面
                      this.dialog.close()
                    }
                  }
                )
                // 开启
                this.dialog.open()
              }
            })
              .layoutWeight(1)

            TextView({ text: item.getUserType() ? '管理员' : '普通',
              fontColor: item.getUserType() ? Color.Red : Color.Grey
            })
              .layoutWeight(1)
              .align(Alignment.Center)

            TextView({ text: item.getUserState() ? '正常' : '禁用',
              fontColor: item.getUserState() ? Color.Grey : Color.Blue })
              .layoutWeight(1)
              .align(Alignment.Center)


            Row({ space: 5 }) {
              ButtonView({
                text: "删除",
                comWidth: 50,
                comHeight: 30,
                backgroundColor2: item.getUserType() === UserType.Admin ? Color.Grey : "#fff35454",
                type: ButtonType.Normal,
                isEnabled: item.getUserType() === UserType.Admin ? false : true,
                onClickCallback: () => {
                  this.changUserState(Operate.Delete, item)
                }
              })
              ButtonView({
                text: (item.getUserType() === UserType.Admin) ? '禁用' : ((item.getUserState()) ? '禁用' : '启用'),
                comWidth: 50,
                comHeight: 30,
                backgroundColor2: item.getUserType() === UserType.Admin ? Color.Grey : (item.getUserState() ? "#ffe3da46" : "#ff0b95e0"),
                type: ButtonType.Normal,
                isEnabled: item.getUserType() === UserType.Admin ? false : true,
                onClickCallback: () => {
                  item.setUserState(item.getUserState() ? UserState.Disable : UserState.Enable)
                  this.list[index] = new User() // 保证页面的即使刷新,用户感受
                  this.list[index] = item
                  this.changUserState(Operate.Update, item) // 处理业务
                }
              })
              ButtonView({
                text: "修改",
                comWidth: 50,
                comHeight: 30,
                backgroundColor2: item.getUserType() === UserType.Admin ? Color.Grey : "#ff0f9107",
                type: ButtonType.Normal,
                isEnabled: item.getUserType() === UserType.Admin ? false : true,
                onClickCallback: () => {
                  this.changUserState(Operate.UpdateUser, item)
                }
              })
            }
            .layoutWeight(2)
            .align(Alignment.Center)

          }.width("100%")
          .padding({ bottom: 5, top: 5 })
          .border({ width: { bottom: 1 }, color: { bottom: "#ddd" } })
        }

// ============================================================================== 
// 分页UI
        Row({ space: 30 }) {
          Row() {
            Text(`页数: ${this.page}/${this.totalPage}`)
              .fontColor("#ff0b95e0")
          }.padding({ left: 30 })

          Row({ space: 10 }) {
            ButtonView({
              text: "|<",
              comWidth: 50,
              comHeight: 30,
              backgroundColor2: "#ff0b95e0",
              type: ButtonType.Normal,
              onClickCallback: () => {
                if (this.page != 1) {
                  this.page = 1
                  this.onClickCallBack()
                }
              }
            })
            ButtonView({
              text: "<<",
              comWidth: 50,
              comHeight: 30,
              backgroundColor2: "#ff0b95e0",
              type: ButtonType.Normal,
              onClickCallback: () => {
                if (this.page > 1) {
                  this.page--
                  this.onClickCallBack()
                }
              }
            })
            ButtonView({
              text: ">>",
              comWidth: 50,
              comHeight: 30,
              backgroundColor2: "#ff0b95e0",
              type: ButtonType.Normal,
              onClickCallback: () => {
                if (this.page < this.totalPage) {
                  this.page++
                  this.onClickCallBack()
                }
              }
            })
            ButtonView({
              text: ">|",
              comWidth: 50,
              comHeight: 30,
              backgroundColor2: "#ff0b95e0",
              type: ButtonType.Normal,
              onClickCallback: () => {
                if (this.totalPage != this.page) {
                  this.page = this.totalPage
                  this.onClickCallBack()
                }
              }
            })
          }
        }
        .padding({ top: 20, right: 20 })
        .justifyContent(FlexAlign.SpaceBetween)
        .width("100%")
:4
if (this.loading) {
        // 加载
        LoadingProgress() // 加载动画
          .width(50)
          .height(50)
        Text("数据加载中.....")
          .fontColor("#bbb")
          .margin({top:20})
      } else if (this.list.length==0){
        Text("暂无数据")
          .fontColor("#bbb")
          .margin({top:20})
      } else  {
      	// 正常业务处理
      }
…弹框和短通知工具包
import promptAction from '@ohos.promptAction'
// 短通知
export  function showToast(info:string,time:number = 3*1000){
  promptAction.showToast({message:info,duration:time})
}

// 消息弹窗
export  function  showDialog(msg:string,title:string='系统提示',callback:()=>void=()=>{}){ // callback 赋值的为默认值
  promptAction.showDialog({
    title:title,
    message:msg
  }).catch(callback)
}

// 确认弹框
export  function  AlterDialog(title:string,message:string,submit:()=>void=()=>{},cancel:()=>void=()=>{}){
  AlertDialog.show({
    title:title,
    message:message,
    primaryButton:{
      value:"取消",
      action:()=>{
        cancel()
      }
    },
    secondaryButton:{
      value:'确定',
      action: () => {
        submit()
      }
    },
  })
}

// log和err日志
export  function  loggerInfo(str:string){
	console.info('system===>',str)
}
export  function  loggerError(str:string){
	console.error('system===>',str)
}

成形UI

…聊天界面UI

在这里插入图片描述

思路:

代码组件

类、接口、枚举工具包

进程间通讯(IPC)

import commonEventManager from '@ohos.commonEventManager'


// Publisher通讯事件类型
enum PublishEventType {
  APP_PUBLISH = "APP_PUBLISH",
  CARD_PUBLISH = "CARD_PUBLISH"
}


class IPCManagerClass {
  static publishCount:number = 1
  // 发布者
  static publish(eventType:PublishEventType,data:string){
    // commonEventManager作用:可用于进程间通讯
    commonEventManager.publish(eventType,{data},(err)=>{
      if(err){
        // 失败只发3次
        if(this.publishCount<=3){
          this.publish(eventType,data)
        }else{
          this.publishCount = 1
        }
      }else{
        this.publishCount = 1
      }
    })
  }
  // 订阅者
  static subscribe(eventType:PublishEventType,subscriber,callback:(event:string)=>void){
    commonEventManager.createSubscriber({ events: [eventType] }, (err, data) => {
      if (err) {
        return console.log('common-->', `创建订阅者error ${JSON.stringify(err)}`)
      }
      console.log('common-->', `创建订阅者success`)
      subscriber = data
      if (subscriber !== null) {
        //订阅事件
        commonEventManager.subscribe(subscriber, (err, data) => {
          if (err) {
            return console.error(`logData`, '订阅事件失败')
          }
          console.log('common-->',`接受订阅事件:${data.data}`)
          callback(data.data)
        })
      } else {
        console.error('common-->',`需要创建subscriber`);
      }
    })
  }
}

export {IPCManagerClass,PublishEventType}

设计模式

单例模式

单例模式分为: 懒汉式和饿汉式,懒汉式常用。
创建步骤:
1.将构造器私有化:   private constructor() {} // 可以修改为传参,定义一个私有属性,在new Class时,赋值即可
2.定义私有静态实例,但不创建:  private static clsss: Class
3.创建共用的静态函数:
public static initialization():Class{
if(!clsss){
Class.class = new Class() // 不存在是才会创建
//若有参数 Class.class = new Class('参数') // 不存在是才会创建
}
return Class.class
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大众筹码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值