拿QQ的登陆来说吧,效果如下:
当点击密码框右侧的小×图标时输入的内容就都清空了,真的很方便,我之前在项目中也自定义过这种效果的输入框并且在项目中一直使用它,在此期间并没有发现什么Bug,之前的自定义结构如下:
实现方式是使用一个RelativeLayout,它包含了三个控件,两边是ImageView控件,中间是EditText控件,当点击右侧清除按钮后就可以清除输入框的内容了,但是最近在做产品优化的时候感觉之前写的这个自定义控件在代码量上来说有点浪费,明明Android的EditText有drawableLeft和drawableRight属性,为什么不去直接使用呢?于是用笔在草稿纸上画了画,花点时间又重新写了一个具有同样效果的输入框,并且在代码量上来说简化了不少。
工程中ClearEditText就是新定义的带清除功能的输入框,主要是继承了EditText为了利用它的drawableLeft和drawableRight属性,那么赶紧看看它的实现吧
- public class ClearEditText extends EditText implements TextWatcher,
- OnFocusChangeListener {
- /**
- * 左右两侧图片资源
- */
- private Drawable left, right;
- /**
- * 是否获取焦点,默认没有焦点
- */
- private boolean hasFocus = false;
- /**
- * 手指抬起时的X坐标
- */
- private int xUp = 0;
- public ClearEditText(Context context) {
- this(context, null);
- }
- public ClearEditText(Context context, AttributeSet attrs) {
- this(context, attrs, android.R.attr.editTextStyle);
- }
- public ClearEditText(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- initWedgits();
- }
- /**
- * 初始化各组件
- * @param attrs
- * 属性集
- */
- private void initWedgits() {
- try {
- // 获取drawableLeft图片,如果在布局文件中没有定义drawableLeft属性,则此值为空
- left = getCompoundDrawables()[0];
- // 获取drawableRight图片,如果在布局文件中没有定义drawableRight属性,则此值为空
- right = getCompoundDrawables()[2];
- initDatas();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- /**
- * 初始化数据
- */
- private void initDatas() {
- try {
- // 第一次显示,隐藏删除图标
- setCompoundDrawablesWithIntrinsicBounds(left, null, null, null);
- addListeners();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- /**
- * 添加事件监听
- */
- private void addListeners() {
- try {
- setOnFocusChangeListener(this);
- addTextChangedListener(this);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count,
- int after) {
- }
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int after) {
- if (hasFocus) {
- if (TextUtils.isEmpty(s)) {
- // 如果为空,则不显示删除图标
- setCompoundDrawablesWithIntrinsicBounds(left, null, null, null);
- } else {
- // 如果非空,则要显示删除图标
- if (null == right) {
- right = getCompoundDrawables()[2];
- }
- setCompoundDrawablesWithIntrinsicBounds(left, null, right, null);
- }
- }
- }
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- try {
- switch (event.getAction()) {
- case MotionEvent.ACTION_UP:
- // 获取点击时手指抬起的X坐标
- xUp = (int) event.getX();
- // 当点击的坐标到当前输入框右侧的距离小于等于getCompoundPaddingRight()的距离时,则认为是点击了删除图标
- // getCompoundPaddingRight()的说明:Returns the right padding of the view, plus space for the right Drawable if any.
- if ((getWidth() - xUp) <= getCompoundPaddingRight()) {
- if (!TextUtils.isEmpty(getText().toString())) {
- setText("");
- }
- }
- break;
- default:
- break;
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- return super.onTouchEvent(event);
- }
- @Override
- public void afterTextChanged(Editable s) {
- }
- @Override
- public void onFocusChange(View v, boolean hasFocus) {
- try {
- this.hasFocus = hasFocus;
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
解释一下ClearEditText的执行顺序,它首先继承了EditText也就是说具有了EditText的所有功能,然后又实现了TextWatcher和OnFocusChangeListener接口,其中接口OnFocusChangeListener是用来监听当前输入框是否获取到焦点的,我们把当前输入框获取到的焦点的状态值赋给hasFocus成员变量,如果获取到了焦点则hasFocus的值为true,当在输入框中输入内容的时候会触发TextWatcher监听器,就会顺序执行beforeTextChanged,onTextChanged和afterTextChanged方法,而我们仅仅需要在onTextChanged方法中对输入框的值进行判断就行了,如果输入框框的值非空就显示清空按钮小图标否则不显示,设置图标隐藏和显示的方法就是直接调用setCompoundDrawablesWithIntrinsicBounds方法就行了,这个方法的具体使用就不详解了,大体就是说按照我们给定的图片尺寸把图片画到输入框的四个方向上,如果传入值为null就表示不绘制。
接下来就是对清空小图标进行监听了,我首先想到的是如果清空小图标是显示的,就给他设置事件监听比如onClickListener或者是onTouchListener,但是遗憾的是找了API并没有发现EditText提供对drawLeft或者是drawRight的事件监听,又来又打算自定义一个监听器但仔细想想又把代码复杂化了,其实EditText是间接继承View的而View中有个onTouchEvent方法,我们可以重写onTouchEvent方法并在它里边根据我们手指摁下或者是抬起的坐标进行判断,如果点击的位置到当前控件右侧的距离小于等于当前控件右侧小图标的宽度加上paddingRight的值,则我们可以直接认为是点击了清除小图标,就可以对当前控件进行清空操作了,幸好EditText给我们提供了一个我现在认为是非常实在的方法:getCompoundPaddingRight(),文档的解释就是:Returns the right padding of the view, plus space for the right Drawable if any.它的值就是清空小图标的宽度加上paddingRight的值,呵呵,有了它就好办多了,(*^__^*) 嘻嘻……
好了,接下来我们来看看在布局文件中怎么使用它
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:background="#ffffff">
- <TextView
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_margin="10dip"
- android:text="@string/hello"
- android:gravity="center"
- android:textSize="20sp"
- android:textColor="#000000" />
- <com.llew.e.clear.view.wedgit.ClearEditText
- android:id="@+id/clearEditText"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_margin="10dip"
- android:paddingRight="8dip"
- android:paddingTop="5dip"
- android:paddingBottom="5dip"
- android:hint="请输入QQ号码"
- android:background="@drawable/pay_widget_input"
- android:drawableLeft="@drawable/super_qq"
- android:drawableRight="@drawable/clear_normal_list" />
- </LinearLayout>
就是写完整我们自定义的包名就行了,属性用的全是父类中的,再次感谢Java给我们提供的继承特性,呵呵,
在运行之前还是贴出来MainActivity代码吧,其实真的很简单:
- public class MainActivity extends Activity {
- /** Called when the activity is first created. */
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- }
- }
输入前:
输入后:
点击清除图标后: