Android开发中的自定义控件

前言

大家可能会问 安卓的生态那么大 官方给出的可用控件那么多 我们为什么还要去学这个难又枯燥的自定义控件的知识呢,这对于安卓初级工程师或者初学者来说的确是不太要求掌握的。但你要是不想局限于小白,想成为更高级的安卓开发者,那么掌握自己造轮子的技术是非常有必要的,而且掌握了自定义控件,你可以随时应对以后产品经理的无理需求是不是?哈哈哈。

自定义控件的步骤

首先想要做一个控件是有一个完整的流程和套路的,下面我用一张流程图来展示。

控件的类型

首先控件可以分为两大类,一种是View,一种就是ViewGroup。

View

那么什么是view呢?通俗点说就是一个孩子,它不能再容纳更多的控件了,它只是它自己,就比如说Button,TextView,这种就是View。

1
2
3
4
5
6
7
8
<TextView    
android:text="GE"
android:textSize="25sp"
android:textColor="@color/black"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</TextView>

ViewGround

ViewGround从字面翻译过来就是视图群,也就是说在这个控件里面它是父亲,它能容纳多个控件,比如我们的布局,LinearLayout等等。

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
<LinearLayout
android:layout_width="wrap_content"
android:orientation="horizontal"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:text="18个关注"
android:layout_height="wrap_content">

</TextView>
<TextView
android:layout_width="35dp"
android:gravity="center"
android:text="|"
android:layout_height="wrap_content">

</TextView>
<TextView
android:layout_width="wrap_content"
android:text="12个粉丝"
android:layout_height="wrap_content">

</TextView>
<TextView
android:layout_width="35dp"
android:text="|"
android:gravity="center"
android:layout_height="wrap_content">

</TextView>
<TextView
android:layout_width="wrap_content"
android:text="LV10"
android:layout_height="wrap_content">

</TextView>
</LinearLayout>

由以上代码就可知,这一个LinerLayout作为ViewGround一共容纳了五个TextView控件。

看到这里相比大家对View和ViewGround有一个清晰的认知了吧。那么我们接着讲自定义控件。

控件的自定义属性与获取

做一个自定义控件最基本的就是要自定义控件的属性了。属性有包括颜色,控件高度,宽度,圆角等。

以下就是LinearLayout的属性。

1
2
3
4
5
6
7
8
 <LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="41px"
android:orientation="horizontal">
</LinearLayout>

自定义属性的步骤

1.声明属性

首先找到项目文件中的values中的themes文件,控件的属性就可以在这里声明。如下:

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
<resources xmlns:tools="http://schemas.android.com/tools">

<!--自定义viewgroup属性-->
<declare-styleable name="Flowlayout">
<attr name="maxLine" format="integer"/>
<attr name="itemHorizontalMargin" format="dimension"/>
<attr name="itemVerticalMargin" format="dimension"/>
<attr name="textMaxLength" format="integer"/>
<attr name="lines" format="integer"/>
<attr name="textColor" format="color"/>
<attr name="borderColor" format="color"/>
<attr name="borderRadio" format="dimension"/>
</declare-styleable>

<declare-styleable name="InputNumberView">
<attr name="max" format="integer"/>
<attr name="min" format="integer"/>
<attr name="defaultValue" format="integer"/>
<attr name="step" format="integer"/>
<attr name="binBackground" format="reference"/>
<attr name="disable" format="boolean"/>
</declare-styleable>

<declare-styleable name="SlideMenuView">
<attr name="function_t" format="enum">
<enum name="delete_t" value="0x30"/>
<enum name="read_t" value="0x50"/>
<enum name="top_t" value="0x03"/>
</attr>
</declare-styleable>
<declare-styleable name="WatchFace">
<attr name="secondColor" format="color"/>
<attr name="minColor" format="color"/>
<attr name="hourColor" format="color"/>
<attr name="degreeColor" format="color"/>
<attr name="scaleColor" format="color"/>
<attr name="FaceBackground" format="reference"/>
<attr name="scaleShow" format="boolean"/>
</declare-styleable>
</resources>

右上可知声明属性attr的语法都是先由 declare-styleable来包含。其中name即属性的名字,而后面的format则是该属性的类型。比如说我要定义一个颜色,就按照下面这样写:

1
<attr name="degreeColor" format="color"/>

2.使用属性:

比如说我要使用WatchFaceView中定义的背景图片属性:

1
2
3
4
5
<com.example.watchface.WatchFaceView
android:layout_width="312dp"
GE:FaceBackground="@mipmap/bg"
android:layout_height="306dp" >
</com.example.watchface.WatchFaceView>

3.添加命名空间:

添加这个的目的是为了方便获取到空间的属性。

1
xmlns:GE="https://schemas.android.com/apk/res-auto"

4.获取属性值

在类中有固定的方法来方便我们获取自定义控件的属性值,下面以我做的SlideMenu这个自定义控件来举例。

首先在SlideMenu这个类中,我们需要重写四个方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public SlideMenuView(Context context) {
this(context,null);
}

public SlideMenuView(Context context, AttributeSet attrs) {
this(context, attrs,0);
scroller=new Scroller(context);
}

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

public SlideMenuView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SlideMenuView);//获取自定义属性值
function = a.getInt(R.styleable.SlideMenuView_function_t, 0x30);
a.recycle();
}

由以上代码可知,我们可以通过上下文context.obtainStyledAttributes();方法来获取到刚刚我们在资源文件中自定义的控件中的一个TypedArray实例,我们可以通过这个实例来获取到具体各个控件的属性值,比如说上面的function。

测量

测量在自定义控件中是非常重要的,需要大家去重点掌握。其中ViewGround有两种:一种是测量自己(设置自己的大小),一种是设置期望孩子的大小。而View则是测量自己的大小。

1.重写onMeasure方法

1
2
3
4
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}