【学习目标】
- 区分TextInput、TextArea、Search三大组件的核心差异,掌握精准的场景选型逻辑;
- 掌握三大组件的基础创建、输入类型配置、样式定制,适配各类输入开发场景;
- 熟练绑定核心通用事件,实现输入数据的获取与业务处理;
- 掌握Search专属能力与三大组件控制器的精细控制用法;
- 掌握输入内容过滤的核心实现方式,保证输入内容合法性;
- 具备输入校验、键盘避让等实战能力,独立完成登录、搜索等高频场景开发;
- 能结合业务灵活组合组件,从功能和视觉层面优化用户输入体验。
一、文本输入类组件核心认知
(一)组件整体定位
TextInput、TextArea、Search是鸿蒙ArkTS核心文本输入类组件,基于统一输入底层能力封装,支持通用样式与高频事件;针对单行短文本、多行长文本、搜索专属三大场景做差异化优化,是移动端应用开发必备基础组件。
(二)核心选型原则与组件差异
核心原则:按输入形态和业务场景选型,禁止跨场景混用。三者核心差异、专属优势与适配场景如下:
| 组件名 | 核心特性 | 专属优势 | 适用场景 |
|---|---|---|---|
| TextInput | 单行输入,不自动折行 | 输入模式丰富,控制器光标/选框控制能力精细 | 账号、密码、手机号、验证码等短文本输入 |
| TextArea | 多行输入,自动折行,支持滚动 | 适配长文本,可配置自动高度/滚动条,支持长文本编辑控制 | 评论、留言、文章编辑、长备注填写 |
| Search | 单行输入,搜索场景专属 | 内置搜索/清除图标,默认回车搜索逻辑,降低自定义开发成本 | 全局搜索、商品检索、页面内搜索栏 |
(三)核心绑定规范与组件接口
各组件对应专属构造函数,彼此独立不通用,核心接口与绑定规则如下:
| 组件名 | 专属构造函数接口 | 核心可选属性 |
|---|---|---|
| TextInput | TextInputOptions | text: ResourceStr、controller: TextInputController、placeholder: ResourceStr |
| TextArea | TextAreaOptions | text: ResourceStr、controller: TextAreaController、placeholder: ResourceStr |
| Search | SearchOptions | value: 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 原生正则过滤
仅需编写允许的字符集 ,无需添加^$首尾限定符,非法字符直接拦截:
javascript
// 仅允许字母/数字(账号/验证码场景)
TextInput({ text: this.content, placeholder: '请输入字母/数字' })
.inputFilter('[0-9A-Za-z]', (filtered) => {
console.log('过滤的非法内容:', filtered);
})
2. onWillChange 自定义逻辑过滤
仅支持返回boolean类型(true允许本次内容变更,false拦截变更,界面不更新),如需实现内容自动修正,需手动更新绑定的状态变量并返回false拦截原始输入,覆盖复杂业务规则:
javascript
// 首位禁止输入空格
TextInput({ text: this.inputText, placeholder: '首位无空格' })
.onWillChange((changeInfo:EditableTextChangeValue)=>{
return changeInfo.content.trimStart() !== "";
})
3. 组合过滤(生产高频用法)
inputFilter做底层字符限制 + onWillChange做业务规则校验,兼顾性能与需求:
javascript
// 账号规则:仅字母/数字 + 首位不能为数字
TextInput({ text: this.account, placeholder: '字母开头,字母/数字组合' })
.inputFilter('[0-9A-Za-z]')
.onWillChange((newValue) => {
return newValue.content.replace(/^[0-9]/, '') !== "";
});
(三)组件控制器
所有控制器继承自TextContentControllerBase,必须与组件一对一绑定,禁止跨组件混用,用于光标、选框、编辑状态精细控制。
1. 控制器-组件对应关系
| 输入组件 | 专属控制器 | 核心能力 |
|---|---|---|
| TextInput | TextInputController | 单行文本光标定位、选框控制、退出编辑态 |
| TextArea | TextAreaController | 多行文本光标定位、选框控制、长文本编辑适配 |
| Search | SearchController | 继承TextInputController能力,适配搜索框控制 |
2. 通用核心方法
| 方法名称 | 功能说明 | 典型场景 |
|---|---|---|
caretPosition(pos: number): void |
设置光标位置(索引从0开始) | 提交后光标归位、验证码输入后光标跳转 |
setTextSelection(start: number, end: number): void |
获焦状态下设置文本选中区域 | 快速选中错误文本、批量编辑前置操作 |
stopEditing(): void |
退出编辑态,关闭自定义键盘 | 自定义键盘手动关闭、提交后退出编辑 |
3. 控制器基础使用方法
javascript
private inputController = new TextInputController();
// 光标移至文本开头
this.inputController.caretPosition(0);
// 选中0~6位字符
this.inputController.setTextSelection(0, 6);
// 主动退出编辑状态
this.inputController.stopEditing();
(四)全局焦点控制
焦点控制为页面级能力,与组件控制器解耦,是鸿蒙标准焦点管理方案:
1. 核心方法
| 核心方法 | 功能说明 | 参数/场景 |
|---|---|---|
requestFocus(id: string): boolean |
指定ID组件获取焦点,唤起输入法 | 参数:组件唯一ID;场景:页面自动聚焦、校验失败定位 |
clearFocus(): void |
清除全页面焦点,收起软键盘 | 无参数;场景:点击空白处、提交/搜索完成 |
2. 标准使用代码
javascript
// 获取全局焦点控制器
const focusController = this.getUIContext().getFocusController();
// 精准聚焦(组件必须绑定id)
focusController.requestFocus("account_input");
// 全局失焦,收起键盘
focusController.clearFocus();
注意 :调用
requestFocus前,必须为目标组件设置唯一.id('xxx'),否则聚焦失效。
(五)专属输入模式枚举
强制规范:各组件仅可使用自身专属枚举,严禁跨组件混用。
1. TextInput 专属:InputType
| 枚举成员 | 功能说明 | 适用场景 |
|---|---|---|
| Normal | 基础通用输入 | 用户名、普通文本 |
| Number | 纯数字输入 | 验证码、订单号 |
| PhoneNumber | 电话格式输入 | 手机号、座机号 |
| 邮箱格式输入 | 邮箱登录/注册表单 | |
| Password | 密码隐藏输入 | 账号登录密码 |
| NUMBER_PASSWORD | 纯数字密码 | 支付密码、锁屏密码 |
| USER_NAME | 用户名专属,支持密码库填充 | 账号登录/注册 |
| NEW_PASSWORD | 新密码,支持强度校验 | 密码重置、新用户注册 |
| NUMBER_DECIMAL | 带一位小数点数字 | 金额、身高、体重 |
| URL | 网址格式输入 | 链接填写、校验 |
| ONE_TIME_CODE | 一次性验证码(API20+) | 短信验证码输入 |
2. TextArea 专属:TextAreaType
| 枚举成员 | 功能说明 | 适用场景 |
|---|---|---|
| NORMAL | 基础多行输入 | 评论、留言、长文本 |
| NUMBER | 纯数字多行输入 | 长数字序列、数字备注 |
| PHONE_NUMBER | 电话格式多行输入 | 批量号码录入 |
| 邮箱格式多行输入 | 邮箱批量录入 | |
| NUMBER_DECIMAL | 带小数点数字 | 长文本内含数值 |
| URL | 网址格式 | 长文本内含链接 |
| ONE_TIME_CODE | 一次性验证码(API20+) | 验证码批量录入 |
3. Search 专属:SearchType
| 枚举成员 | 功能说明 | 适用场景 |
|---|---|---|
| NORMAL | 通用搜索输入 | 全文、关键词检索 |
| NUMBER | 纯数字搜索 | 订单号、快递号、ID检索 |
| PHONE_NUMBER | 电话格式搜索 | 手机号、联系电话检索 |
| 邮箱格式搜索 | 用户邮箱检索 | |
| 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工程,标准目录结构如下:
InputApplication/
├── AppScope/
│ └── app.json5
├── entry/
│ ├── src/
│ │ ├── main/
│ │ │ ├── ets/
│ │ │ │ ├── entryability/
│ │ │ │ │ └── EntryAbility.ets
│ │ │ │ ├── pages/
│ │ │ │ │ ├── Index.ets // 导航主页面
│ │ │ │ │ ├── InputBasicPage.ets // 示例1:三大组件基础用法
│ │ │ │ │ ├── RegisterFormPage.ets // 示例2:注册表单实战
│ │ │ │ │ └── InputSearchBarPage.ets // 示例3:顶部搜索栏实战
│ │ │ │ │ └── CommentAreaPage.ets // 示例4:评论区实战
│ │ │ ├── resources/
│ │ │ │ └── media/ // 自定义图标:icon_back、search、icon_clear
│ │ │ └── module.json5
│ │ └── build-profile.json5
└── build-profile.json5
五、导航主页面(Index.ets)
javascript
import { router } from '@kit.ArkUI';
interface RouterButton {
title: string;
url: string;
}
@Entry
@Component
struct Index {
private buttonList: RouterButton[] = [
{ title: "示例1:三大组件基础用法", url: 'pages/InputBasicPage' },
{ title: "示例2:标准注册表单实战", url: 'pages/RegisterFormPage' },
{ title: "示例3:顶部搜索栏实战", url: 'pages/InputSearchBarPage' },
{ title: "示例4:发布评论实战", url: 'pages/CommentAreaPage' },
];
build() {
Column({ space: 12 }) {
Text("TextInput/TextArea/Search")
.fontSize(30)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 30 });
ForEach(
this.buttonList,
(item: RouterButton) => {
Button(item.title)
.width('85%')
.height(42)
.backgroundColor($r('sys.color.brand'))
.fontColor(Color.White)
.borderRadius(8)
.fontSize(15)
.onClick(() => router.pushUrl({ url: item.url }));
},
(item: RouterButton) => item.url
);
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
.backgroundColor('#F5F5F5');
}
}
运行效果
六、示例1:三大组件基础用法(InputBasicPage.ets)
核心掌握点
- 区分TextInput/TextArea的
text绑定与Search的value绑定; - 正确使用组件专属输入类型枚举;
- 完成核心事件绑定与数据实时同步;
- 控制器一对一绑定规范;
- TextArea行数、行间距、溢出等长文本配置。
javascript
import { LengthMetrics } from '@kit.ArkUI';
@Entry
@Component
struct InputBasicPage {
@State textInputStr: string = '';
@State textAreaStr: string = '';
@State searchStr: string = '';
private textInputController = new TextInputController();
private textAreaController = new TextAreaController();
private searchController = new SearchController();
build() {
Column({ space: 25 }) {
Text("三大组件基础用法")
.fontSize(24)
.fontWeight(FontWeight.Bold)
.width('100%')
.textAlign(TextAlign.Center);
// 1. TextInput 手机号输入
TextInput({
placeholder: '请输入11位手机号',
controller: this.textInputController,
text: this.textInputStr
})
.type(InputType.PhoneNumber)
.maxLength(11)
.width('90%')
.height(50)
.padding(15)
.backgroundColor('#F5F5F5')
.borderRadius(8)
.shadow({ radius: 2, color: '#00000010' })
.onChange((value) => {
this.textInputStr = value;
});
// 2. TextArea 评论输入
TextArea({
placeholder: '请输入你的评论,最多200字...',
controller: this.textAreaController,
text: this.textAreaStr
})
.type(TextAreaType.NORMAL)
.minLines(3)
.maxLines(5, { overflowMode: MaxLinesMode.SCROLL })
.lineSpacing(LengthMetrics.px(10), { onlyBetweenLines: true })
.enableAutoSpacing(true)
.maxLength(200)
.width('90%')
.height(120)
.padding(15)
.backgroundColor('#F5F5F5')
.borderRadius(8)
.shadow({ radius: 2, color: '#00000010' })
.onChange((value) => {
this.textAreaStr = value;
})
.onBlur(()=>{
console.log("【TextArea-onBlur】评论输入框失去焦点,当前内容:", this.textAreaStr)
})
.onSubmit( (enterKey: EnterKeyType, event: SubmitEvent)=>{
console.log("【TextArea-onSubmit】按键类型:", enterKey, "提交内容:", event.text)
})
.onFocus(() => {
console.log("【TextArea-onFocus】评论输入框获取焦点")
})
.onCopy((value) => {
console.log("【TextArea-onCopy】复制内容:", value)
})
.onCut((value) => {
console.log("【TextArea-onCut】剪切内容:", value)
})
.onPaste((value) => {
console.log("【TextArea-onPaste】粘贴内容:", value)
})
.onTextSelectionChange((selectionStart: number, selectionEnd: number) => {
console.log("【TextArea-onTextSelectionChange】起始:", selectionStart, "结束:", selectionEnd)
});
// 3. Search 搜索输入
Search({
placeholder: '搜索商品、文章、用户...',
controller: this.searchController,
value: this.searchStr
})
.enterKeyType(EnterKeyType.Search)
.type(SearchType.NORMAL)
.maxLength(50)
.width('90%')
.height(45)
.backgroundColor('#F5F5F5')
.borderRadius(25)
.onChange((value) => {
this.searchStr = value;
});
}
.width('100%')
.height('100%')
.padding(20)
.backgroundColor(Color.White)
.justifyContent(FlexAlign.Center);
}
}
运行效果
七、示例2:标准注册表单实战(RegisterFormPage.ets)
核心掌握点
- TextInput表单场景完整配置与属性绑定;
- inputFilter字符级过滤与业务校验;
- 密码框全局显隐双向联动;
- 实时+失焦+提交三层校验逻辑;
- 全局焦点控制与键盘避让;
- 表单按钮动态状态控制。
javascript
@Entry
@Component
struct RegisterFormPage {
@State phone: string = '';
@State pwd: string = '';
@State confirmPwd: string = '';
@State phoneError: string = '';
@State pwdError: string = '';
@State confirmPwdError: string = '';
@State isShowPwd: boolean = false;
private phoneController = new TextInputController();
private pwdController = new TextInputController();
private confirmPwdController = new TextInputController();
private showToast(message: string) {
try {
this.getUIContext().getPromptAction().showToast({ message, duration: 2000 });
} catch (error) {}
}
private validateForm(): boolean {
this.phoneError = this.pwdError = this.confirmPwdError = '';
let isPass = true;
if (!/^1[3-9]\d{9}$/.test(this.phone.trim())) {
this.phoneError = this.phone ? '手机号格式错误' : '请输入手机号';
isPass = false;
}
if (this.pwd.trim().length < 6 || this.pwd.trim().length > 16) {
this.pwdError = this.pwd ? '密码长度为6-16位' : '请设置密码';
isPass = false;
}
if (this.confirmPwd.trim() !== this.pwd.trim()) {
this.confirmPwdError = this.confirmPwd ? '两次密码不一致' : '请确认密码';
isPass = false;
}
return isPass;
}
private submitRegister() {
if (this.validateForm()) {
this.showToast('注册成功');
this.phone = this.pwd = this.confirmPwd = '';
this.isShowPwd = false;
this.getUIContext().getFocusController().clearFocus();
}
}
onPageShow(): void {
try {
setTimeout(() => {
this.getUIContext().getFocusController().requestFocus('phone_id');
}, 200);
} catch (error) {}
}
build() {
Scroll() {
Column({ space: 20 }) {
Text("用户注册")
.fontSize(28)
.fontWeight(FontWeight.Bold)
.margin({ top: 40, bottom: 20 })
.width('100%')
.textAlign(TextAlign.Center);
// 手机号输入
TextInput({
placeholder: '请输入手机号',
controller: this.phoneController,
text: this.phone
})
.inputFilter('[0-9]', (filteredChars) => {
console.log('过滤非数字字符:', filteredChars);
})
.type(InputType.PhoneNumber)
.maxLength(11)
.width('90%')
.showUnderline(true)
.underlineColor({ error: '#FF4D4F' })
.showError(this.phoneError)
.id('phone_id')
.onChange((value) => {
this.phone = value;
this.phoneError = '';
})
.onBlur(() => {
if (this.phone && !/^1[3-9]\d{9}$/.test(this.phone)) {
this.phoneError = '手机号格式错误';
}
})
.onSubmit(() => {
try {
this.getUIContext().getFocusController().requestFocus('pwd_id');
} catch (error) {}
});
// 密码输入
TextInput({
placeholder: '请设置密码(6-16位)',
controller: this.pwdController,
text: this.pwd
})
.type(InputType.Password)
.maxLength(16)
.width('90%')
.id('pwd_id')
.inputFilter('[a-zA-Z0-9!@#$%^&*]', (filtered) => {
filtered && this.showToast(`禁止输入:${filtered}`);
})
.showPasswordIcon(true)
.showPassword(this.isShowPwd)
.onSecurityStateChange((isShowPassword: boolean) => {
this.isShowPwd = isShowPassword;
})
.onChange((value) => {
this.pwd = value;
this.pwdError = '';
})
.onSubmit(() => {
try {
this.getUIContext().getFocusController().requestFocus('confirm_pwd_id');
} catch (error) {}
});
// 确认密码
TextInput({
placeholder: '请再次输入密码',
controller: this.confirmPwdController,
text: this.confirmPwd
})
.type(InputType.Normal)
.maxLength(16)
.width('90%')
.showUnderline(true)
.underlineColor({ error: '#FF4D4F' })
.showError(this.confirmPwdError)
.id('confirm_pwd_id')
.inputFilter('[a-zA-Z0-9!@#$%^&*]')
.showPasswordIcon(true)
.showPassword(this.isShowPwd)
.onSecurityStateChange((isShowPassword: boolean) => {
this.isShowPwd = isShowPassword;
})
.onChange((value) => {
this.confirmPwd = value;
this.confirmPwdError = '';
})
.onSubmit(() => this.submitRegister());
Row({ space: 8 }) {
Checkbox()
.select(this.isShowPwd)
.selectedColor($r('sys.color.brand'))
.onChange((v) => this.isShowPwd = v);
Text('显示密码').fontSize(14).fontColor('#666666');
}
.width('90%')
.margin({ top: 5 });
Button("立即注册")
.width('90%')
.height(45)
.backgroundColor(
this.phone.trim() && this.pwd.trim() && this.confirmPwd.trim()
? $r('sys.color.brand')
: '#CCCCCC'
)
.fontColor(Color.White)
.enabled(this.phone.trim() && this.pwd.trim() && this.confirmPwd.trim())
.onClick(() => this.submitRegister());
}
.width('100%')
.alignItems(HorizontalAlign.Center);
}
.width('100%')
.onClick(() => this.getUIContext().getFocusController().clearFocus())
.backgroundColor(Color.White);
}
}
运行效果
| 账号-输入框样式-数字键盘 | 密码-输入框样式---密码键盘 | 密码-输入框样式---有图标 |
|---|---|---|
八、示例3:顶部搜索栏实战(InputSearchBarPage.ets)
核心掌握点
- 搜索栏标准化布局实现;
- Search图标、按钮、输入类型完整配置;
- 实时联想词过滤与列表渲染;
- onChange+onSubmit+onBlur事件组合;
- 焦点控制与交互优化;
- 粘贴内容长度校验。
javascript
@Entry
@Component
struct InputSearchBarPage {
@State searchKey: string = '';
@State suggestList: string[] = [];
private allSuggestWords = [
'ArkTS基础教程',
'鸿蒙组件开发',
'鸿蒙基础入门',
'TextInput用法',
'Search组件实战',
'鸿蒙布局规范',
'ArkUI开发指南'
];
private showToast(message: string) {
try {
this.getUIContext().getPromptAction().showToast({ message });
} catch (error) {}
}
private getSearchSuggest(keyword: string) {
if (!keyword.trim()) {
this.suggestList = [];
return;
}
this.suggestList = this.allSuggestWords.filter(item =>
item.toLowerCase().includes(keyword.toLowerCase())
);
}
private doSearch(keyword: string) {
if (!keyword.trim()) {
this.showToast("请输入搜索关键词");
return;
}
this.showToast(`执行搜索:${keyword}`);
console.log(`执行搜索:${keyword}`);
}
build() {
Column({ space: 0 }) {
Row({ space: 20, alignItems: ItemAlign.Center }) {
Image($r('app.media.icon_back'))
.width(25)
.height(25)
.objectFit(ImageFit.Contain);
Search({
placeholder: '搜索本页内容...',
value: this.searchKey
})
.id('searchInput')
.type(SearchType.NORMAL)
.height(36)
.placeholderFont({ size: 14 })
.enterKeyType(EnterKeyType.Search)
.backgroundColor('#F5F5F5')
.layoutWeight(1)
.constraintSize({ maxWidth: '562.5vp' })
.searchIcon({ color: '#999', size: 18, src: $r('app.media.search') })
.cancelButton({
style: CancelButtonStyle.INPUT,
icon: { src: $r('app.media.icon_clear'), size: 16, color: '#666' }
})
.searchButton('搜索', {
fontSize: 15,
fontColor: '#007DFF',
autoDisable: true
})
.onChange((value: string) => {
this.searchKey = value;
this.getSearchSuggest(value);
})
.onBlur(() => {
setTimeout(() => {
this.suggestList = [];
}, 200);
})
.onSubmit((searchContent: string) => {
this.doSearch(searchContent);
this.suggestList = [];
this.getUIContext().getFocusController().clearFocus();
})
.onPaste((value) => {
if (value.length > 50) {
this.showToast('搜索内容不能超过50字');
}
});
}
.width('100%')
.height(60)
.padding({ left: 15, right: 15 })
.justifyContent(FlexAlign.Start)
.backgroundColor(Color.White);
if (this.suggestList.length > 0) {
List() {
ForEach(this.suggestList, (item: string) => {
ListItem() {
Text(item)
.fontSize(14)
.fontColor('#333')
.padding({ left: 20, top: 12, bottom: 12 })
.width('100%');
}
.backgroundColor(Color.White)
.onClick(() => {
this.searchKey = item;
this.doSearch(item);
this.suggestList = [];
this.getUIContext().getFocusController().clearFocus();
});
}, (item: string) => item)
}
.divider({ strokeWidth: 1, startMargin: 20, endMargin: 20 })
.width('100%')
.height('calc(100% - 60vp)')
.backgroundColor($r('sys.color.comp_background_list_card'));
}
}
.width('100%')
.height('100%')
.backgroundColor('#F5F5F5');
}
}
运行效果
| 搜索框自定义图标 | 回车键显示-搜索 |
|---|---|
九、核心知识点总结
(一)组件选型核心原则
- 单行短文本(账号、密码、手机号、验证码)→ 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)、同步加载避闪烁等实战技巧,重点解决圆形头像实现、图片裁剪、加载失败占位、大图片性能优化等高频开发场景。