找回密码
 立即注册
首页 业界区 业界 鸿蒙应用开发UI基础第十五节:文本输入组件核心讲解与实 ...

鸿蒙应用开发UI基础第十五节:文本输入组件核心讲解与实战

赖珊 昨天 13:35
【学习目标】


  • 区分TextInput、TextArea、Search三大组件的核心差异,掌握精准的场景选型逻辑;
  • 掌握三大组件的基础创建、输入类型配置、样式定制,适配各类输入开发场景;
  • 熟练绑定核心通用事件,实现输入数据的获取与业务处理;
  • 掌握Search专属能力与三大组件控制器的精细控制用法;
  • 掌握输入内容过滤的核心实现方式,保证输入内容合法性;
  • 具备输入校验、键盘避让等实战能力,独立完成登录、搜索等高频场景开发;
  • 能结合业务灵活组合组件,从功能和视觉层面优化用户输入体验。
一、文本输入类组件核心认知

(一)组件整体定位

TextInput、TextArea、Search是鸿蒙ArkTS核心文本输入类组件,基于统一输入底层能力封装,支持通用样式与高频事件;针对单行短文本、多行长文本、搜索专属三大场景做差异化优化,是移动端应用开发必备基础组件。
(二)核心选型原则与组件差异

核心原则:按输入形态和业务场景选型,禁止跨场景混用。三者核心差异、专属优势与适配场景如下:
组件名核心特性专属优势适用场景TextInput单行输入,不自动折行输入模式丰富,控制器光标/选框控制能力精细账号、密码、手机号、验证码等短文本输入TextArea多行输入,自动折行,支持滚动适配长文本,可配置自动高度/滚动条,支持长文本编辑控制评论、留言、文章编辑、长备注填写Search单行输入,搜索场景专属内置搜索/清除图标,默认回车搜索逻辑,降低自定义开发成本全局搜索、商品检索、页面内搜索栏(三)核心绑定规范与组件接口

各组件对应专属构造函数,彼此独立不通用,核心接口与绑定规则如下:
组件名专属构造函数接口核心可选属性TextInputTextInputOptionstext: ResourceStr、controller: TextInputController、placeholder: ResourceStrTextAreaTextAreaOptionstext: ResourceStr、controller: TextAreaController、placeholder: ResourceStrSearchSearchOptionsvalue: ResourceStr、controller: SearchController、placeholder: ResourceStr、icon:string
说明:ResourceStr为联合类型(Resource | string),支持直接传入字符串或通过$r引用系统/应用资源。
二、核心能力

(一)通用事件

TextInput、TextArea、Search事件触发逻辑统一,仅onSubmit回调参数存在组件差异,其余事件参数完全一致:
事件名称功能说明onChange输入内容实时变化触发,用于实时校验、字数统计、数据同步、自定义过滤onSubmit按下回车/搜索键触发(Search支持点击搜索按钮),用于表单提交、搜索请求、内容发布onFocus组件获得焦点触发,用于样式高亮、提示展示、键盘唤起前置处理onBlur组件失去焦点触发,用于格式校验、样式恢复、草稿自动保存onCopy复制内容触发,用于复制监控、敏感内容脱敏、操作日志记录onCut剪切内容触发,用于剪切行为监控、自定义剪切逻辑onPaste粘贴内容触发,用于粘贴内容校验、长度限制、格式过滤onEditChange编辑状态变化触发,用于编辑状态监控、键盘显隐联动onTextSelectionChange光标/文本选中范围变化触发,用于选中内容处理、光标位置联动onContentScroll内容滚动触发,用于长文本滚动监控、联动布局调整onWillInsert系统输入法插入内容前触发,用于前置校验、非法内容拦截onDidInsert内容插入完成触发,用于格式后置修正、数据同步onWillDelete内容删除前触发,用于关键内容防删、删除确认onDidDelete内容删除完成触发,用于空白内容兜底、状态同步onWillChange文本即将变更触发(时序晚于增删事件),用于全局内容拦截、复合规则校验
时序说明:onWillChange执行时序晚于onWillInsert/onWillDelete,早于onDidInsert/onDidDelete。
(二)输入内容过滤

三大组件支持统一过滤方案,Search仅需将text绑定替换为value,过滤逻辑可直接复用:

  • inputFilter:原生正则过滤,轻量高效,适用于基础字符级规则
  • onWillChange:内容变更前拦截,支持自定义逻辑,适用于位置、字节、自动修正等复杂场景。
1. inputFilter 原生正则过滤

仅需编写允许的字符集,无需添加^$首尾限定符,非法字符直接拦截:
  1. // 仅允许字母/数字(账号/验证码场景)
  2. TextInput({ text: this.content, placeholder: '请输入字母/数字' })
  3.   .inputFilter('[0-9A-Za-z]', (filtered) => {
  4.     console.log('过滤的非法内容:', filtered);
  5.   })
复制代码
2. onWillChange 自定义逻辑过滤

仅支持返回boolean类型(true允许本次内容变更,false拦截变更,界面不更新),如需实现内容自动修正,需手动更新绑定的状态变量并返回false拦截原始输入,覆盖复杂业务规则:
  1. // 首位禁止输入空格
  2. TextInput({ text: this.inputText, placeholder: '首位无空格' })
  3. .onWillChange((changeInfo:EditableTextChangeValue)=>{
  4.     return changeInfo.content.trimStart() !== "";
  5.   })
复制代码
3. 组合过滤(生产高频用法)

inputFilter做底层字符限制 + onWillChange做业务规则校验,兼顾性能与需求:
  1. // 账号规则:仅字母/数字 + 首位不能为数字
  2. TextInput({ text: this.account, placeholder: '字母开头,字母/数字组合' })
  3.   .inputFilter('[0-9A-Za-z]')
  4.   .onWillChange((newValue) => {
  5.     return newValue.content.replace(/^[0-9]/, '') !== "";
  6.   });
复制代码
(三)组件控制器

所有控制器继承自TextContentControllerBase,必须与组件一对一绑定,禁止跨组件混用,用于光标、选框、编辑状态精细控制。
1. 控制器-组件对应关系

输入组件专属控制器核心能力TextInputTextInputController单行文本光标定位、选框控制、退出编辑态TextAreaTextAreaController多行文本光标定位、选框控制、长文本编辑适配SearchSearchController继承TextInputController能力,适配搜索框控制2. 通用核心方法

方法名称功能说明典型场景caretPosition(pos: number): void设置光标位置(索引从0开始)提交后光标归位、验证码输入后光标跳转setTextSelection(start: number, end: number): void获焦状态下设置文本选中区域快速选中错误文本、批量编辑前置操作stopEditing(): void退出编辑态,关闭自定义键盘自定义键盘手动关闭、提交后退出编辑3. 控制器基础使用方法
  1. private inputController = new TextInputController();
  2. // 光标移至文本开头
  3. this.inputController.caretPosition(0);
  4. // 选中0~6位字符
  5. this.inputController.setTextSelection(0, 6);
  6. // 主动退出编辑状态
  7. this.inputController.stopEditing();
复制代码
(四)全局焦点控制

焦点控制为页面级能力,与组件控制器解耦,是鸿蒙标准焦点管理方案:
1. 核心方法

核心方法功能说明参数/场景requestFocus(id: string): boolean指定ID组件获取焦点,唤起输入法参数:组件唯一ID;场景:页面自动聚焦、校验失败定位clearFocus(): void清除全页面焦点,收起软键盘无参数;场景:点击空白处、提交/搜索完成2. 标准使用代码
  1. // 获取全局焦点控制器
  2. const focusController = this.getUIContext().getFocusController();
  3. // 精准聚焦(组件必须绑定id)
  4. focusController.requestFocus("account_input");
  5. // 全局失焦,收起键盘
  6. focusController.clearFocus();
复制代码
注意:调用requestFocus前,必须为目标组件设置唯一.id('xxx'),否则聚焦失效。
(五)专属输入模式枚举

强制规范:各组件仅可使用自身专属枚举,严禁跨组件混用。
1. TextInput 专属:InputType

枚举成员功能说明适用场景Normal基础通用输入用户名、普通文本Number纯数字输入验证码、订单号PhoneNumber电话格式输入手机号、座机号Email邮箱格式输入邮箱登录/注册表单Password密码隐藏输入账号登录密码NUMBER_PASSWORD纯数字密码支付密码、锁屏密码USER_NAME用户名专属,支持密码库填充账号登录/注册NEW_PASSWORD新密码,支持强度校验密码重置、新用户注册NUMBER_DECIMAL带一位小数点数字金额、身高、体重URL网址格式输入链接填写、校验ONE_TIME_CODE一次性验证码(API20+)短信验证码输入2. TextArea 专属:TextAreaType

枚举成员功能说明适用场景NORMAL基础多行输入评论、留言、长文本NUMBER纯数字多行输入长数字序列、数字备注PHONE_NUMBER电话格式多行输入批量号码录入EMAIL邮箱格式多行输入邮箱批量录入NUMBER_DECIMAL带小数点数字长文本内含数值URL网址格式长文本内含链接ONE_TIME_CODE一次性验证码(API20+)验证码批量录入3. Search 专属:SearchType

枚举成员功能说明适用场景NORMAL通用搜索输入全文、关键词检索NUMBER纯数字搜索订单号、快递号、ID检索PHONE_NUMBER电话格式搜索手机号、联系电话检索EMAIL邮箱格式搜索用户邮箱检索NUMBER_DECIMAL小数搜索金额、数值区间检索URL网址搜索链接、外链检索ONE_TIME_CODE验证码检索校验码信息检索(六)通用回车键类型:EnterKeyType

三大组件通用枚举,自定义软键盘回车按钮样式与语义,触发后均执行onSubmit:
枚举成员键盘显示语义核心场景Go前往/箭头执行操作、页面跳转单输入框提交、密码框确认Search搜索/放大镜搜索触发Search组件、搜索栏Send发送/纸飞机内容发送聊天、评论快速发送Next下一个/右箭头切换下一个输入框表单连续输入(账号→密码→验证码)Done完成/对勾结束输入、收起键盘长文本编辑完成、表单最后一项输入PREVIOUS上一个/左箭头切换上一个输入框表单反向连续输入(验证码→密码→账号)NEW_LINE回车/换行换行/确认TextArea长文本换行、多行内容编辑三、各组件专属能力

(一)Search 组件专属能力

基于TextInput封装,聚焦搜索场景,提供原生图标与按钮配置:
属性名称功能说明实战场景searchButton右侧搜索按钮配置(文字/样式),点击触发onSubmit搜索栏内置提交按钮,无需自定义searchIcon自定义左侧搜索图标(尺寸、颜色、资源)统一APP图标风格,适配深色模式cancelButton清除按钮配置(显隐规则、图标)输入后一键清空内容(二)TextArea 组件专属能力

聚焦多行长文本,提供排版与高度控制能力:
属性名称功能说明实战场景minLines/maxLines最小/最大行数,支持溢出滚动/截断评论区3~5行,超出滚动lineSpacing行间距,支持仅行间生效长文本排版优化,提升可读性ellipsisMode超长文本省略位置(首/中/尾)非编辑态长文本预览heightAdaptivePolicy高度自适应策略动态适配输入内容高度(三)TextInput 组件专属能力

聚焦单行表单/密码场景,提供表单专属样式与交互:
1. 专属属性

属性名称功能说明实战场景showUnderline开启下划线样式,替代常规边框表单输入框简约下划线风格underlineColor配置多状态下划线颜色(常态/聚焦/错误/禁用)输入态高亮、错误态标红showPassword/showPasswordIcon密码显隐开关 + 显隐图标控制密码框一键切换可见/隐藏showUnit输入框后置单位展示(需配合下划线)金额框显示「元」、手机号框标注用途showError绑定错误提示文本,自动展示/隐藏表单校验失败实时提示passwordRules密码生成规则,透传密码保险箱新密码输入自动生成合规密码2. 专属事件

事件名称功能说明实战场景onSecurityStateChange密码显隐状态切换回调同步图标状态、全局显隐联动四、工程结构

基于鸿蒙6.0 API 20、Stage模型创建InputApplication工程,标准目录结构如下:
  1. InputApplication/
  2. ├── AppScope/
  3. │   └── app.json5
  4. ├── entry/
  5. │   ├── src/
  6. │   │   ├── main/
  7. │   │   │   ├── ets/
  8. │   │   │   │   ├── entryability/
  9. │   │   │   │   │   └── EntryAbility.ets
  10. │   │   │   │   ├── pages/
  11. │   │   │   │   │   ├── Index.ets                  // 导航主页面
  12. │   │   │   │   │   ├── InputBasicPage.ets         // 示例1:三大组件基础用法
  13. │   │   │   │   │   ├── RegisterFormPage.ets       // 示例2:注册表单实战
  14. │   │   │   │   │   └── InputSearchBarPage.ets     // 示例3:顶部搜索栏实战
  15. │   │   │   │   │   └── CommentAreaPage.ets     // 示例4:评论区实战
  16. │   │   │   ├── resources/
  17. │   │   │   │   └── media/                        // 自定义图标:icon_back、search、icon_clear
  18. │   │   │   └── module.json5
  19. │   │   └── build-profile.json5
  20. └── build-profile.json5
复制代码
五、导航主页面(Index.ets)
  1. import { router } from '@kit.ArkUI';
  2. interface RouterButton {
  3.   title: string;
  4.   url: string;
  5. }
  6. @Entry
  7. @Component
  8. struct Index {
  9.   private buttonList: RouterButton[] = [
  10.     { title: "示例1:三大组件基础用法", url: 'pages/InputBasicPage' },
  11.     { title: "示例2:标准注册表单实战", url: 'pages/RegisterFormPage' },
  12.     { title: "示例3:顶部搜索栏实战", url: 'pages/InputSearchBarPage' },
  13.     { title: "示例4:发布评论实战", url: 'pages/CommentAreaPage' },
  14.   ];
  15.   build() {
  16.     Column({ space: 12 }) {
  17.       Text("TextInput/TextArea/Search")
  18.         .fontSize(30)
  19.         .fontWeight(FontWeight.Bold)
  20.         .margin({ bottom: 30 });
  21.       ForEach(
  22.         this.buttonList,
  23.         (item: RouterButton) => {
  24.           Button(item.title)
  25.             .width('85%')
  26.             .height(42)
  27.             .backgroundColor($r('sys.color.brand'))
  28.             .fontColor(Color.White)
  29.             .borderRadius(8)
  30.             .fontSize(15)
  31.             .onClick(() => router.pushUrl({ url: item.url }));
  32.         },
  33.         (item: RouterButton) => item.url
  34.       );
  35.     }
  36.     .width('100%')
  37.     .height('100%')
  38.     .justifyContent(FlexAlign.Center)
  39.     .backgroundColor('#F5F5F5');
  40.   }
  41. }
复制代码
运行效果

1.png

六、示例1:三大组件基础用法(InputBasicPage.ets)

核心掌握点


  • 区分TextInput/TextArea的text绑定与Search的value绑定;
  • 正确使用组件专属输入类型枚举;
  • 完成核心事件绑定与数据实时同步;
  • 控制器一对一绑定规范;
  • TextArea行数、行间距、溢出等长文本配置。
  1. import { LengthMetrics } from '@kit.ArkUI';
  2. @Entry
  3. @Component
  4. struct InputBasicPage {
  5.   @State textInputStr: string = '';
  6.   @State textAreaStr: string = '';
  7.   @State searchStr: string = '';
  8.   private textInputController = new TextInputController();
  9.   private textAreaController = new TextAreaController();
  10.   private searchController = new SearchController();
  11.   build() {
  12.     Column({ space: 25 }) {
  13.       Text("三大组件基础用法")
  14.         .fontSize(24)
  15.         .fontWeight(FontWeight.Bold)
  16.         .width('100%')
  17.         .textAlign(TextAlign.Center);
  18.       // 1. TextInput 手机号输入
  19.       TextInput({
  20.         placeholder: '请输入11位手机号',
  21.         controller: this.textInputController,
  22.         text: this.textInputStr
  23.       })
  24.         .type(InputType.PhoneNumber)
  25.         .maxLength(11)
  26.         .width('90%')
  27.         .height(50)
  28.         .padding(15)
  29.         .backgroundColor('#F5F5F5')
  30.         .borderRadius(8)
  31.         .shadow({ radius: 2, color: '#00000010' })
  32.         .onChange((value) => {
  33.           this.textInputStr = value;
  34.         });
  35.       // 2. TextArea 评论输入
  36.       TextArea({
  37.         placeholder: '请输入你的评论,最多200字...',
  38.         controller: this.textAreaController,
  39.         text: this.textAreaStr
  40.       })
  41.         .type(TextAreaType.NORMAL)
  42.         .minLines(3)
  43.         .maxLines(5, { overflowMode: MaxLinesMode.SCROLL })
  44.         .lineSpacing(LengthMetrics.px(10), { onlyBetweenLines: true })
  45.         .enableAutoSpacing(true)
  46.         .maxLength(200)
  47.         .width('90%')
  48.         .height(120)
  49.         .padding(15)
  50.         .backgroundColor('#F5F5F5')
  51.         .borderRadius(8)
  52.         .shadow({ radius: 2, color: '#00000010' })
  53.         .onChange((value) => {
  54.           this.textAreaStr = value;
  55.         })
  56.         .onBlur(()=>{
  57.           console.log("【TextArea-onBlur】评论输入框失去焦点,当前内容:", this.textAreaStr)
  58.         })
  59.         .onSubmit( (enterKey: EnterKeyType, event: SubmitEvent)=>{
  60.           console.log("【TextArea-onSubmit】按键类型:", enterKey, "提交内容:", event.text)
  61.         })
  62.         .onFocus(() => {
  63.           console.log("【TextArea-onFocus】评论输入框获取焦点")
  64.         })
  65.         .onCopy((value) => {
  66.           console.log("【TextArea-onCopy】复制内容:", value)
  67.         })
  68.         .onCut((value) => {
  69.           console.log("【TextArea-onCut】剪切内容:", value)
  70.         })
  71.         .onPaste((value) => {
  72.           console.log("【TextArea-onPaste】粘贴内容:", value)
  73.         })
  74.         .onTextSelectionChange((selectionStart: number, selectionEnd: number)  => {
  75.           console.log("【TextArea-onTextSelectionChange】起始:", selectionStart, "结束:", selectionEnd)
  76.         });
  77.       // 3. Search 搜索输入
  78.       Search({
  79.         placeholder: '搜索商品、文章、用户...',
  80.         controller: this.searchController,
  81.         value: this.searchStr
  82.       })
  83.         .enterKeyType(EnterKeyType.Search)
  84.         .type(SearchType.NORMAL)
  85.         .maxLength(50)
  86.         .width('90%')
  87.         .height(45)
  88.         .backgroundColor('#F5F5F5')
  89.         .borderRadius(25)
  90.         .onChange((value) => {
  91.           this.searchStr = value;
  92.         });
  93.     }
  94.     .width('100%')
  95.     .height('100%')
  96.     .padding(20)
  97.     .backgroundColor(Color.White)
  98.     .justifyContent(FlexAlign.Center);
  99.   }
  100. }
复制代码
运行效果

2.png

七、示例2:标准注册表单实战(RegisterFormPage.ets)

核心掌握点


  • TextInput表单场景完整配置与属性绑定;
  • inputFilter字符级过滤与业务校验;
  • 密码框全局显隐双向联动;
  • 实时+失焦+提交三层校验逻辑;
  • 全局焦点控制与键盘避让;
  • 表单按钮动态状态控制。
  1. @Entry
  2. @Component
  3. struct RegisterFormPage {
  4.   @State phone: string = '';
  5.   @State pwd: string = '';
  6.   @State confirmPwd: string = '';
  7.   @State phoneError: string = '';
  8.   @State pwdError: string = '';
  9.   @State confirmPwdError: string = '';
  10.   @State isShowPwd: boolean = false;
  11.   private phoneController = new TextInputController();
  12.   private pwdController = new TextInputController();
  13.   private confirmPwdController = new TextInputController();
  14.   private showToast(message: string) {
  15.     try {
  16.       this.getUIContext().getPromptAction().showToast({ message, duration: 2000 });
  17.     } catch (error) {}
  18.   }
  19.   private validateForm(): boolean {
  20.     this.phoneError = this.pwdError = this.confirmPwdError = '';
  21.     let isPass = true;
  22.     if (!/^1[3-9]\d{9}$/.test(this.phone.trim())) {
  23.       this.phoneError = this.phone ? '手机号格式错误' : '请输入手机号';
  24.       isPass = false;
  25.     }
  26.     if (this.pwd.trim().length < 6 || this.pwd.trim().length > 16) {
  27.       this.pwdError = this.pwd ? '密码长度为6-16位' : '请设置密码';
  28.       isPass = false;
  29.     }
  30.     if (this.confirmPwd.trim() !== this.pwd.trim()) {
  31.       this.confirmPwdError = this.confirmPwd ? '两次密码不一致' : '请确认密码';
  32.       isPass = false;
  33.     }
  34.     return isPass;
  35.   }
  36.   private submitRegister() {
  37.     if (this.validateForm()) {
  38.       this.showToast('注册成功');
  39.       this.phone = this.pwd = this.confirmPwd = '';
  40.       this.isShowPwd = false;
  41.       this.getUIContext().getFocusController().clearFocus();
  42.     }
  43.   }
  44.   onPageShow(): void {
  45.     try {
  46.       setTimeout(() => {
  47.         this.getUIContext().getFocusController().requestFocus('phone_id');
  48.       }, 200);
  49.     } catch (error) {}
  50.   }
  51.   build() {
  52.     Scroll() {
  53.       Column({ space: 20 }) {
  54.         Text("用户注册")
  55.           .fontSize(28)
  56.           .fontWeight(FontWeight.Bold)
  57.           .margin({ top: 40, bottom: 20 })
  58.           .width('100%')
  59.           .textAlign(TextAlign.Center);
  60.         // 手机号输入
  61.         TextInput({
  62.           placeholder: '请输入手机号',
  63.           controller: this.phoneController,
  64.           text: this.phone
  65.         })
  66.           .inputFilter('[0-9]', (filteredChars) => {
  67.             console.log('过滤非数字字符:', filteredChars);
  68.           })
  69.           .type(InputType.PhoneNumber)
  70.           .maxLength(11)
  71.           .width('90%')
  72.           .showUnderline(true)
  73.           .underlineColor({ error: '#FF4D4F' })
  74.           .showError(this.phoneError)
  75.           .id('phone_id')
  76.           .onChange((value) => {
  77.             this.phone = value;
  78.             this.phoneError = '';
  79.           })
  80.           .onBlur(() => {
  81.             if (this.phone && !/^1[3-9]\d{9}$/.test(this.phone)) {
  82.               this.phoneError = '手机号格式错误';
  83.             }
  84.           })
  85.           .onSubmit(() => {
  86.             try {
  87.               this.getUIContext().getFocusController().requestFocus('pwd_id');
  88.             } catch (error) {}
  89.           });
  90.         // 密码输入
  91.         TextInput({
  92.           placeholder: '请设置密码(6-16位)',
  93.           controller: this.pwdController,
  94.           text: this.pwd
  95.         })
  96.           .type(InputType.Password)
  97.           .maxLength(16)
  98.           .width('90%')
  99.           .id('pwd_id')
  100.           .inputFilter('[a-zA-Z0-9!@#$%^&*]', (filtered) => {
  101.             filtered && this.showToast(`禁止输入:${filtered}`);
  102.           })
  103.           .showPasswordIcon(true)
  104.           .showPassword(this.isShowPwd)
  105.           .onSecurityStateChange((isShowPassword: boolean) => {
  106.             this.isShowPwd = isShowPassword;
  107.           })
  108.           .onChange((value) => {
  109.             this.pwd = value;
  110.             this.pwdError = '';
  111.           })
  112.           .onSubmit(() => {
  113.             try {
  114.               this.getUIContext().getFocusController().requestFocus('confirm_pwd_id');
  115.             } catch (error) {}
  116.           });
  117.         // 确认密码
  118.         TextInput({
  119.           placeholder: '请再次输入密码',
  120.           controller: this.confirmPwdController,
  121.           text: this.confirmPwd
  122.         })
  123.           .type(InputType.Normal)
  124.           .maxLength(16)
  125.           .width('90%')
  126.           .showUnderline(true)
  127.           .underlineColor({ error: '#FF4D4F' })
  128.           .showError(this.confirmPwdError)
  129.           .id('confirm_pwd_id')
  130.           .inputFilter('[a-zA-Z0-9!@#$%^&*]')
  131.           .showPasswordIcon(true)
  132.           .showPassword(this.isShowPwd)
  133.           .onSecurityStateChange((isShowPassword: boolean) => {
  134.             this.isShowPwd = isShowPassword;
  135.           })
  136.           .onChange((value) => {
  137.             this.confirmPwd = value;
  138.             this.confirmPwdError = '';
  139.           })
  140.           .onSubmit(() => this.submitRegister());
  141.         Row({ space: 8 }) {
  142.           Checkbox()
  143.             .select(this.isShowPwd)
  144.             .selectedColor($r('sys.color.brand'))
  145.             .onChange((v) => this.isShowPwd = v);
  146.           Text('显示密码').fontSize(14).fontColor('#666666');
  147.         }
  148.         .width('90%')
  149.         .margin({ top: 5 });
  150.         Button("立即注册")
  151.           .width('90%')
  152.           .height(45)
  153.           .backgroundColor(
  154.             this.phone.trim() && this.pwd.trim() && this.confirmPwd.trim()
  155.               ? $r('sys.color.brand')
  156.               : '#CCCCCC'
  157.           )
  158.           .fontColor(Color.White)
  159.           .enabled(this.phone.trim() && this.pwd.trim() && this.confirmPwd.trim())
  160.           .onClick(() => this.submitRegister());
  161.       }
  162.       .width('100%')
  163.       .alignItems(HorizontalAlign.Center);
  164.     }
  165.     .width('100%')
  166.     .onClick(() => this.getUIContext().getFocusController().clearFocus())
  167.     .backgroundColor(Color.White);
  168.   }
  169. }
复制代码
运行效果

账号-输入框样式-数字键盘密码-输入框样式—密码键盘密码-输入框样式—有图标
3.png
4.png
5.png
八、示例3:顶部搜索栏实战(InputSearchBarPage.ets)

核心掌握点


  • 搜索栏标准化布局实现;
  • Search图标、按钮、输入类型完整配置;
  • 实时联想词过滤与列表渲染;
  • onChange+onSubmit+onBlur事件组合;
  • 焦点控制与交互优化;
  • 粘贴内容长度校验。
  1. @Entry
  2. @Component
  3. struct InputSearchBarPage {
  4.   @State searchKey: string = '';
  5.   @State suggestList: string[] = [];
  6.   private allSuggestWords = [
  7.     'ArkTS基础教程',
  8.     '鸿蒙组件开发',
  9.     '鸿蒙基础入门',
  10.     'TextInput用法',
  11.     'Search组件实战',
  12.     '鸿蒙布局规范',
  13.     'ArkUI开发指南'
  14.   ];
  15.   private showToast(message: string) {
  16.     try {
  17.       this.getUIContext().getPromptAction().showToast({ message });
  18.     } catch (error) {}
  19.   }
  20.   private getSearchSuggest(keyword: string) {
  21.     if (!keyword.trim()) {
  22.       this.suggestList = [];
  23.       return;
  24.     }
  25.     this.suggestList = this.allSuggestWords.filter(item =>
  26.       item.toLowerCase().includes(keyword.toLowerCase())
  27.     );
  28.   }
  29.   private doSearch(keyword: string) {
  30.     if (!keyword.trim()) {
  31.       this.showToast("请输入搜索关键词");
  32.       return;
  33.     }
  34.     this.showToast(`执行搜索:${keyword}`);
  35.     console.log(`执行搜索:${keyword}`);
  36.   }
  37.   build() {
  38.     Column({ space: 0 }) {
  39.       Row({ space: 20, alignItems: ItemAlign.Center }) {
  40.         Image($r('app.media.icon_back'))
  41.           .width(25)
  42.           .height(25)
  43.           .objectFit(ImageFit.Contain);
  44.         Search({
  45.           placeholder: '搜索本页内容...',
  46.           value: this.searchKey
  47.         })
  48.           .id('searchInput')
  49.           .type(SearchType.NORMAL)
  50.           .height(36)
  51.           .placeholderFont({ size: 14 })
  52.           .enterKeyType(EnterKeyType.Search)
  53.           .backgroundColor('#F5F5F5')
  54.           .layoutWeight(1)
  55.           .constraintSize({ maxWidth: '562.5vp' })
  56.           .searchIcon({ color: '#999', size: 18, src: $r('app.media.search') })
  57.           .cancelButton({
  58.             style: CancelButtonStyle.INPUT,
  59.             icon: { src: $r('app.media.icon_clear'), size: 16, color: '#666' }
  60.           })
  61.           .searchButton('搜索', {
  62.             fontSize: 15,
  63.             fontColor: '#007DFF',
  64.             autoDisable: true
  65.           })
  66.           .onChange((value: string) => {
  67.             this.searchKey = value;
  68.             this.getSearchSuggest(value);
  69.           })
  70.           .onBlur(() => {
  71.             setTimeout(() => {
  72.               this.suggestList = [];
  73.             }, 200);
  74.           })
  75.           .onSubmit((searchContent: string) => {
  76.             this.doSearch(searchContent);
  77.             this.suggestList = [];
  78.             this.getUIContext().getFocusController().clearFocus();
  79.           })
  80.           .onPaste((value) => {
  81.             if (value.length > 50) {
  82.               this.showToast('搜索内容不能超过50字');
  83.             }
  84.           });
  85.       }
  86.       .width('100%')
  87.       .height(60)
  88.       .padding({ left: 15, right: 15 })
  89.       .justifyContent(FlexAlign.Start)
  90.       .backgroundColor(Color.White);
  91.       if (this.suggestList.length > 0) {
  92.         List() {
  93.           ForEach(this.suggestList, (item: string) => {
  94.             ListItem() {
  95.               Text(item)
  96.                 .fontSize(14)
  97.                 .fontColor('#333')
  98.                 .padding({ left: 20, top: 12, bottom: 12 })
  99.                 .width('100%');
  100.             }
  101.             .backgroundColor(Color.White)
  102.             .onClick(() => {
  103.               this.searchKey = item;
  104.               this.doSearch(item);
  105.               this.suggestList = [];
  106.               this.getUIContext().getFocusController().clearFocus();
  107.             });
  108.           }, (item: string) => item)
  109.         }
  110.         .divider({ strokeWidth: 1, startMargin: 20, endMargin: 20 })
  111.         .width('100%')
  112.         .height('calc(100% - 60vp)')
  113.         .backgroundColor($r('sys.color.comp_background_list_card'));
  114.       }
  115.     }
  116.     .width('100%')
  117.     .height('100%')
  118.     .backgroundColor('#F5F5F5');
  119.   }
  120. }
复制代码
运行效果

搜索框自定义图标回车键显示-搜索
6.png
7.png
九、核心知识点总结

(一)组件选型核心原则


  • 单行短文本(账号、密码、手机号、验证码)→ TextInput:单行不折行,输入模式与控制器控制能力丰富;
  • 多行长文本(评论、留言、文章编辑)→ TextArea:自动折行、支持滚动,行数与排版配置完善;
  • 搜索专属场景(全局/页面内搜索)→ Search:内置图标与搜索语义,减少自定义开发;
  • 富文本编辑 → 选用鸿蒙RichEditor,不适用常规输入组件(单独设计一节内容针对富文本讲解)
(二)枚举与绑定规范


  • 输入类型强绑定:InputType→TextInput、TextAreaType→TextArea、SearchType→Search,禁止混用;
  • 数据绑定区分:TextInput/TextArea使用text,Search使用value;
  • 回车键类型EnterKeyType全组件通用,按业务语义选择。
(三)通用开发规范


  • 键盘避让:输入页面外层嵌套Scroll,避免软键盘遮挡;
  • 输入过滤:简单字符规则用inputFilter,复杂业务规则用onWillChange,可组合使用;
  • 校验分层:onChange清错、onBlur格式校验、submit全量校验;
  • 控制器:与组件一一对应,禁止跨组件复用,用于光标与选框精细控制;
  • 焦点管理:用getUIContext().getFocusController()做全局聚焦/失焦,组件需绑定唯一id。
(四)事件与交互规范


  • onChange:负责数据实时同步与基础过滤;
  • onSubmit:Search直接取searchContent,TextInput/TextArea从event.text取值;
  • onPaste:用于粘贴内容长度、格式校验;
  • onWillChange:内容变更前置拦截,支持自动修正,复杂过滤首选方案。
(五)组件专属能力要点


  • TextInput:聚焦表单与密码场景,核心使用下划线、错误提示、密码显隐相关API;
  • TextArea:聚焦长文本,核心配置行数、行间距、高度自适应、溢出策略;
  • Search:聚焦搜索场景,核心使用图标定制、搜索按钮、清除按钮、空内容禁用等交互配置。
十、配套代码


  • 工程名称:InputApplication
  • 仓库地址:https://gitee.com/HarmonyOS-UI-Basics/harmony-os-ui-basics.git
十一、下节预告

下一节我们将学习核心基础组件(三)图片展示组件 Image,从三大核心维度系统掌握图片展示全场景开发能力:

  • 数据源加载:覆盖本地资源、网络图片、Resource资源、媒体库资源、Base64、PixelMap像素图、DrawableDescriptor高级封装等全类型加载方式,明确权限申请、缓存策略与预下载优化技巧;
  • 关键属性配置:详解objectFit缩放模式、interpolation抗锯齿插值、objectRepeat重复样式、renderMode渲染模式、sourceSize解码尺寸、colorFilter滤镜等核心属性,适配不同展示需求;
  • 进阶实战能力:掌握矢量图(SVG)颜色修改、分层图片叠加、多帧动画图片、加载状态监听(onComplete/onError)、同步加载避闪烁等实战技巧,重点解决圆形头像实现、图片裁剪、加载失败占位、大图片性能优化等高频开发场景。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册