其实早就想写一篇关于自定义控件的文章,可是总感觉没有什么可以下笔的,自定义控件其实并不麻烦,但是总想不出什么实用性比较强的控件。(其实是太难的我也不会,哈哈,不过不要在意这些细节)首先我们要知道,自定义控件分三种类型:1,继承已有的view,然后去重写 2,直接继承view,自己去draw 3,组合控件。今天我要讲的是如何自定义一个删除功能的EditText,很明显属于第一种类型,因为我要继承系统的EditText去实现。
那么我先说一下我的需求:
1)自定义的EditText可以实现清空输入内容的功能,即删除功能
2)删除的图标在输入框内容不为空,并且焦点在当前EditText上时才会显示
效果如下图:
自定义EditText代码如下:
package com.personal.xiaoshuai.customedittextdemo; import android.content.Context; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.text.Editable; import android.text.TextUtils; import android.text.TextWatcher; import android.util.AttributeSet; import android.util.Log; import android.util.TypedValue; import android.view.MotionEvent; import android.view.View; import android.widget.EditText; public class EditTextWithClear extends EditText { private String TAG = EditTextWithClear.class.getSimpleName(); private Drawable dRight; private Rect rBounds; private Context context; private int offset; private boolean isEmpty = true;//输入是否为空,默认为true public EditTextWithClear(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); this.context = context; bindListener(); } public EditTextWithClear(Context context, AttributeSet attrs) { super(context, attrs); this.context = context; bindListener(); } public EditTextWithClear(Context context) { super(context); this.context = context; bindListener(); } private void bindListener() { addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { } @Override public void afterTextChanged(Editable s) { Log.i(TAG, "-----afterTextChanged-----"); isEmpty = TextUtils.isEmpty(s) ? true : false; //输入框内容不为空,并且有焦点 if (!isEmpty && hasFocus()) {//这可以根据自己的需求替换成自己喜欢的删除图片 Drawable drawable = context.getResources().getDrawable(R.drawable.backspace);//这行代码是必须的 否则图片会不显示 drawable.setBounds(0, 0, drawable.getMinimumWidth(), drawable.getMinimumHeight()); setClearDrawable(drawable); } else { setClearDrawable(null); } } }); setOnFocusChangeListener(new OnFocusChangeListener() { @Override public void onFocusChange(View v, boolean hasFocus) { Log.i(TAG, "-----onFocusChange-----"); Log.i(TAG, "hasFocus:" + hasFocus()); //输入框内容不为空,并且有焦点 if (!isEmpty && hasFocus) { Drawable drawable = context.getResources().getDrawable(R.drawable.backspace); drawable.setBounds(0, 0, drawable.getMinimumWidth(), drawable.getMinimumHeight()); setClearDrawable(drawable); } else { setClearDrawable(null); } } }); } /** * 这个方法是配合padding来用的,是根据我自己的实际需求添加的, 大家可以不用关心offset有什么用 * * @param right * @param offset */ public void setClearDrawable(Drawable right, int offset) { this.offset = offset; setCompoundDrawables(getCompoundDrawables()[0], getCompoundDrawables()[1], right, getCompoundDrawables()[3]); } /** * @param right */ public void setClearDrawable(Drawable right) { setCompoundDrawables(getCompoundDrawables()[0], getCompoundDrawables()[1], right, getCompoundDrawables()[3]); } @Override public void setCompoundDrawables(Drawable left, Drawable top, Drawable right, Drawable bottom) { if (right != null) { dRight = right; } super.setCompoundDrawables(left, top, right, bottom); } @Override public boolean onTouchEvent(MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_UP && dRight != null) { rBounds = dRight.getBounds(); final int x = (int) event.getX(); final int y = (int) event.getY(); //判断点击区域是否在删除按钮的区域内 if (x >= (this.getRight() - offset - rBounds.width() - dp2px(context, 15) * 2) && x <= (this.getRight() - this.getPaddingRight()) && y >= this.getPaddingTop() && y <= (this.getHeight() - this.getPaddingBottom()) && getCompoundDrawables()[2] != null ) { this.setText(""); //阻止键盘弹出 event.setAction(MotionEvent.ACTION_CANCEL); } } return super.onTouchEvent(event); } @Override protected void finalize() throws Throwable { dRight = null; rBounds = null; super.finalize(); } /** * dp转px * * @param context * @return */ public int dp2px(Context context, float dpVal) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpVal, context.getResources().getDisplayMetrics()); } }使用就和一般的控件一样,直接用就可以,下面我贴一下我demo的代码
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.personal.xiaoshuai.customedittextdemo.MainActivity" > <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerInParent="true" android:padding="15dp" android:orientation="vertical"> <com.personal.xiaoshuai.customedittextdemo.EditTextWithClear android:id="@+id/et_user" android:layout_width="match_parent" android:layout_height="45dp" android:drawableLeft="@drawable/selector_et_user" android:drawablePadding="5dp" android:hint="用户名" /> <com.personal.xiaoshuai.customedittextdemo.EditTextWithClear android:id="@+id/et_password" android:layout_width="match_parent" android:layout_height="45dp" android:layout_marginTop="25dp" android:drawablePadding="5dp" android:drawableLeft="@drawable/selector_et_password" android:inputType="textPassword" android:hint="密 码" /> </LinearLayout> </RelativeLayout>
selector_et_user.xml
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_focused="true" android:drawable="@drawable/user_focus"/> <item android:state_focused="false" android:drawable="@drawable/user_nomal"/> </selector>selector_et_password.xml类似我就不贴了,Activity中没有逻辑代码,也不贴了。今天就写到这吧,其实写这篇文章成就感不是很强,因为自定义控件的精华所在(自定义控件属性)并没有涉及到,而且在调试过程中遇到点小问题,宝宝心里苦,但是宝宝不说~ 这个算是我的失眠之作了 ( ̄o ̄) . z Z