ArkUI TextInput:输入框写得顺,用户才愿意继续填

举报
蓝瘦的蜕变 发表于 2026/08/22 14:42:16 2026/08/22
【摘要】 这篇我们写 TextInput。输入框很小,但它特别容易暴露页面粗糙感:占位文字太重、键盘类型不对、密码框没有眼睛图标、错误态只靠用户自己猜。 所以这个案例不做完整登录页,也不讲状态管理。我们先把输入框本体看清楚:普通输入、手机号、邮箱、验证码、密码、错误态和禁用态。 参考文档: https://developer.h

ArkUI TextInput:输入框写得顺,用户才愿意继续填

这篇我们写 TextInput。输入框很小,但它特别容易暴露页面粗糙感:占位文字太重、键盘类型不对、密码框没有眼睛图标、错误态只靠用户自己猜。

所以这个案例不做完整登录页,也不讲状态管理。我们先把输入框本体看清楚:普通输入、手机号、邮箱、验证码、密码、错误态和禁用态。

参考文档:

  • https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkui
  • https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-ui-development

普通输入框先别写得太硬

一个舒服的输入框,通常不是靠大改结构,而是把几个小地方调顺。

TextInput({ placeholder: '请输入昵称' })
  .width('100%')
  .height(44)
  .fontSize(15)
  .fontColor('#0F172A')
  .placeholderColor('#94A3B8')
  .placeholderFont({ size: 14, weight: 400 })
  .caretColor('#2563EB')
  .maxLength(16)
  .backgroundColor('#FFFFFF')
  .border({ width: 1, color: '#CBD5E1' })
  .borderRadius(8)
  .padding({ left: 12, right: 12 })

这里的 placeholderColor 故意放轻一点。占位文字只是提示,不应该和用户真正输入的内容抢注意力。

caretColor 是光标颜色。页面主色是蓝色时,光标也跟着走,细节会统一一些。

maxLength(16) 是很实用的小限制。昵称这种字段一般不需要无限长,提前限制比后面再拦截更直观。

输入类型要顺手

手机号、邮箱、验证码都能用普通输入框写,但用户体验会差一点。type 的价值就在这里:让系统给出更合适的输入方式。

TextInput({ placeholder: '请输入手机号' })
  .type(InputType.PhoneNumber)
  .enterKeyType(EnterKeyType.Next)
  .maxLength(11)

TextInput({ placeholder: 'name@example.com' })
  .type(InputType.Email)
  .enterKeyType(EnterKeyType.Done)

手机号用 PhoneNumber,邮箱用 Email。这一步很简单,但新手很容易忘,然后所有输入框都停留在默认类型。

enterKeyType 控制软键盘右下角的动作提示。手机号后面还有别的字段,就用 Next;邮箱已经是最后一步,就可以用 Done

验证码这里多加了一个 inputFilter

TextInput({ placeholder: '6 位数字验证码' })
  .type(InputType.Number)
  .inputFilter('[0-9]')
  .maxLength(6)

inputFilter('[0-9]') 表示只让数字字符显示出来。它适合这种很明确的字段。别把所有输入都塞过滤规则,字段越复杂,越应该把校验留给更完整的表单逻辑。

密码框要给用户一点掌控感

密码输入只改 type 还不够。一般来说,我们还会让用户能切换显示和隐藏。

TextInput({ placeholder: '请输入密码' })
  .type(InputType.Password)
  .showPasswordIcon(true)
  .maxLength(20)

InputType.Password 会按密码输入处理,showPasswordIcon(true) 会显示眼睛图标。这个交互很常见,用户也熟,不需要我们额外解释太多。

错误态和禁用态要一眼看懂

错误输入不要只靠红边框暗示。能直接告诉用户问题在哪,就直接说。

TextInput({ text: 'hi', placeholder: '用户名至少 4 个字符' })
  .showError('用户名太短')
  .border({ width: 1, color: '#FCA5A5' })

showError 适合展示输入框自己的错误提示。这个案例没有写提交按钮,也没有写表单校验流程,是因为那会把主题带到状态管理和业务逻辑里。我们后面写状态章节时再把它补完整。

禁用态也一样,要让它看起来明显不能编辑:

TextInput({ text: 'WELCOME-2026', placeholder: '邀请码' })
  .enabled(false)
  .backgroundColor('#E2E8F0')
  .fontColor('#64748B')

别只调用 enabled(false) 就结束。颜色、背景、边框都安静一点,用户才不会误以为它还能点。

看案例时盯这几处

打开案例页后,可以顺着这几个点看:

  • 昵称输入框的占位文字是否足够轻。
  • 手机号和邮箱是否用了不同的输入类型。
  • 验证码是否只能输入数字,并限制在 6 位。
  • 密码框是否出现显示/隐藏图标。
  • 错误态和禁用态是否能一眼分清。

这篇不跑 hvigor。你在 DevEco Studio 里打开案例页,手动点进几个输入框,看键盘、光标和输入限制是否符合预期就行。

如果你现在项目里有表单,可以先只挑一个输入框改:把 placeholderColortypemaxLength 这三个补上。你最常写的是哪类输入框?手机号、搜索框、评论框,还是登录密码?这个问题其实会决定下一篇 TextArea 或搜索输入要不要提前写。

完整示例代码

文件位置:entry/src/main/ets/pages/arkUI/basic/ArkUITextInputDemo.ets

@Entry
@Component
struct ArkUITextInputDemo {
  @Builder
  sectionTitle(title: string, desc: string) {
    Column({ space: 4 }) {
      Text(title)
        .width('100%')
        .fontSize(16)
        .fontWeight(700)
        .fontColor('#0F172A')

      Text(desc)
        .width('100%')
        .fontSize(13)
        .fontColor('#64748B')
        .lineHeight(20)
    }
    .width('100%')
    .alignItems(HorizontalAlign.Start)
  }

  @Builder
  inputLabel(label: string) {
    Text(label)
      .width('100%')
      .fontSize(13)
      .fontWeight(600)
      .fontColor('#334155')
  }

  build() {
    Scroll() {
      Column({ space: 18 }) {
        Text('ArkUI TextInput')
          .width('100%')
          .fontSize(24)
          .fontWeight(700)
          .fontColor('#0F172A')

        Text('输入框写得顺,用户才愿意继续填')
          .width('100%')
          .fontSize(22)
          .fontWeight(700)
          .fontColor('#111827')

        Text('这一页不做登录表单,也不讲状态管理。我们只把 TextInput 自己的几个常用细节摆清楚:占位提示、输入类型、长度限制、密码显示和错误提示。')
          .width('100%')
          .fontSize(14)
          .fontColor('#64748B')
          .lineHeight(22)

        Column({ space: 12 }) {
          this.sectionTitle('先把一个普通输入框写舒服', '占位文字要轻一点,光标颜色跟主色走,输入区域高度也别太挤。')

          this.inputLabel('昵称')

          TextInput({ placeholder: '请输入昵称' })
            .width('100%')
            .height(44)
            .fontSize(15)
            .fontColor('#0F172A')
            .placeholderColor('#94A3B8')
            .placeholderFont({ size: 14, weight: 400 })
            .caretColor('#2563EB')
            .maxLength(16)
            .backgroundColor('#FFFFFF')
            .border({ width: 1, color: '#CBD5E1' })
            .borderRadius(8)
            .padding({ left: 12, right: 12 })
        }
        .width('100%')
        .padding(16)
        .backgroundColor('#F8FAFC')
        .borderRadius(8)

        Column({ space: 12 }) {
          this.sectionTitle('输入类型别全用默认值', '手机号、邮箱、验证码的键盘和输入规则不一样,type 写对,用户少切几次键盘。')

          this.inputLabel('手机号')

          TextInput({ placeholder: '请输入手机号' })
            .width('100%')
            .height(44)
            .type(InputType.PhoneNumber)
            .enterKeyType(EnterKeyType.Next)
            .fontSize(15)
            .fontColor('#0F172A')
            .placeholderColor('#94A3B8')
            .caretColor('#0F766E')
            .maxLength(11)
            .backgroundColor('#FFFFFF')
            .border({ width: 1, color: '#99F6E4' })
            .borderRadius(8)
            .padding({ left: 12, right: 12 })

          this.inputLabel('邮箱')

          TextInput({ placeholder: 'name@example.com' })
            .width('100%')
            .height(44)
            .type(InputType.Email)
            .enterKeyType(EnterKeyType.Done)
            .fontSize(15)
            .fontColor('#0F172A')
            .placeholderColor('#94A3B8')
            .caretColor('#0F766E')
            .backgroundColor('#FFFFFF')
            .border({ width: 1, color: '#99F6E4' })
            .borderRadius(8)
            .padding({ left: 12, right: 12 })

          this.inputLabel('验证码,只允许输入数字')

          TextInput({ placeholder: '6 位数字验证码' })
            .width('100%')
            .height(44)
            .type(InputType.Number)
            .inputFilter('[0-9]')
            .maxLength(6)
            .fontSize(15)
            .fontColor('#0F172A')
            .placeholderColor('#94A3B8')
            .caretColor('#0F766E')
            .backgroundColor('#FFFFFF')
            .border({ width: 1, color: '#99F6E4' })
            .borderRadius(8)
            .padding({ left: 12, right: 12 })
        }
        .width('100%')
        .padding(16)
        .backgroundColor('#F0FDFA')
        .borderRadius(8)

        Column({ space: 12 }) {
          this.sectionTitle('密码框别忘了眼睛图标', 'Password 类型会按密码输入处理,showPasswordIcon 可以让用户自己切换显示和隐藏。')

          this.inputLabel('密码')

          TextInput({ placeholder: '请输入密码' })
            .width('100%')
            .height(44)
            .type(InputType.Password)
            .showPasswordIcon(true)
            .maxLength(20)
            .fontSize(15)
            .fontColor('#0F172A')
            .placeholderColor('#94A3B8')
            .caretColor('#7C3AED')
            .backgroundColor('#FFFFFF')
            .border({ width: 1, color: '#DDD6FE' })
            .borderRadius(8)
            .padding({ left: 12, right: 12 })
        }
        .width('100%')
        .padding(16)
        .backgroundColor('#F5F3FF')
        .borderRadius(8)

        Column({ space: 12 }) {
          this.sectionTitle('错误态和禁用态要看得出来', '错误信息别只靠红色边框猜,不能编辑时也要让输入框明显安静下来。')

          this.inputLabel('用户名')

          TextInput({ text: 'hi', placeholder: '用户名至少 4 个字符' })
            .width('100%')
            .height(44)
            .fontSize(15)
            .fontColor('#0F172A')
            .placeholderColor('#94A3B8')
            .caretColor('#DC2626')
            .showError('用户名太短')
            .backgroundColor('#FFFFFF')
            .border({ width: 1, color: '#FCA5A5' })
            .borderRadius(8)
            .padding({ left: 12, right: 12 })

          this.inputLabel('邀请码')

          TextInput({ text: 'WELCOME-2026', placeholder: '邀请码' })
            .width('100%')
            .height(44)
            .fontSize(15)
            .fontColor('#64748B')
            .enabled(false)
            .backgroundColor('#E2E8F0')
            .border({ width: 1, color: '#CBD5E1' })
            .borderRadius(8)
            .padding({ left: 12, right: 12 })
        }
        .width('100%')
        .padding(16)
        .backgroundColor('#FFF7ED')
        .borderRadius(8)
      }
      .width('100%')
      .padding(20)
      .alignItems(HorizontalAlign.Start)
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#FFFFFF')
  }
}
【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。