首页 > 代码库 > 自定义组件------带删除功能的EditText

自定义组件------带删除功能的EditText

以前在为EditText添加左侧图标,以及右侧一个删除按钮时,经常是使用FrameLayout,当这样代码复用差,维护也麻烦。最好的方法是重写EditText实现该功能。现在看看效果图,后面再讲解实现方式。

技术分享

重写之后的组件有如下功能,只有当EditText内容不为空,而且获得焦点,才会出现删除按钮,点击删除按钮则清空内容。代码如下:

public class CleanableEditText extends EditText {
	
	//回调函数
	private TextWatcherCallBack mCallback;
	//右侧删除图标
	private Drawable mDrawable;
	private Context mContext;

	public void setCallBack(TextWatcherCallBack mCallback) {
		this.mCallback = mCallback;
	}

	public CleanableEditText(Context context) {
		super(context);
		this.mContext = context;
		init();
	}

	public CleanableEditText(Context context, AttributeSet attrs) {
		super(context, attrs);
		this.mContext = context;
		init();
	}

	public CleanableEditText(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		this.mContext = context;
		init();
	}

	public void init() {
		mDrawable = mContext.getResources().getDrawable(R.drawable.ic_clear);
		mCallback = null;
		//重写了TextWatcher,在具体实现时就不用每个方法都实现,减少代码量
		TextWatcher textWatcher = new TextWatcherAdapter() {
			@Override
			public void afterTextChanged(Editable s) {
				//更新状态,检查是否显示删除按钮
				updateCleanable(length(), true);
				//如果有在activity中设置回调,则此处可以触发
				if(mCallback != null)
					mCallback.handleMoreTextChanged();
			}
		};
		this.addTextChangedListener(textWatcher);
		this.setOnFocusChangeListener(new OnFocusChangeListener() {
			
			@Override
			public void onFocusChange(View v, boolean hasFocus) {
				//更新状态,检查是否显示删除按钮
				updateCleanable(length(), hasFocus);
			}
		});
	}

	//当内容不为空,而且获得焦点,才显示右侧删除按钮
	public void updateCleanable(int length, boolean hasFocus){
		if(length() > 0 && hasFocus)
			setCompoundDrawablesWithIntrinsicBounds(null, null, mDrawable, null);
		else
			setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
	}
	

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		final int DRAWABLE_RIGHT = 2;
		//可以获得上下左右四个drawable,右侧排第二。图标没有设置则为空。
		Drawable rightIcon = getCompoundDrawables()[DRAWABLE_RIGHT];
		if (rightIcon != null && event.getAction() == MotionEvent.ACTION_UP) {
			//检查点击的位置是否是右侧的删除图标
			//注意,使用getRwwX()是获取相对屏幕的位置,getX()可能获取相对父组件的位置
			int leftEdgeOfRightDrawable = getRight() - getPaddingRight()
					- rightIcon.getBounds().width();
			if (event.getRawX() >= leftEdgeOfRightDrawable) {
				setText("");
			}
		}
		return super.onTouchEvent(event);
	}

	@Override
	protected void finalize() throws Throwable {
		mDrawable = null;
		super.finalize();
	}
}

实现的关键有两点,其中一点是得知道有API可以为EditText设置上下左右的图标,所以,就可以避免使用FrameLayout那种笨拙的方法(此处右侧图标在组件的代码中自动加入了,左侧图标则需要在XML代码中声明)。需要注意的另一点是得知道如何计算点击事件的位置,


实现过程遇到的一个小问题,在onTouchEvent()方法中,如果消耗事件(依据情况返回true或者false),则会出现一个问题,可以点击EditText,如果设置日志输出,可以发现action_down,action_move,action_up都输出了,代表点击事件正常,但是依然无法获得焦点。所以不难猜测EditText获得焦点可能和点击事件有关。如果强制调用requestFocus()方法,则可以“解决”这个问题,但是存在不稳定现象,有时会出bug,其中原因还没细究。于是,此处我不处理点击事件,直接返回super.onTouchEvent(event)


编码上用了几个小技巧

1,addTextChangedListener时发现经常需要实现三个方法,但是我们又只需重写一个,显得代码有点冗余,解决方式是增加一个adapter。

2,此处已经addTextChangedListener了,那么如果我在activity中也需要监听呢,如果直接监听,则会覆盖CleanableEditText中的监听。为了解决这个方法,我使用了一个回调接口,使用户在addTextChangedListener中有选择的做更多事情。实现方式如下

回调接口的代码如下:

public interface TextWatcherCallBack {
	public void handleMoreTextChanged();
}

activity需要实现TextWatcherCallBack,并且为CleanableEditText设置callback(如以下代码最后两句)


public class LoginActivity extends BaseActivity implements TextWatcherCallBack {

	private ClearableAutoCompleteTextView accountView;
	private CleanableEditText passwordText;
	private Button login;

	@Override
	protected void onCreate(Bundle bundle) {
		super.onCreate(bundle);
		setContentView(R.layout.activity_login);
		accountView = (ClearableAutoCompleteTextView) findViewById(R.id.et_account);
		passwordText = (CleanableEditText) findViewById(R.id.et_password);
		login = (Button) findViewById(R.id.bt_login);
		accountView.setCallBack(this);
		passwordText.setCallBack(this);

		//,,,,,,,
	}
}


该自定义组件的用法写到这,现在已用于我的一个XMPP即时通讯工具,托管在Github上。

传送门:Github地址

master分支是刚入门android写的代码,很差很渣,我自己都看不下去,不过唯一的价值是让我现在能参考smack开发包的API使用,当时也给我写android入门练手了。develop分支是痛下决心重新写的,今晚刚撸了登陆界面。接下来业余时间主要就维护这个项目,尽量多用上android各种知识。并写博客记录这些知识。欢迎fork欢迎提issue。







自定义组件------带删除功能的EditText