Android如何自定义EditText下划线?
曾经做过一个项目,其中登录界面的交互令人印象深刻。交互设计师给出了一个非常作的设计,要求做出包含根据情况可变色的下划线,左侧有可变图标,右侧有可变删除标志的输入框,如图
记录制作过程:
第一版本
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(); } }
效果图:
代码解释:
变量名 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 根据不同的状态,左边的图片不一样
存在的问题: 这版本虽然基本功能已经实现,但是不符合需求,设计中要求文本框中无文字时,右侧删除按钮不显示,不点击删除按钮,删除按钮要保持灰色,点击时才可以变蓝色。
因此有了第二个版本
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
当进入界面,点击输入框,要判断输入框中是否已有文字,如果有则显示灰色的删除按钮,如果没有则不显示,如果点击了删除按钮,删除按钮变蓝色
存在的问题: 这个版本依旧存在问题,就是输入长度超过输入框,所画的线不会延伸,如图
解决方法:
@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下划线的方法,希望对大家的学习有所帮助。