自定义EditText实现输入错误变红的效果

本文介绍如何自定义一个EditText,使其在输入错误时显示红色的底部线条、错误文字提示,并实现清除按钮。通过设置OnFocusChangeListener和TextWatcher监听输入状态,动态改变颜色和图片资源,以及错误提示的显示和隐藏。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这次做的项目设计给的效果图是这样的

不选中时线为灰色,获取焦点后高亮变化,若判断错误后将底部线条文字及清楚按钮都变成红色,同时从界面上面要滑动出错误文字提示。

为了改变EditText线条颜色真是尝试了各种办法,但是小女子能力有限,最后用一种笨方法实现了。。

自定义了一个ClearEditText继承EditText实现onFocusChangeListener接口,在自定义view里大概做的事情就是

1、清除背景,注意版本判断

 if (Build.VERSION.SDK_INT>= Build.VERSION_CODES.JELLY_BEAN) {
            setBackground(
null);
       
} else {
            setBackgroundDrawable(
null);
       
}

2、重写onFocusChange方法,判断当前是错误状态还是正常状态,从而设置颜色及图片资源(其中正常状态时分选中和未选中直接使用selector定义一个xml文件即可)

@Override
   
public void onFocusChange(View v, booleanhasFocus) {
       
if(hasFocus&&this.getText().toString().trim().length()>0){
            setClearIconVisible(
true);//设置右侧清楚按钮为显示
       
}else{
            setClearIconVisible(
false);//不显示清楚按钮
       
}
       
if(!hasFocus && errorState){//若在显示错误样式时失去焦点,则变成正常状态
           
errorState=false;
           
showBottomLine(true);
       
}
    }

 /**
     * 设置清除图标的显示与隐藏,调用setCompoundDrawables为EditText绘制上去
     * @param
visible
    
*/
   
public void setClearIconVisible(booleanvisible) {
        Drawable right = visible ?
mClearDrawable : null;
       
setCompoundDrawables(getCompoundDrawables()[0],
               
getCompoundDrawables()[1],right, getCompoundDrawables()[3]);
   
}

  /**
     * 显示底部线条
     * @param
normal
    
*/
   
public void showBottomLine(booleannormal){
       
if(normal){//正常状态
            setTextColor(
textColor);
           
Drawable right = (getText().length() > 0) ? mClearDrawable: null;
           
setCompoundDrawables(getCompoundDrawables()[0],
                   
getCompoundDrawables()[1],right, mBottomLineNormal);
       
}else{//错误状态
            setTextColor(
textErrorColor);
           
setCompoundDrawables(getCompoundDrawables()[0],
                   
getCompoundDrawables()[1],mErrorClearDrawable, mBottomLineError);//在设置错误的红色底部线条时将右侧清楚按钮也换成红色X号图片
       
}
    }

注:其中drawable图片初始化时必须设置bounds否则不显示,用法如下

  mClearDrawable= getCompoundDrawables()[2];
        if
(mClearDrawable== null) {
           
mClearDrawable= getResources().getDrawable(R.drawable.search_clear);
       
}

mClearDrawable.setBounds(0,0, mClearDrawable.getIntrinsicWidth(),mClearDrawable.getIntrinsicHeight());//必须设置,否则不显示


3、设置TextWatcher,今天编辑内容发生变化后恢复正常状态

//设置输入框里面内容发生改变的监听
       
addTextChangedListener(newTextWatcher() {
           
/**
             * 当输入框里面内容发生变化的时候回调的方法
             */
           
@Override
           
public void onTextChanged(CharSequence s, intstart, int count,
                                      int
after) {
               
setClearIconVisible(s.length() > 0);
               show(s);
                if
(null !=errorView && errorView.getVisibility() == View.VISIBLE) {//若设置了自定义的屏幕上方的view且为显示状态则隐藏
                   
errorView.clearErrorMessage();
               
}
               
if(errorState){
                   
errorState=false;
                   
showBottomLine(true);//设置文字颜色、底部和右侧图片为正常状态的图片
               
}
            }
           
@Override
           
public void beforeTextChanged(CharSequence s, intstart, int count,  intafter) {
            }
           
@Override
           
public void afterTextChanged(Editable s) {
            }
        })
;
   


4、其中屏幕上方错误文字提示是自定义的Relativlayout布局(因为怕亲耐的设计同学还要其它的内容没有直接使用TextView,呵呵),我命名为ErrorView,添加设置清除错误提示的方法,在设置的同时开启动画下滑显示,在清除的时候上滑隐藏。

//显示动画
       
mShowAction = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.0f,
               
Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF,
               
-1.0f, Animation.RELATIVE_TO_SELF, 0.0f);
       
mShowAction.setDuration(500);
       
//隐藏动画
       
mHiddenAction = new TranslateAnimation(Animation.RELATIVE_TO_SELF,
               
0.0f, Animation.RELATIVE_TO_SELF, 0.0f,
               
Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF,
               
-1.0f);
       
mHiddenAction.setDuration(500);
       
mHiddenAction.setAnimationListener(new Animation.AnimationListener() {
           
@Override
           
public void onAnimationStart(Animation animation) {

            }

           
@Override
           
public void onAnimationEnd(Animation animation) {
               
tv_message.setText("");
           
}

           
@Override
           
public void onAnimationRepeat(Animation animation) {

            }
        })
;


public void setErrorMessage(String errorMessage){
       
tv_message.setText(errorMessage);
       
startAnimation(mShowAction);
       
setVisibility(View.VISIBLE);
   
}

   
public void clearErrorMessage(){
        startAnimation(
mHiddenAction);
       
ErrorView.this.setVisibility(View.INVISIBLE);

   
}

5,在自定义EditText中添加设置ErroView的方法,并重写setError方法,若设置了ErroView就用它显示,若没有则用默认方式。

public void setErrorView(ErrorView errorView) {
       
this.errorView = errorView;
   
}

   
@Override
   
public void setError(CharSequence error) {
       
if(null != errorView){
           
errorState=true;
           
errorView.setErrorMessage(error.toString());
           
showBottomLine(false);
       
}else{
           
super.setError(error);
       
}

    }

6、在使用的时候设置ErrorView,如edittext1.setErrorView(errorView);

当需要显示错误的时候edittext1.setError("你输入的号码有误");即可

源代码本来想上传的没找到地方。。。想粘贴上来文章又不全。。。上面写的应该都能看懂了吧,再粘一次试试。

package com.jimubox.jimustock.view.weight;


import
android.content.Context;
import
android.graphics.drawable.Drawable;
import
android.os.Build;
import
android.text.Editable;
import
android.text.TextWatcher;
import
android.util.AttributeSet;
import
android.view.MotionEvent;
import
android.view.View;
import
android.view.View.OnFocusChangeListener;
import
android.widget.EditText;

import
com.jimubox.jimustock.R;
import
com.jimubox.jimustock.constant.SPConstant;
import
com.jimubox.jimustock.utils.SPUtility;

public class
ClearEditText extends EditText implements OnFocusChangeListener {
   
/**
     * 删除按钮的引用
     */
   
private Drawable mClearDrawable;
    private
Drawable mBottomLineNormal;//底部线条
   
private Drawable mBottomLineError;//错误时显示
   
/**
     * 控件是否有焦点
     */
   
private boolean hasFoucs;
   
/**
     * 是否开启6222 2316 1256和138 8888 8888
     * 银行卡号和电话号码显示方式
     * 默认显示银行卡号显示方式
     */
   
public boolean showType;
   
/**
     * 开启电话号码显示方式
     */
   
public boolean showMobileType;

   
//是否输入
   
private boolean isRun = false;
   
//输入的内容
   
private String inputStr = "";


    private
ErrorView errorView;
    private boolean
errorState;
    private int
textColor;
    private int
textErrorColor;
    private
Drawable mErrorClearDrawable;
    private
Context context;
    public
ClearEditText(Context context, AttributeSet attrs) {
       
//这里构造方法也很重要,不加这个很多属性不能再XML里面定义
       
this(context, attrs, android.R.attr.editTextStyle);
        this
.context=context;
   
}

   
public ClearEditText(Context context, AttributeSet attrs, int defStyle) {
       
super(context, attrs, defStyle);
       
init(context);
   
}


   
private void init(Context context) {
       
this.context=context;
       
errorState=false;
       
//获取EditText的DrawableRight,假如没有设置我们就使用默认的图片
       
mClearDrawable = getCompoundDrawables()[2];
        if
(mClearDrawable == null) {
           
mClearDrawable = getResources().getDrawable(R.drawable.search_clear);
       
}
       
mClearDrawable.setBounds(0, 0, mClearDrawable.getIntrinsicWidth(), mClearDrawable.getIntrinsicHeight());
        if
(mBottomLineNormal==null){
           
mBottomLineNormal = getResources().getDrawable(R.drawable.edittext_bg);
       
}
       
if(mBottomLineError == null){
           
mBottomLineError = getResources().getDrawable(R.drawable.line4);
       
}
       
mBottomLineNormal.setBounds(0, 0, mBottomLineNormal.getIntrinsicWidth(), mBottomLineNormal.getIntrinsicHeight());
       
mBottomLineError.setBounds(0, 0, mBottomLineError.getIntrinsicWidth(), mBottomLineError.getIntrinsicHeight());
        if
(mErrorClearDrawable == null){
           
mErrorClearDrawable = getResources().getDrawable(R.drawable.close_red);
       
}
       
mErrorClearDrawable.setBounds(0, 0, mErrorClearDrawable.getIntrinsicWidth(), mErrorClearDrawable.getIntrinsicHeight());
        if
(SPUtility.getBoolean2SP(context, SPConstant.SP_THEME_WHITE)){
           
textColor=getResources().getColor(R.color.mainTextColor_white);
       
}else {
           
textColor=getResources().getColor(R.color.mainTextColor);
       
}
       
textErrorColor=getResources().getColor(R.color.red_statusColor);

       
showBottomLine(true);
        if
(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            setBackground(
null);
       
} else {
            setBackgroundDrawable(
null);
       
}
       
//默认设置隐藏图标
       
setClearIconVisible(false);
       
//设置焦点改变的监听
       
setOnFocusChangeListener(this);
       
//设置输入框里面内容发生改变的监听
       
addTextChangedListener(new TextWatcher() {
           
/**
             * 当输入框里面内容发生变化的时候回调的方法
             */
           
@Override
           
public void onTextChanged(CharSequence s, int start, int count,
                                      int
after) {
//                if(hasFoucs){
               
setClearIconVisible(s.length() > 0);
//                }
               
show(s);
                if
(null != errorView && errorView.getVisibility() == View.VISIBLE) {
                   
errorView.clearErrorMessage();

               
}
               
if(errorState){
                   
errorState=false;
                   
showBottomLine(true);
               
}
            }

           
@Override
           
public void beforeTextChanged(CharSequence s, int start, int count,
                                          int
after) {
            }

           
@Override
           
public void afterTextChanged(Editable s) {

            }
        })
;
   
}


   
/**
     * 因为我们不能直接给EditText设置点击事件,所以我们用记住我们按下的位置来模拟点击事件
     * 当我们按下的位置 在  EditText的宽度 - 图标到控件右边的间距 - 图标的宽度  和
     * EditText的宽度 - 图标到控件右边的间距之间我们就算点击了图标,竖直方向就没有考虑
     */
   
@Override
   
public boolean onTouchEvent(MotionEvent event) {
       
if (event.getAction() == MotionEvent.ACTION_UP) {
           
if (getCompoundDrawables()[2] != null) {
               
boolean touchable = event.getX() > (getWidth() - getTotalPaddingRight())
                        && (event.getX() < ((getWidth() - getPaddingRight())))
;
                if
(touchable) {
                   
this.setText("");
               
}
            }
        }
       
return super.onTouchEvent(event);
   
}


   
/**
     * 设置清除图标的显示与隐藏,调用setCompoundDrawables为EditText绘制上去
     * @param
visible
    
*/
   
public void setClearIconVisible(boolean visible) {
        Drawable right = visible ?
mClearDrawable : null;
       
setCompoundDrawables(getCompoundDrawables()[0],
               
getCompoundDrawables()[1], right, getCompoundDrawables()[3]);
   
}

   
/**
     * 开启银行卡号和电话号码显示方式
     */
   
private void show(CharSequence s){
       
if(showType){//开启了银行卡号、电话号码显示方式
           
if(isRun){
               
isRun = false;
                return;
           
}
           
isRun = true;
           
inputStr = "";
           
String newStr = s.toString();
           
newStr = newStr.replace(" ", "");
            int
index = 0;
            if
(showMobileType){//电话号码显示方式
               
if((index + 3)< newStr.length()){
                   
inputStr += (newStr.substring(index, index + 3)+ " ");
                   
index += 3;
               
}
            }
           
while((index + 4)< newStr.length()){
               
inputStr += (newStr.substring(index, index + 4)+ " ");
               
index += 4;
           
}
           
inputStr += (newStr.substring(index, newStr.length()));
            this
.setText(inputStr);
            this
.setSelection(inputStr.length());
       
}
    }

   
@Override
   
public void onFocusChange(View v, boolean hasFocus) {
       
if(hasFocus&&this.getText().toString().trim().length()>0){
            setClearIconVisible(
true);
       
}else{
            setClearIconVisible(
false);
       
}
       
if(!hasFocus && errorState){
           
errorState=false;
           
showBottomLine(true);
       
}
    }


   
public ErrorView getErrorView() {
       
return errorView;
   
}

   
public void setErrorView(ErrorView errorView) {
       
this.errorView = errorView;
   
}

   
@Override
   
public void setError(CharSequence error) {
       
if(null != errorView){
           
errorState=true;
           
errorView.setErrorMessage(error.toString());
           
showBottomLine(false);
       
}else{
           
super.setError(error);
       
}

    }

   
/**
     * 显示底部线条
     * @param
normal
    
*/
   
public void showBottomLine(boolean normal){
       
if(normal){
            setTextColor(
textColor);
           
Drawable right = (getText().length() > 0) ? mClearDrawable : null;
           
setCompoundDrawables(getCompoundDrawables()[0],
                   
getCompoundDrawables()[1], right, mBottomLineNormal);
       
}else{
            setTextColor(
textErrorColor);
           
setCompoundDrawables(getCompoundDrawables()[0],
                   
getCompoundDrawables()[1], mErrorClearDrawable, mBottomLineError);
       
}
    }

}


ErrorView的代码

package com.jimubox.jimustock.view.weight;

import
android.content.Context;
import
android.util.AttributeSet;
import
android.view.LayoutInflater;
import
android.view.View;
import
android.view.animation.Animation;
import
android.view.animation.TranslateAnimation;
import
android.widget.ImageView;
import
android.widget.RelativeLayout;
import
android.widget.TextView;

import
com.jimubox.jimustock.R;

/**
 * Created by tangkaili on 15/6/10.
 */
public class ErrorView extends RelativeLayout{
   
private Context context;
    private
TextView tv_message;
    private
ImageView iv_clean;
    private
TranslateAnimation mShowAction;
    private
TranslateAnimation mHiddenAction;

    public
ErrorView(Context context) {
       
super(context);
       
init(context);
   
}

   
public ErrorView(Context context, AttributeSet attrs) {
       
super(context, attrs);
       
init(context);
   
}

   
public ErrorView(Context context, AttributeSet attrs, int defStyle) {
       
super(context, attrs, defStyle);
       
init(context);
   
}

   
public void init(Context context){
       
this.context=context;
       
View view = LayoutInflater.from(context).inflate(R.layout.errorview_layout,null);
       
tv_message = (TextView)view.findViewById(R.id.error_text);
       
iv_clean = (ImageView)view.findViewById(R.id.error_clear);
       
iv_clean.setOnClickListener(new OnClickListener() {
           
@Override
           
public void onClick(View v) {
                clearErrorMessage()
;
           
}
        })
;
        this
.addView(view);
       
setVisibility(View.INVISIBLE);
       
//显示动画
       
mShowAction = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.0f,
               
Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF,
               
-1.0f, Animation.RELATIVE_TO_SELF, 0.0f);
       
mShowAction.setDuration(500);
       
//隐藏动画
       
mHiddenAction = new TranslateAnimation(Animation.RELATIVE_TO_SELF,
               
0.0f, Animation.RELATIVE_TO_SELF, 0.0f,
               
Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF,
               
-1.0f);
       
mHiddenAction.setDuration(500);
       
mHiddenAction.setAnimationListener(new Animation.AnimationListener() {
           
@Override
           
public void onAnimationStart(Animation animation) {

            }

           
@Override
           
public void onAnimationEnd(Animation animation) {
               
tv_message.setText("");
           
}

           
@Override
           
public void onAnimationRepeat(Animation animation) {

            }
        })
;
   
}

   
public void setErrorMessage(String errorMessage){
       
tv_message.setText(errorMessage);
       
startAnimation(mShowAction);
       
setVisibility(View.VISIBLE);
   
}

   
public void clearErrorMessage(){
        startAnimation(
mHiddenAction);
       
ErrorView.this.setVisibility(View.INVISIBLE);

   
}

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值