Android 自定义 View 分类

自定义 View 分类

  1. 继承View重写onDraw方法。实现一些不规则效果。需要自己支持wrap_content,padding也需自己处理。
  2. 继承ViewGroup派生特殊的Layout。实现自定义布局。需要合适的处理ViewGroup的测量和布局这两个过程,并同时处理子元素的测量和布局过程。
  3. 继承特定的View(如TextView)。用于扩展。不需要自己支持wrap_content,padding等。
  4. 继承特定的ViewGroup(如LinearLayout)。用于扩展。不需要处理ViewGroup的测量和布局这两个过程。

继承现有控件

相对而言,这是一种较简单的实现方式。因为大部分核心工作,比如关于控件大小的测量、控件位置的摆放等相关的计算,在系统中都已经实现并封装好,开发人员只要在其基础上进行一些扩展,并按照自己的意图显示相应的 UI 元素。比如以下代码:

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
public class CustomToolBar extends RelativeLayout {

private ImageView leftImg, rightImg;
private TextView titleTextView;

//1.声明一个接口
public interface ImgClickListener{
public void leftImgClick();
public void rightImgClick();
}
//2.创建一个接口变量
private ImgClickListener imgClickListener;

//3.为接口变量声明一个set方法,
public void setImgClickListener(ImgClickListener imgClickListener) {
this.imgClickListener = imgClickListener;
}

public CustomToolBar(Context context) {
this(context, null);
}

public CustomToolBar(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}

public CustomToolBar(final Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
titleTextView = new TextView(context);
leftImg = new ImageView(context);
leftImg.setPadding(12, 12, 12, 12);
rightImg = new ImageView(context);
rightImg.setPadding(12, 12, 12, 12);
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.CustomToolBar);
String titleText = ta.getString(R.styleable.CustomToolBar_titleText);
//第二个参数表示默认颜色
int titleTextColor = ta.getColor(R.styleable.CustomToolBar_myTitleTextColor, Color.BLACK);
//已经由sp转为px
float titleTextSize = ta.getDimension(R.styleable.CustomToolBar_titleTextSize, 12);

//读取图片
Drawable leftDrawable = ta.getDrawable(R.styleable.CustomToolBar_leftImageSrc);
Drawable rightDrawable = ta.getDrawable(R.styleable.CustomToolBar_rightImageSrc);

//回收TypedArray
ta.recycle();

leftImg.setImageDrawable(leftDrawable);
rightImg.setImageDrawable(rightDrawable);
titleTextView.setText(titleText);
titleTextView.setTextSize(titleTextSize);
titleTextView.setTextColor(titleTextColor);

//给控件设置LayoutParams时,该控件的父容器是那个,就选那个的LayoutParams
LayoutParams leftParams = new LayoutParams((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48, getResources().getDisplayMetrics()),
(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48, getResources().getDisplayMetrics()));
//表示该控件和父容器的左边对齐
leftParams.addRule(ALIGN_PARENT_LEFT, TRUE);
this.addView(leftImg, leftParams);

LayoutParams titleParams = new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
titleParams.addRule(CENTER_IN_PARENT, TRUE);
addView(titleTextView, titleParams);

LayoutParams rightParams = new LayoutParams((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48, getResources().getDisplayMetrics()),
(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48, getResources().getDisplayMetrics()));
rightParams.addRule(ALIGN_PARENT_RIGHT, TRUE);
addView(rightImg, rightParams);

//4.点击ImageView时调用接口中的方法
leftImg.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (imgClickListener!=null) {
imgClickListener.leftImgClick();
}
}
});
rightImg.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (imgClickListener!=null) {
imgClickListener.rightImgClick();
}
}
});

}
}

自定义属性

有时候我们想在 XML 布局文件中使用 CustomToolBar 时,希望能在 XML 文件中直接指定 title 的显示内容、字体颜色,leftImage 和 rightImage 的显示图片等。这就需要使用自定义属性。自定义属性具体步骤分为以下几步:

attrs.xml 中声明自定义属性

在 res 的 values 目录下的 attrs.xml 文件中(没有就自己新建一个),使用 标签自定义属性,如下所示:

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="CustomToolBar">
<attr name="titleText" format="string|reference"/>
<attr name="myTitleTextColor" format="color|reference"/>
<attr name="titleTextSize" format="dimension|reference"/>
<attr name="leftImageSrc" format="reference"/>
<attr name="rightImageSrc" format="reference"/>
</declare-styleable>
</resources>

解释说明:

  • 标签代表定义一个自定义属性集合,一般会与自定义控件结合使用;
  • 标签则是某一条具体的属性,name 是属性名称,format 代表属性的格式。

在 XML 布局文件中使用自定义属性

需要先添加命名空间 xmlns:app,然后通过命名空间 app 引用自定义属性,并传入相应的图片资源和字符串内容。

1
xmlns:app="http://schemas.android.com/apk/res-auto"

在 CustomToolBar 中获取自定义属性的引用值

1
2
3
4
5
6
7
8
9
10
11
12
public CustomToolBar(Context context, AttributeSet attrs) {
super(context, attrs);
// 主要是通过 Context.obtainStyleAttributes 方法获取到自定义属性的集合,然后从这个集合中取出相应的自定义属性。
TypedArray ta = context.obtainStyledAttributes(attrs,R.styleable.CustomToolBar);
String titleText = ta.getString(R.styleable.CustomToolBar_titleText);
int titleTextColor = ta.getColor(R.styleable.CustomToolBar_myTitleTextColor, Color.BLACK);
// 已经由 sp 转为 px
float titleTextSize = ta.getDimension(R.styleable.CustomToolBar_titleTextSize,12);
// 读取图片
Drawable leftDrawable = ta.getDrawable(R.styleable.CustomToolBar_leftImageSrc);
Drawable rightDrawable = ta.getDrawable(R.styleable.CustomToolBar_rightImageSrc);
}

直接继承自 View 或者 ViewGroup

这种方式相比第一种麻烦一些,但是更加灵活,也能实现更加复杂的 UI 界面。一般情况下使用这种实现方式需要解决以下几个问题:

  • 自定义控件的大小,也就是宽和高分别设置多少;
  • 如果是 ViewGroup,如何合理安排其内部子 View 的摆放位置;
  • 如何根据相应的属性将 UI 元素绘制到界面。

以上 3 个问题依次在如下 3 个方法中得到解决:

  • onMeasure
  • onLayout
  • onDraw

因此自定义 View 的重点工作其实就是复写并合理的实现这 3 个方法。注意:并不是每个自定义 View 都需要实现这 3 个方法,大多数情况下只需要实现其中 2 个甚至 1 个方法也能满足需求。


备注

欢迎关注微信公众号:非也缘也