纯血鸿蒙APP实战开发——自定义安全键盘案例

news2024/11/26 0:48:48

介绍

金融类应用在密码输入时,一般会使用自定义安全键盘。本示例介绍如何使用TextInput组件实现自定义安全键盘场景,主要包括TextInput.customKeyboard绑定自定义键盘、自定义键盘布局和状态更新等知识点。

效果图预览

实现思路

1. 使用TextInput的customKeyboard的属性方法来设置自定义键盘

当设置自定义键盘时,输入框激活后不会打开系统输入法,而是加载应用指定的自定义组件,针对系统键盘的enterKeyType属性设置将无效。自定义键盘采用覆盖原始界面的方式呈现,不会对应用原始界面产生压缩或者上提。默认在输入控件失去焦点时,关闭自定义键盘,开发者也可以通过TextInputController.stopEditing方法控制键盘关闭。

2. 自定义键盘布局

键盘枚举类型:

  • 键盘类型分为数字键盘,大写、小写键盘,特殊字符键盘
  • 键盘按键类型分为输入操作INPUT、删除操作DELETE、切换数字键盘操作NUMERIC、切换大小写键盘CAPSLOCK、切换数字键盘SPECIAL共五种类型
/**
 * 键盘类型枚举
 */
export enum EKeyboardType {
  NUMERIC,    //数字键盘
  UPPERCASE,  // 大写字母键盘
  LOWERCASE,  // 小写字母键盘
  SPECIAL,    // 特殊字符键盘
}

/**
 * 键盘按键类型枚举
 */
export enum EKeyType {
  INPUT,   // 输入类型,输入具体的值
  DELETE,  // 删除一个输入字符
  NUMERIC, // 切换数字键盘
  CAPSLOCK, // 切换大小写键盘
  SPECIAL, //  切换特殊字符键盘
}

在真实业务场景下,自定义安全键盘数据包括值、UI属性、位置等都通过数据请求来下发,键盘按键数据接口定义如下:


/**
 * 键盘按键数据接口
 */
export interface IKeyAttribute {
  label: string | Resource;
  value?: string;
  type?: EKeyType;
  fontSize?: number;
  fontColor?: string | Color;
  backgroundColor?: string | Color;
  position?: [number, number, number, number];
}

自定义键盘布局:分为标题栏和键盘两部分,键盘使用Grid布局,每个按键GridItem的值、UI属性和位置都通过数据请求下发,不需要额外计算。

数字键盘为4*3的网格布局,但是大小写键盘和特殊字符键盘的布局为不规则布局,如果设置为4 * 10的网格,有的按键占用1 * 1.5,但是GridItem属性不支持占用非整数列。本文将该场景下将网格拆分为更小的单元,为4 * 20网格布局,每个字母按键占1 * 2,删除按键则占1 * 3,空格则占1 * 10,这样就保证每个按键都要占用整数单元。

Column() {
  this.titleBar();

  Grid() {
    ForEach(this.items, (item: IKeyAttribute) => {
      GridItem() {
        this.myGridItem(item)
      }
      .width('100%')
      .height(this.itemHeight)
      .rowStart(item?.position?.[0])
      .columnEnd(item?.position?.[1])
      .columnStart(item?.position?.[2])
      .columnEnd(item?.position?.[3])
      .backgroundColor(item.backgroundColor)
      .borderRadius($r("app.integer.key_border_radius"))
      .onClick(() => {
            ....
      })
    }, (item: IKeyAttribute, index: number) => JSON.stringify(item) + index)
  }
  .margin({ bottom: $r("app.integer.key_board_marin_bottom") })
  .columnsTemplate(this.curKeyboardType === EKeyboardType.NUMERIC ? "1fr 1fr 1fr" :
    "1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr")
  .rowsTemplate("1fr 1fr 1fr 1fr") // Grid高度均分成4份
  .rowsGap(this.rowSpace) // 设置行间距
  .columnsGap(this.columnSpace) // 设置列间距
  .width('100%')
  .height(this.itemHeight * this.rowCount + this.rowSpace * (this.rowCount - 1))
}
.width('100%')
.padding({ left: this.columnSpace, right: this.columnSpace })
.backgroundColor(Color.Black)
}
3. 状态更新

主要是子组件自定义键盘的按键事件如何传递到父组件,可以在父组件定义好键盘按键事件响应函数onKeyboardEvent,传递给子组件,然后子组件按键时调用父组件传递过来的onKeyboardEvent即可。需要注意的是,在子组件中,必须定义inputValue且使用@Link装饰器,这样能保证子组件调用时onKeyboardEvent时inputValue不为空,父子组件数据双向更新。

@Component
export struct CustomSafeKeyboardView {
  @State inputValue: string = '';
  @State items: IKeyAttribute[] = numericKeyData;
  @State curKeyboardType: EKeyboardType = EKeyboardType.NUMERIC;
  controller: TextInputController = new TextInputController();

  /**
   * 键盘按键事件响应函数
   * @param item
   */
  onKeyboardEvent(item: IKeyAttribute) {
    switch (item.type) {
      // 输入类型,更新输入内容
      case EKeyType.INPUT:
        this.inputValue += item.value;
        break;
      // 删除一个已输入的末尾字符
      case EKeyType.DELETE:
        this.inputValue = this.inputValue.slice(0, -1);
        break;
      // 切换数字字符键盘
      case EKeyType.NUMERIC:
        if (this.curKeyboardType !== EKeyboardType.NUMERIC) {
          this.curKeyboardType = EKeyboardType.NUMERIC;
          this.items = numericKeyData;
        }
        break;
      // 切换大小写
      case EKeyType.CAPSLOCK:
        if (this.curKeyboardType === EKeyboardType.LOWERCASE) {
          // 切换大写字母键盘
          this.curKeyboardType = EKeyboardType.UPPERCASE;
          this.items = upperCaseKeyData;
        } else {
          // 切换小写字母键盘
          this.curKeyboardType = EKeyboardType.LOWERCASE;
          this.items = lowerCaseKeyData;
        }
        break;
      // 切换特殊字符键盘
      case EKeyType.SPECIAL:
        if (this.curKeyboardType !== EKeyboardType.SPECIAL) {
          this.curKeyboardType = EKeyboardType.SPECIAL;
          this.items = specialKeyData;
        }
        break;
      default:
        console.info(`Sorry, we are out of input type.`);
    }
  }

  /**
   * 自定义键盘组件Builder
   */
  @Builder
  customKeyboardBuilder() {
    CustomKeyboard({
      items: this.items,
      inputValue: this.inputValue,
      curKeyboardType: this.curKeyboardType,
      onKeyboardEvent: this.onKeyboardEvent,
      controller: this.controller
    })
  }

  build() {
    Column() {
      Row()
        .height($r("app.integer.row_height"))

      Image($r("app.media.avatar"))
        .width($r("app.integer.avatar_weight"))
        .height($r("app.integer.avatar_height"))
        .objectFit(ImageFit.Fill)

      Text($r("app.string.account_name"))
        .fontSize($r("app.integer.text_font_size"))
        .margin({ top: $r("app.integer.common_margin_padding") })

      TextInput({
        text: this.inputValue,
        placeholder: $r("app.string.placeholder"),
        controller: this.controller
      })// 绑定自定义键盘
        .type(InputType.Password)
        .customKeyboard(this.customKeyboardBuilder())// 绑定自定义安全键盘
        .height($r("app.integer.text_input_height"))
        .border(null)
        .margin({ top: $r("app.integer.common_margin_padding") })

      Button($r("app.string.login_button_label"))
        .type(ButtonType.Capsule)
        .fontSize($r("app.integer.login_button_font_size"))
        .width($r("app.integer.login_button_width"))
        .height($r("app.integer.login_button_height"))
        .margin({ top: $r("app.integer.login_button_margin") })
        .backgroundColor(Color.Pink)
        .onClick(() => {
          this.controller.stopEditing();
        })

    }
    .width($r("app.string.one_hundred_percent"))
    .height($r("app.string.one_hundred_percent"))
    .padding($r("app.integer.common_margin_padding"))
  }
}

高性能知识点

不涉及

工程结构&模块类型

customsafekeyboard              // har类型
|---components                  // 自定义组件
|   ---CustomKeyboard.ets  
|---model                       // 模型层
|   ---Constants                // 定义常量数据
|---CustomSafeKeyboardView.ets  // 主页面

模块依赖

  1. 依赖common模块来实现日志的打印
  2. 依赖路由模块,供entry模块实现路由导航

参考资料

TextInput

鸿蒙全栈开发全新学习指南

也为了积极培养鸿蒙生态人才,让大家都能学习到鸿蒙开发最新的技术,针对一些在职人员、0基础小白、应届生/计算机专业、鸿蒙爱好者等人群,整理了一套纯血版鸿蒙(HarmonyOS Next)全栈开发技术的学习路线【包含了大厂APP实战项目开发】

本路线共分为四个阶段:

第一阶段:鸿蒙初中级开发必备技能

第二阶段:鸿蒙南北双向高工技能基础:gitee.com/MNxiaona/733GH

第三阶段:应用开发中高级就业技术

第四阶段:全网首发-工业级南向设备开发就业技术:https://gitee.com/MNxiaona/733GH

《鸿蒙 (Harmony OS)开发学习手册》(共计892页)

如何快速入门?

1.基本概念
2.构建第一个ArkTS应用
3.……

开发基础知识:gitee.com/MNxiaona/733GH

1.应用基础知识
2.配置文件
3.应用数据管理
4.应用安全管理
5.应用隐私保护
6.三方应用调用管控机制
7.资源分类与访问
8.学习ArkTS语言
9.……

基于ArkTS 开发

1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……

鸿蒙开发面试真题(含参考答案):gitee.com/MNxiaona/733GH

鸿蒙入门教学视频:

美团APP实战开发教学:gitee.com/MNxiaona/733GH

写在最后

  • 如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
  • 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
  • 关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。
  • 想要获取更多完整鸿蒙最新学习资源,请移步前往小编:gitee.com/MNxiaona/733GH

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1651141.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

为什么你的企业需要微信小程序?制作微信小程序有什么好处?

什么是小程序? WeChat小程序作为更大的WeChat生态系统中的子应用程序。它们就像更小、更基本的应用程序,在更大的应用程序(WeChat)中运行。这些程序为用户提供了额外的高级功能,以便在使用WeChat服务时加以利用。根据…

linux系统 虚拟机的安装详细步骤

window: (1) 个人:win7 win10 win11 winxp (2)服务器:windows server2003 2008 2013 linux: (1)centos7 5 6 8 (2)redhat (3)ubuntu (4)kali 什么是linux: 主要是基于命令来完成各种操作,类似于DO…

0基础学PHP有多难?

php作为web端最佳的开发语言,没有华而不实,而是经受住了时间考验,是一门非常值得学习的编程语言。 目前市场上各种网站、管理系统、小程序、APP等,基本都是使用PHP开发的,也侧面反映了PHP的需求以及学习的必要性&…

UTONMOS:真正的“游戏元宇宙”还有多遥远?

元宇宙来源于科幻小说的概念,已成为真实世界中的流行语。围绕这一新兴概念,一场产、学、研的实践正在展开。 数字化转型中,元宇宙能否担当大任?这些新概念在中国语境下如何落地?本文将深入挖掘国内元宇宙游戏产业的发…

数据结构-线性表-应用题-2.2-6

从有序顺序表中删除所有其值重复的元素,使表中的元素的值均不同 有序顺序表,值相同的元素一定在连续的位置上,初始时将第一个元素是为非重复的有序表,之后依次判断后面的元素是否与前面的非重复表的最后一个元素相同,…

当AI遇见现实:数智化时代的人类社会新图景

文章目录 一、数智化时代的机遇二、数智化时代的挑战三、如何适应数智化时代《图解数据智能》内容简介作者简介精彩书评目录精彩书摘强化学习什么是强化学习强化学习与监督学习的区别强化学习与无监督学习的区别 前言/序言 随着科技的日新月异,我们步入了一个前所未…

大数据硬核技能进阶:Spark3实战智能物业运营系统

Spark Streaming 是 Spark 的一个子模块,用于快速构建可扩展,高吞吐量,高容错的流处理程序。具有以下特点: 大数据硬核技能进阶:Spark3实战智能物业运营系统(网盘超清) 通过高级 API 构建应用程序,简单易…

【详细介绍下图搜索算法】

🎥博主:程序员不想YY啊 💫CSDN优质创作者,CSDN实力新星,CSDN博客专家 🤗点赞🎈收藏⭐再看💫养成习惯 ✨希望本文对您有所裨益,如有不足之处,欢迎在评论区提出…

技术分享-上海泗博MPI转以太网模块MPI-131实现Node-RED直接访问西门子PLC数据

上海泗博自动化MPI-131是一款用于西门子S7系列PLC(包括S7-200、S7-300、S7-400)以及西门子数控机床(如840D、840DSL等)的以太网通讯模块,无需编程,即插即用,支持通过模块上下载PLC程序和数据监控…

翻译《The Old New Thing》 - What is the deal with the ES_OEMCONVERT flag?

What is the deal with the ES_OEMCONVERT flag? - The Old New Thinghttps://devblogs.microsoft.com/oldnewthing/20050719-12/?p34893 Raymond Chen 在 2005年07月19日 ES_OEMCONVERT 标志是怎么回事? 简要 文章讨论了 ES_OEMCONVERT 编辑控件风格的起源和用途…

DNS域名解析服务的部署及优化方案

实验要求: 1.配置2台服务器要求如下: a)服务器1: 主机名:dns-master.timinglee.org ip地址: 172.25.254.100 配置好软件仓库 b)服务器2: 主机名:dns-slave.timinglee.org ip地址&am…

element-plus el-time-picker 时间段选择(可多选)

实现一个如图的时间段选择器 处理好时间回显逻辑&#xff0c;组件内[‘’,‘’],后端数据[{startTime:‘’,endTime:‘’}]处理好加和减的显示逻辑 <template><div><div v-for"(item, index) in currentChoose" :key"index" class"fl…

Linux实操之常用指令详解

文章目录 vi 和 vimvi 和 vim 基本使用 开机、重启和用户登录注销关机&重启命令用户登录和注销 用户管理基本介绍基本语法细节说明修改密码删除用户查询用户信息指令切换用户查看当前用户/登录用户用户组 实用指令指定运行级别帮助指令文件目录类时间和日期类搜索查找类压缩…

向各位请教一个问题

这是菜鸟上的一道题目&#xff0c;单单拿出来问问大家&#xff0c;看看能不能解惑 &#xff0c;谢谢各位&#xff01; 题目25&#xff1a;求12!3!...20!的和 解题思路&#xff1a;这个题不知道为什么我用DEV C 5.11显示出来为0.000000&#xff0c;可能版本有问题&#xff1f;&a…

装饰器模式-原理分析以及动手练习

目录 应用场景涉及的角色和类&#xff08;个人理解&#xff09;涉及的角色组件&#xff08;标准&#xff09;基本实现 Demo&#xff08;可以直接 copy 跑一下看效果&#xff09;自己动手实战需求参考答案 相关话题参考文章 应用场景 需要给一个现有类添加附加功能&#xff0c;…

如何安全可控地进行内外网跨网络传输文件?

跨网络传输文件通常指的是在不同的网络环境之间移动数据&#xff0c;这在现代企业运营中非常常见。随着网络技术的高速发展&#xff0c;为了有效地保护内部的核心数据资产&#xff0c;企业一般会采用内外网隔离的策略。在进行内外网跨网络传输文件时&#xff0c;需要考虑多种因…

Java性能优化(一):Java基础-ArrayList和LinkedList

引言 集合作为一种存储数据的容器&#xff0c;是我们日常开发中使用最频繁的对象类型之一。JDK为开发者提供了一系列的集合类型&#xff0c;这些集合类型使用不同的数据结构来实现。因此&#xff0c;不同的集合类型&#xff0c;使用场景也不同。 很多同学在面试的时候&#x…

2024爆火的AI设备Rabbit R1到底是什么?有人说它是AI的iPhone时刻,有人说它是套壳的安卓

大家好&#xff0c;我是木易&#xff0c;一个持续关注AI领域的互联网技术产品经理&#xff0c;国内Top2本科&#xff0c;美国Top10 CS研究生&#xff0c;MBA。我坚信AI是普通人变强的“外挂”&#xff0c;所以创建了“AI信息Gap”这个公众号&#xff0c;专注于分享AI全维度知识…

22_Scala集合Seq

文章目录 Seq序列1.构建集合2.List集合元素拼接&&集合拼接3.可变Seq&&List3.1 ListBuffer创建3.2 增删改查3.3 相互转化 Appendix1.Scala起别名2.Seq底层3.关于运算符操作: :4.空集合的表示 Seq序列 –Seq表示有序&#xff0c;数据可重复的集合 1.构建集合 …

IOS离线打包uniapp的信息时报错如下的解决方法

IOS离线打包uniapp的信息时报错如下的解决方法 问题描述&#xff1a; Extract app intents metadata 0.1 seconds XExtractAppIntentsMetadata(in target HBuilder from project HBuilder-Hello)cd /Users/whb/space/vpt/vptios/HBuilder-Hello/Applications/Xcode.app/Conte…