Android如何自定义EditText下划线?

时间:2022-03-05 17:35:33

曾经做过一个项目,其中登录界面的交互令人印象深刻。交互设计师给出了一个非常作的设计,要求做出包含根据情况可变色的下划线,左侧有可变图标,右侧有可变删除标志的输入框,如图

Android如何自定义EditText下划线?

记录制作过程:

第一版本

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
public class lineedittext extends edittext {
 
private paint mpaint;
private int color;
public static final int status_focused = 1;
public static final int status_unfocused = 2;
public static final int status_error = 3;
private int status = 2;
private drawable del_btn;
private drawable del_btn_down;
private int focuseddrawableid = r.drawable.user_select;// 默认的
private int unfocuseddrawableid = r.drawable.user;
private int errordrawableid = r.drawable.user_error;
drawable left = null;
private context mcontext;
public lineedittext(context context) {
 
 super(context);
 mcontext = context;
 init();
}
public lineedittext(context context, attributeset attrs) {
 
 super(context, attrs);
 mcontext = context;
 init();
 
}
public lineedittext(context context, attributeset attrs, int defstryle) {
 
 super(context, attrs, defstryle);
 mcontext = context;
 typedarray a = context.obtainstyledattributes(attrs,
   r.styleable.lineedittext, defstryle, 0);
 focuseddrawableid = a.getresourceid(
   r.styleable.lineedittext_drawablefocus, r.drawable.user_select);
 unfocuseddrawableid = a.getresourceid(
   r.styleable.lineedittext_drawableunfocus, r.drawable.user);
 errordrawableid = a.getresourceid(
   r.styleable.lineedittext_drawableerror, r.drawable.user_error);
 a.recycle();
 init();
}
/** * 2014/7/31 * * @author aimee.zhang */
 
private void init() {
 mpaint = new paint();
 // mpaint.setstyle(paint.style.fill);
 mpaint.setstrokewidth(3.0f);
 color = color.parsecolor("#bfbfbf");
 setstatus(status);
 del_btn = mcontext.getresources().getdrawable(r.drawable.del_but_bg);
 del_btn_down = mcontext.getresources().getdrawable(r.drawable.del_but_bg_down);
 addtextchangedlistener(new textwatcher() {
 
  @override
  public void ontextchanged(charsequence arg0, int arg1, int arg2,
    int arg3) {
  }
 
  @override
  public void beforetextchanged(charsequence arg0, int arg1,
    int arg2, int arg3) {
  }
 
  @override
  public void aftertextchanged(editable arg0) {
   setdrawable();
  }
 });
 setdrawable();
}
 
@override
protected void ondraw(canvas canvas) {
 super.ondraw(canvas);
 mpaint.setcolor(color);
 canvas.drawline(0, this.getheight() - 1, this.getwidth(),
   this.getheight() - 1, mpaint);
}
 
// 删除图片
private void setdrawable() {
 if (length() < 1) {
  setcompounddrawableswithintrinsicbounds(left, null, del_btn, null);
 } else {
  setcompounddrawableswithintrinsicbounds(left, null, del_btn_down,null);
 }
}
 
// 处理删除事件
@override
public boolean ontouchevent(motionevent event) {
 if (del_btn_down != null && event.getaction() == motionevent.action_up) {
  int eventx = (int) event.getrawx();
  int eventy = (int) event.getrawy();
  log.e("eventxy", "eventx = " + eventx + "; eventy = " + eventy);
  rect rect = new rect();
  getglobalvisiblerect(rect);
  rect.left = rect.right - 50;
  if (rect.contains(eventx, eventy))
  settext("");
 }
 return super.ontouchevent(event);
}
 
public void setstatus(int status) {
 this.status = status;
 
 
 if (status == status_error) {
  try {
   left = getresources().getdrawable(errordrawableid);
  } catch (notfoundexception e) {
   e.printstacktrace();
  }
  setcolor(color.parsecolor("#f57272"));
 } else if (status == status_focused) {
  try {
   left = getresources().getdrawable(focuseddrawableid);
  } catch (notfoundexception e) {
   e.printstacktrace();
  }
  setcolor(color.parsecolor("#5e99f3"));
 } else {
  try {
   left = getresources().getdrawable(unfocuseddrawableid);
  } catch (notfoundexception e) {
   e.printstacktrace();
  }
  setcolor(color.parsecolor("#bfbfbf"));
 }
 if (left != null) {
// left.setbounds(0, 0, 30, 40);
 
// this.setcompounddrawables(left, null, null, null);
 
  setcompounddrawableswithintrinsicbounds(left,null,del_btn,null);
 }
 postinvalidate();
}
 
public void setleftdrawable(int focuseddrawableid, int unfocuseddrawableid,
  int errordrawableid) {
 this.focuseddrawableid = focuseddrawableid;
 this.unfocuseddrawableid = unfocuseddrawableid;
 this.errordrawableid = errordrawableid;
 setstatus(status);
}
 
@override
protected void onfocuschanged(boolean focused, int direction,
  rect previouslyfocusedrect) {
 super.onfocuschanged(focused, direction, previouslyfocusedrect);
 if (focused) {
  setstatus(status_focused);
 } else {
  setstatus(status_unfocused);
 }
}
 
@override
protected void finalize() throws throwable {
 super.finalize();
};
 
public void setcolor(int color) {
 this.color = color;
 this.settextcolor(color);
 invalidate();
}
}

效果图:

Android如何自定义EditText下划线?

代码解释:

变量名 status_focused,status_unfocused,status_error 标示了三种状态,选中状况为蓝色,未选中状态为灰色,错误状态为红色。 focuseddrawableid unfocuseddrawableid errordrawableid 存放三种状态的图片,放置于最左侧。

canvas.drawline(0, this.getheight() - 1, this.getwidth(),this.getheight() - 1, mpaint); //画edittext 最下方的线 setcompounddrawableswithintrinsicbounds(left, null, del_btn, null); //放置左边的和右边的图片(左,上,右,下) 相当于 android:drawableleft="" android:drawableright=""

1、ontouchevent 当手机点击时,第一个先执行的函数,当点击右侧删除图标是清空 edittext
2、setstatus 根据不同的状态,左边的图片不一样

存在的问题: 这版本虽然基本功能已经实现,但是不符合需求,设计中要求文本框中无文字时,右侧删除按钮不显示,不点击删除按钮,删除按钮要保持灰色,点击时才可以变蓝色。

因此有了第二个版本

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
public class lineedittext extends edittext implements textwatcher, <br /> onfocuschangelistener{
 
private paint mpaint;
private int color;
public static final int status_focused = 1;
public static final int status_unfocused = 2;
public static final int status_error = 3;
private int status = 2;
private drawable del_btn;
private drawable del_btn_down;
private int focuseddrawableid = r.drawable.user_select;// 默认的
private int unfocuseddrawableid = r.drawable.user;
private int errordrawableid = r.drawable.user_error;
drawable left = null;
private context mcontext;
/**
 * 是否获取焦点,默认没有焦点
 */
private boolean hasfocus = false;
/**
 * 手指抬起时的x坐标
 */
private int xup = 0;
 
public lineedittext(context context) {
 super(context);
 mcontext = context;
 init();
}
 
public lineedittext(context context, attributeset attrs) {
 super(context, attrs);
 mcontext = context;
 init();
 
}
 
public lineedittext(context context, attributeset attrs, int defstryle) {
 super(context, attrs, defstryle);
 mcontext = context;
 typedarray a = context.obtainstyledattributes(attrs,
   r.styleable.lineedittext, defstryle, 0);
 focuseddrawableid = a.getresourceid(
   r.styleable.lineedittext_drawablefocus, r.drawable.user_select);
 unfocuseddrawableid = a.getresourceid(
   r.styleable.lineedittext_drawableunfocus, r.drawable.user);
 errordrawableid = a.getresourceid(
   r.styleable.lineedittext_drawableerror, r.drawable.user_error);
 a.recycle();
 init();
}
 
/**
 * 2014/7/31
 *
 * @author aimee.zhang
 */
private void init() {
 mpaint = new paint();
 // mpaint.setstyle(paint.style.fill);
 mpaint.setstrokewidth(3.0f);
 color = color.parsecolor("#bfbfbf");
 setstatus(status);
 del_btn = mcontext.getresources().getdrawable(r.drawable.del_but_bg);
 del_btn_down = mcontext.getresources().getdrawable(r.drawable.del_but_bg_down);
 addlisteners();
 setcompounddrawableswithintrinsicbounds(left, null, null, null);
}
 
@override
protected void ondraw(canvas canvas) {
 super.ondraw(canvas);
 mpaint.setcolor(color);
 canvas.drawline(0, this.getheight() - 1, this.getwidth(),
   this.getheight() - 1, mpaint);
}
 
// 删除图片
// private void setdrawable() { // if (length() < 1) { // setcompounddrawableswithintrinsicbounds(left, null, null, null); // } else { // setcompounddrawableswithintrinsicbounds(left, null, del_btn,null); // } // }
 
// 处理删除事件
@override
public boolean ontouchevent(motionevent event) {
 if (del_btn != null && event.getaction() == motionevent.action_up) {
  // 获取点击时手指抬起的x坐标
  xup = (int) event.getx();
  log.e("xup", xup+"");
  /*rect rect = new rect();
  getglobalvisiblerect(rect);
  rect.left = rect.right - 50;*/
   // 当点击的坐标到当前输入框右侧的距离小于等于 getcompoundpaddingright() 的距离时,则认为是点击了删除图标
  if ((getwidth() - xup) <= getcompoundpaddingright()) {
   if (!textutils.isempty(gettext().tostring())) {
    settext("");
   }
  }
 }else if(del_btn != null && event.getaction() == motionevent.action_down && gettext().length()!=0){
  setcompounddrawableswithintrinsicbounds(left,null,del_btn_down,null);
 }else if(gettext().length()!=0){
  setcompounddrawableswithintrinsicbounds(left,null,del_btn,null);
 }
 return super.ontouchevent(event);
}
 
public void setstatus(int status) {
 this.status = status;
 
 
 if (status == status_error) {
  try {
   left = getresources().getdrawable(errordrawableid);
  } catch (notfoundexception e) {
   e.printstacktrace();
  }
  setcolor(color.parsecolor("#f57272"));
 } else if (status == status_focused) {
  try {
   left = getresources().getdrawable(focuseddrawableid);
  } catch (notfoundexception e) {
   e.printstacktrace();
  }
  setcolor(color.parsecolor("#5e99f3"));
 } else {
  try {
   left = getresources().getdrawable(unfocuseddrawableid);
  } catch (notfoundexception e) {
   e.printstacktrace();
  }
  setcolor(color.parsecolor("#bfbfbf"));
 }
 if (left != null) {
// left.setbounds(0, 0, 30, 40); // this.setcompounddrawables(left, null, null, null); setcompounddrawableswithintrinsicbounds(left,null,null,null); } postinvalidate(); }
 
public void setleftdrawable(int focuseddrawableid, int unfocuseddrawableid,
  int errordrawableid) {
 this.focuseddrawableid = focuseddrawableid;
 this.unfocuseddrawableid = unfocuseddrawableid;
 this.errordrawableid = errordrawableid;
 setstatus(status);
}
 private void addlisteners() {
  try {
   setonfocuschangelistener(this);
   addtextchangedlistener(this);
  } catch (exception e) {
   e.printstacktrace();
  }
 }
@override
protected void onfocuschanged(boolean focused, int direction,
  rect previouslyfocusedrect) {
 super.onfocuschanged(focused, direction, previouslyfocusedrect);
 this.hasfocus=focused;
 if (focused) {
  setstatus(status_focused);
 } else {
  setstatus(status_unfocused);
  setcompounddrawableswithintrinsicbounds(left,null,null,null);
 }
}
 
@override
protected void finalize() throws throwable {
 super.finalize();
};
 
public void setcolor(int color) {
 this.color = color;
 this.settextcolor(color);
 invalidate();
}
 
 
 
@override
public void aftertextchanged(editable arg0) {
 // todo auto-generated method stub
 postinvalidate();
}
 
@override
public void beforetextchanged(charsequence arg0, int arg1, int arg2,
  int arg3) {
 // todo auto-generated method stub
  if (textutils.isempty(arg0)) {
   // 如果为空,则不显示删除图标
   setcompounddrawableswithintrinsicbounds(left, null, null, null);
  } else {
   // 如果非空,则要显示删除图标
   setcompounddrawableswithintrinsicbounds(left, null, del_btn, null);
  }
}
@override
 public void ontextchanged(charsequence s, int start, int before, int after) {
 if (hasfocus) {
  if (textutils.isempty(s)) {
   // 如果为空,则不显示删除图标
   setcompounddrawableswithintrinsicbounds(left, null, null, null);
  } else {
   // 如果非空,则要显示删除图标
   setcompounddrawableswithintrinsicbounds(left, null, del_btn, null);
  }
 }
}
 
@override
public void onfocuschange(view arg0, boolean arg1) {
 // todo auto-generated method stub
 try {
  this.hasfocus = arg1;
 } catch (exception e) {
  e.printstacktrace();
 }
}
}

比较关键的方法是:ontouchevent

当进入界面,点击输入框,要判断输入框中是否已有文字,如果有则显示灰色的删除按钮,如果没有则不显示,如果点击了删除按钮,删除按钮变蓝色

存在的问题: 这个版本依旧存在问题,就是输入长度超过输入框,所画的线不会延伸,如图

Android如何自定义EditText下划线?

解决方法:

?
1
2
3
4
5
6
7
8
9
10
@override
 
protected void ondraw(canvas canvas) {
 super.ondraw(canvas);
 mpaint.setcolor(color);
 int x=this.getscrollx();
 int w=this.getmeasuredwidth();
 canvas.drawline(0, this.getheight() - 1, w+x,
   this.getheight() - 1, mpaint);
}

w:获取控件长度

x:延伸后的长度

最终效果:

Android如何自定义EditText下划线?

以上就是android实现自定义的edittext下划线的方法,希望对大家的学习有所帮助。