自定义组件------带删除功能的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。

时间: 2024-10-13 11:40:23

自定义组件------带删除功能的EditText的相关文章

【Android-EditText】自定义带删除功能的EditText

我们经常在一些应用中见到输入框带有删除功能,今天我们就来实现这个功能(文字组织能力不强,大家随便看看).主要是记录一下自己的学习经历,如果对大家有帮助,我会更开心的. 先上图: 实现要点: 1.当输入框为空时,删除按钮隐藏: 2.当输入框不为空时,显示删除按钮. 核心代码: package com.example.view; import com.example.ui.R; import android.content.Context; import android.graphics.Rect;

android-android 带删除功能的EditText

DJEditText.java /** * Created by xp.chen on 2016/11/25. */ public class DJEditText extends AppCompatEditText { private static final int DRAWABLE_LEFT = 0; private static final int DRAWABLE_TOP = 1; private static final int DRAWABLE_RIGHT = 2; private

添加删除功能的EditText

EditTextWithDel组件的功能如下:        1.在没用内容的时候显示不可用的图片状态,在有内容的时候显示可用的图片状态:        2.在有内容的时候点击删除按钮可以删除EditText中的内容: 组件的代码如下: [java]view plaincopyprint? package com.sunday.customs; import com.example.customs.R; import android.content.Context; import android

自定义组件之会抖动的edittext

废话休提,先上效果图. android自定义组件,所需重写的方法往往有:两参数的构造方法(布局加载必须).ondraw().ontouchevent().onfocuschanged()等方法,不少的组件还需自定义attributeset.本组件的实现主要是借助ontouchevent.onfocuschanged和textwatcher以及一个自定义动画来实现的. 主要思路是,在onfocuschanged和ontextchanged方法中通过getconpounddrawable()和set

带删除按钮的EditText

在使用输入框的时候,常常需要在输入框后带有一键清除输入内容的按钮.采用自定义View的方式是复用性较高的方法.另一方面也可以采用控件"控件+监听"的较为简单的方法来实现. 布局文件: <LinearLayout     android:layout_width="match_parent"     android:layout_height="0dp"     android:layout_weight="1">

android 自定义组件-带图片的textView

1. 定义属性 <?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="icon_textview"> <attr name="iconSrc" format="reference"/> </declare-styleable> <attr nam

35.Android之带删除按钮EditText学习

今天实现Android里自定义带删除功能的EditText,效果如下: 当输入内容时,EditText变为带有一个删除功能按钮的编辑框,如图: 实现代码很简单,直接上代码, 布局文件xml: 1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 an

带删除的edittext

1 package com.sixin.view; 2 3 import android.content.Context; 4 import android.graphics.Canvas; 5 import android.graphics.Color; 6 import android.graphics.Paint; 7 import android.graphics.Rect; 8 import android.graphics.drawable.Drawable; 9 import an

django中的admin组件之自定义组件的数据展示以及自定义列

虽然我们基本实现了我们自定义组件stark的功能,接下来就是显示数据了. 今天我们先来写查.先来写表单部分 我们如果需要显示我们自己配置类中的字段,应该怎么做呢? 我们有这样一个想法,将这些数据放在一个列表中,类似这样[['python',122],['linux',110]],然后在页面上依次循环这个列表,取值就行了. self.model.objects.all()中里面有我们需要的对象,self.list_display中有我们需要的字段.所以: def listview(self,req