自定义控件分三种:

  1. 自绘控件
  2. 组合控件
  3. 继承控件

代码已上传到 github
以后的自定义控件就都放这个仓库

需求

这里由于项目的需要实现一个自定义EditText,主要实现的为两点,一个是工具图标toolIcon,例如点击清除EditText内容。一个为EditText左边的提示图标hintIcon, 例如输入账号密码时前面的图标。

为了让这个控件的拓展性更高,设置了两个点击事件接口。对于toolIcon来说,默认点击事件为清除EditText内容,如果需要更改,在代码中设设置相关的点击事件即可。

步骤

  1. 继承EditText
  2. 编写attrs.xml, 创建declare-styleable
  3. 编写MyEditText
  4. 布局中使用

实现

获取布局文件中设置的属性

这里返回的是一个TypedArray数组,获取之后就可以获得布局文件中设置的属性了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
private void init(Context context, AttributeSet attrs) {
TypedArray typedArray = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.MyEditText,
0, 0);

hintIcon = typedArray.getDrawable(R.styleable.MyEditText_hintIcon);
toolIcon = typedArray.getDrawable(R.styleable.MyEditText_toolIcon);
fixed = typedArray.getBoolean(R.styleable.MyEditText_fixed, true);

if (toolIcon != null && fixed) {
setHeight(toolIcon.getIntrinsicHeight());
}

setCompoundDrawablesWithIntrinsicBounds(hintIcon, null, null, null);

setCompoundDrawablePadding(10);

typedArray.recycle();

onClickListenerWithEditTextToolIcon = new OnClickListenerWithEditTextToolIcon() {
@Override
public void onClick() {
setText("");
}
};
}

设置资源图片
EditText是继承自TextView,在TextView中存在两个方法

1
2
setCompoundDrawablesWithIntrinsicBounds(left, top, right, bottom)
setCompoundDrawables(left, top, right, bottom)

是设置资源图片的位置,第一个方法和第二个方法的区别在于第一个方法中资源图片的大小是由系统来获取图片固有的大小,第二个方法则是需要自己通过LayoutParams设置大小。

设置点击事件
我们通过setCompoundDrawables()等方法设置的图片,而由于在父类中并没有提供相关的图片点击处理接口,因此可以重写onTouchEvent()来实现相关的点击事件,只需要根据我们手指落点或抬起点的位置就可以判断手指是否点击了相关图片。在这里,我选择了手指抬起时处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/**
* Override the touchEvent to judge whether click toolIcon or hintIcon
*
* @param event motionEvent
* @return super
*/
@Override
public boolean onTouchEvent(MotionEvent event) {

if (event.getAction() == MotionEvent.ACTION_UP) {
if (hintIcon != null) {
if (event.getX() < hintIcon.getIntrinsicWidth()
&& event.getX() > 0) {
if (getCompoundDrawables()[0] != null
&& onClickListenerWithEditTextHintIcon != null) {
onClickListenerWithEditTextHintIcon.onClick();
}
}
}

if (toolIcon != null) {
if (event.getX() > (getWidth()
- toolIcon.getIntrinsicWidth())
&& event.getX() < getWidth()) {
if (getCompoundDrawables()[2] != null ) {
onClickListenerWithEditTextToolIcon.onClick();
}
}
}
}

return super.onTouchEvent(event);
}

/**
* interface when click hintIcon
*/
public interface OnClickListenerWithEditTextHintIcon {
void onClick();
}

/**
* interface when click toolIcon
*/
public interface OnClickListenerWithEditTextToolIcon {
void onClick();
}

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
package com.customwidget.lzqwidget.cuswidget;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.EditText;

import com.customwidget.lzqwidget.R;

/**
* Custom widget of EditText with two icon.
* Created by lizhongquan on 16-1-6.
*/
public class MyEditText extends EditText {

private Drawable hintIcon = null;
private Drawable toolIcon = null;

/**
* HintIcon clickListener
*/
private OnClickListenerWithEditTextHintIcon onClickListenerWithEditTextHintIcon = null;

/**
* Default clear the EditText
*/
private OnClickListenerWithEditTextToolIcon onClickListenerWithEditTextToolIcon = null;

/**
* Default fixed the Height
*/
private boolean fixed = true;

public MyEditText(Context context) {
super(context);
}

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

public MyEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}

private void init(Context context, AttributeSet attrs) {
TypedArray typedArray = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.MyEditText,
0, 0);

hintIcon = typedArray.getDrawable(R.styleable.MyEditText_hintIcon);
toolIcon = typedArray.getDrawable(R.styleable.MyEditText_toolIcon);
fixed = typedArray.getBoolean(R.styleable.MyEditText_fixed, true);

if (toolIcon != null && fixed) {
setHeight(toolIcon.getIntrinsicHeight());
}

setCompoundDrawablesWithIntrinsicBounds(hintIcon, null, null, null);

setCompoundDrawablePadding(10);

typedArray.recycle();

onClickListenerWithEditTextToolIcon = new OnClickListenerWithEditTextToolIcon() {
@Override
public void onClick() {
setText("");
}
};
}

/**
* Override the touchEvent to judge whether click toolIcon or hintIcon
*
* @param event motionEvent
* @return super
*/
@Override
public boolean onTouchEvent(MotionEvent event) {

if (event.getAction() == MotionEvent.ACTION_UP) {
if (hintIcon != null) {
if (event.getX() < hintIcon.getIntrinsicWidth()
&& event.getX() > 0) {
if (getCompoundDrawables()[0] != null
&& onClickListenerWithEditTextHintIcon != null) {
onClickListenerWithEditTextHintIcon.onClick();
}
}
}

if (toolIcon != null) {
if (event.getX() > (getWidth()
- toolIcon.getIntrinsicWidth())
&& event.getX() < getWidth()) {
if (getCompoundDrawables()[2] != null ) {
onClickListenerWithEditTextToolIcon.onClick();
}
}
}
}

return super.onTouchEvent(event);
}

/**
* the clickListener of click hintIcon
*
* @param clickListenerOfHintIcon OnClickListenerWithEditTextHintIcon
*/
public void setOnClickListenerWithEditTextHintIcon(
OnClickListenerWithEditTextHintIcon clickListenerOfHintIcon) {
this.onClickListenerWithEditTextHintIcon = clickListenerOfHintIcon;
}

/**
* the clickListener of click toolIcon
*
* @param clickListenerOfToolIcon OnClickListenerWithEditTextToolIcon
*/
public void setOnClickListenerWithEditTextToolIcon(
OnClickListenerWithEditTextToolIcon clickListenerOfToolIcon) {
this.onClickListenerWithEditTextToolIcon = clickListenerOfToolIcon;
}

/**
* onTextChange
*
* @param text text
* @param start start
* @param lengthBefore lengthBefore
* @param lengthAfter lengthAfter
*/
@Override
protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
super.onTextChanged(text, start, lengthBefore, lengthAfter);

if (text.length() > 0 && getCompoundDrawables()[2] == null && toolIcon != null) {
// hintIcon.setBounds(10, 0, 10, 0);
setCompoundDrawablesWithIntrinsicBounds(hintIcon, null, toolIcon, null);
}

if (text.length() == 0 && getCompoundDrawables()[2] != null && toolIcon != null) {
setCompoundDrawablesWithIntrinsicBounds(hintIcon, null, null, null);
}
}

/**
* interface when click hintIcon
*/
public interface OnClickListenerWithEditTextHintIcon {
void onClick();
}

/**
* interface when click toolIcon
*/
public interface OnClickListenerWithEditTextToolIcon {
void onClick();
}
}

attrs.xml:

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="utf-8"?>
<resources>

<declare-styleable name="MyEditText">
<attr name="hintIcon" format="integer" />
<attr name="toolIcon" format="integer" />
<attr name="fixed" format="boolean" />
</declare-styleable>

</resources>