热门标签 | HotTags
当前位置:  开发笔记 > Android > 正文

Android如何自定义EditText下划线?

Android如何自定义EditText下划线?本文教大家利用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, 
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下划线的方法,希望对大家的学习有所帮助。


推荐阅读
  • QUIC协议:快速UDP互联网连接
    QUIC(Quick UDP Internet Connections)是谷歌开发的一种旨在提高网络性能和安全性的传输层协议。它基于UDP,并结合了TLS级别的安全性,提供了更高效、更可靠的互联网通信方式。 ... [详细]
  • 本文介绍如何在 Android 中通过代码模拟用户的点击和滑动操作,包括参数说明、事件生成及处理逻辑。详细解析了视图(View)对象、坐标偏移量以及不同类型的滑动方式。 ... [详细]
  • 深入理解 Oracle 存储函数:计算员工年收入
    本文介绍如何使用 Oracle 存储函数查询特定员工的年收入。我们将详细解释存储函数的创建过程,并提供完整的代码示例。 ... [详细]
  • 本文总结了2018年的关键成就,包括职业变动、购车、考取驾照等重要事件,并分享了读书、工作、家庭和朋友方面的感悟。同时,展望2019年,制定了健康、软实力提升和技术学习的具体目标。 ... [详细]
  • 在计算机技术的学习道路上,51CTO学院以其专业性和专注度给我留下了深刻印象。从2012年接触计算机到2014年开始系统学习网络技术和安全领域,51CTO学院始终是我信赖的学习平台。 ... [详细]
  • CSS 布局:液态三栏混合宽度布局
    本文介绍了如何使用 CSS 实现液态的三栏布局,其中各栏具有不同的宽度设置。通过调整容器和内容区域的属性,可以实现灵活且响应式的网页设计。 ... [详细]
  • Linux 系统启动故障排除指南:MBR 和 GRUB 问题
    本文详细介绍了 Linux 系统启动过程中常见的 MBR 扇区和 GRUB 引导程序故障及其解决方案,涵盖从备份、模拟故障到恢复的具体步骤。 ... [详细]
  • 本文介绍了如何使用jQuery根据元素的类型(如复选框)和标签名(如段落)来获取DOM对象。这有助于更高效地操作网页中的特定元素。 ... [详细]
  • 本文将详细介绍如何使用剪映应用中的镜像功能,帮助用户轻松实现视频的镜像效果。通过简单的步骤,您可以快速掌握这一实用技巧。 ... [详细]
  • 深入理解Cookie与Session会话管理
    本文详细介绍了如何通过HTTP响应和请求处理浏览器的Cookie信息,以及如何创建、设置和管理Cookie。同时探讨了会话跟踪技术中的Session机制,解释其原理及应用场景。 ... [详细]
  • 本文介绍如何在 Xcode 中使用快捷键和菜单命令对多行代码进行缩进,包括右缩进和左缩进的具体操作方法。 ... [详细]
  • 本文介绍了一款用于自动化部署 Linux 服务的 Bash 脚本。该脚本不仅涵盖了基本的文件复制和目录创建,还处理了系统服务的配置和启动,确保在多种 Linux 发行版上都能顺利运行。 ... [详细]
  • 在Linux系统中配置并启动ActiveMQ
    本文详细介绍了如何在Linux环境中安装和配置ActiveMQ,包括端口开放及防火墙设置。通过本文,您可以掌握完整的ActiveMQ部署流程,确保其在网络环境中正常运行。 ... [详细]
  • Android 渐变圆环加载控件实现
    本文介绍了如何在 Android 中创建一个自定义的渐变圆环加载控件,该控件已在多个知名应用中使用。我们将详细探讨其工作原理和实现方法。 ... [详细]
  • 如何在WPS Office for Mac中调整Word文档的文字排列方向
    本文将详细介绍如何使用最新版WPS Office for Mac调整Word文档中的文字排列方向。通过这些步骤,用户可以轻松更改文本的水平或垂直排列方式,以满足不同的排版需求。 ... [详细]
author-avatar
兰州老头_337
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有