1# 自定义输入框
2## 场景介绍
3输入框是一种常见的场景,开发者为了丰富其多样性,在输入框中的占位符、下划线做了相关处理。本文即为大家介绍如何开发自定义输入框。
4## 效果呈现
5效果图如下:
6
7![](figures/custom-textinput.gif)
8
9## 运行环境
10本例基于以下环境开发,开发者也可以基于其他适配的版本进行开发:
11- IDE: DevEco Studio 3.2 Release
12- SDK: Ohos_sdk_public 3.2.12.5 (API Version 9 Release)
13## 实现思路
14- 点击输入框,提示文字向上浮动并同时改变文字颜色,输入框底线进行匀速放缩:通过监听输入框状态(onEditChange)发生变化时,提示文字向上浮动以及底部线进行放缩,同时改变文字和底部线的颜色。
15- 在输入文本时,输入框右侧显示“清除”图标。点击“清空”图标,清空输入文本:通过监听输入框中有文本输入(onChange)时,右侧显示“清除”图标。
16## 开发步骤
171. 封装TextInput组件去监听onEditChange事件(输入状态的变化)和onchange事件(输入内容的变化)。具体代码块如下:
18    ```ts
19    TextInput({text:this.textValue})
20      .width('100%')
21      .height('100%')
22      .borderRadius(0)
23      .borderColor('#86C7CC')
24      .borderWidth({bottom: 1})
25      .backgroundColor(Color.Transparent)
26      .type(this.typeInput)
27      .showPasswordIcon(false)
28      // 输入内容发生变化
29      .onEditChange((isEditing)=>{
30        this.isEditing = isEditing
31        this.isFloatHint = this.isEditing || this.textValue !==''
32      })
33      // 输入状态变化时
34      .onChange((value)=>{
35        this.textValue = value
36        this.isFloatHint = this.isEditing || this.textValue !== ''
37    })
38    ```
392. 输入状态发生变化(onEditChange)时,提示文字向上浮动并同时改变颜色;输入框底线匀速放缩并改变颜色。具体代码如下:
40    ```ts
41    // 提示文本(占位符)
42    Text(this.placeholder)
43      .enabled(false)
44      .fontColor(this.isFloatHint ?  '#5D7DB3' : '#E1E4EA')
45      .position({x: 0, y: this.isFloatHint ? 0 : '50%'})
46      .translate({x: 15, y: '-50%'})
47       .animation({duration:100, curve: Curve.Linear})
48
49    // 底部线
50    Line()
51      .width('100%')
52      .height(1)
53      .backgroundColor('#5D7DB3')
54      .position({x: 0, y: 44})
55      .scale({x: this.isFloatHint ? 1 : 0, centerX: 0})
56      .animation({duration: 300, curve: Curve.Linear})
57    ```
583. 输入内容发生变化(onChange)时,输入框右侧显示“清除”图标,点击该图标清空输入框内容。具体代码块如下:
59    ```ts
60    if (this.textValue){
61      Image($r("app.media.canel"))
62        .width(15)
63        .height(15)
64        .border({width: 1, radius: 15, color: '#fffffff'})
65        .position({x: '90%', y: '50%'})
66        .translate({y: '-50%'})
67        .onClick(()=>{
68            this.textValue = ''
69        })
70    }
71    ```
724. 构建界面UI。具体代码块如下:
73    ```ts
74    @Entry
75    @Component
76    struct InputFloatHintPage{
77      build(){
78        Column(){
79          Text('欢迎使用')
80            .fontSize(50)
81            .fontColor('#91B5A9')
82          Column(){
83            InputFloatHint({placeholder: '请输入用户名'})
84            Blank().height(30)
85            InputFloatHint({placeholder: '请输入密码'})
86          Column() {
87            Row() {
88              Checkbox()
89                .select(false)
90                .selectedColor(0x39a2db)
91                .padding({left:1})
92              Text('免密登录')
93              Blank().width(150)
94              Text('忘记密码')
95                .fontColor('#5C9DBA')
96            }
97          }.justifyContent(FlexAlign.SpaceBetween)
98            Blank().height(50)
99            Button('登录').width(150)
100          }
101          .width('90%')
102          .padding(30)
103          .backgroundColor(Color.White)
104          .border({radius: 10})
105          .shadow({radius: 10, color: '#5C9DBA' })
106        }.width('100%')
107        .height('100%')
108        .justifyContent(FlexAlign.Center)
109      }
110    }
111    ```
112## 完整代码
113完整示例代码如下:
114```ts
115@Component
116struct InputFloatHint{
117  @State isFloatHint: boolean = false
118  @State textValue: string = ''
119  isEditing = false
120  placeholder: string
121  typeInput:InputType = InputType.Normal
122
123  build(){
124    Stack(){
125      TextInput({text:this.textValue})
126        .width('100%')
127        .height('100%')
128        .borderRadius(0)
129        .borderColor('#86C7CC')
130        .borderWidth({bottom: 1})
131        .backgroundColor(Color.Transparent)
132        .type(this.typeInput)
133        .showPasswordIcon(false)
134        .onEditChange((isEditing)=>{
135          this.isEditing = isEditing
136          this.isFloatHint = this.isEditing || this.textValue !==''
137        })
138        .onChange((value)=>{
139          this.textValue = value
140          this.isFloatHint = this.isEditing || this.textValue !== ''
141        })
142      Text(this.placeholder)
143        .enabled(false)
144        .fontColor(this.isFloatHint ?  '#5D7DB3' : '#E1E4EA')
145        .position({x: 0, y: this.isFloatHint ? 0 : '50%'})
146        .translate({x: 15, y: '-50%'})
147        .animation({duration:100, curve: Curve.Linear})
148
149      Line()
150        .width('100%')
151        .height(1)
152        .backgroundColor('#5D7DB3')
153        .position({x: 0, y: 44})
154        .scale({x: this.isFloatHint ? 1 : 0, centerX: 0})
155        .animation({duration: 300, curve: Curve.Linear})
156
157      if (this.textValue){
158        Image($r("app.media.canel"))
159          .width(15)
160          .height(15)
161          .border({width: 1, radius: 15, color: '#fffffff'})
162          .position({x: '90%', y: '50%'})
163          .translate({y: '-50%'})
164          .onClick(()=>{
165            this.textValue = ''
166          })
167      }
168    }.width('100%').height(45)
169  }
170}
171
172@Entry
173@Component
174struct InputFloatHintPage{
175  build(){
176    Column(){
177      Text('欢迎使用')
178        .fontSize(50)
179        .fontColor('#91B5A9')
180      Column(){
181        InputFloatHint({placeholder: '账号'})
182        Blank().height(30)
183        InputFloatHint({placeholder: '密码'})
184        Blank().height(50)
185        Button('登录').width(150)
186      }
187      .width('90%')
188      .padding(30)
189      .backgroundColor(Color.White)
190      .border({radius: 10})
191      .shadow({radius: 10, color: '#5C9DBA' })
192    }.width('100%')
193    .height('100%')
194    .justifyContent(FlexAlign.Center)
195  }
196}
197```
198
199## 参考
200[图形变换](../application-dev/reference/apis-arkui/arkui-ts/ts-universal-attributes-transformation.md)
201
202[TextInput](../application-dev/reference/apis-arkui/arkui-ts/ts-basic-components-textinput.md)
203