Android学习笔记---自定义View基础

Android系统中提供给我们的View控件基本上能满足我们的基本应用的需求,但有时因为一些特殊的需求,我们不得不自己创建符合我们要求的View.所以学会自定义View控件还是很有必要的.

自定义View的基本步骤

  1. 创建View
  2. 处理View布局
  3. 进行View的绘制
  4. 处理View的事件监听
  5. 优化View

上面的步骤就是创建自定义View的一些基本的步骤了.下面是各个步骤的详细介绍.

创建View

要创建一个自定义的View,就需要继承系统的View类或其子类,而且我们需要实现View类的一些基础的方法.其中构造方法是必须要有的,而且我们要在布局文件中使用自定义View就必须在构造方法中声明.

1
2
3
4
5
6
7
8
class MyView extends View {//继承View
public MyView(Context context) {
super(context);
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
}//为了支持布局文件中进行创建和编辑
}

通常我们的自定义View都会有自定义的属性,便于在布局文件中使用,而自定义属性通常都会写在res/values/attrs.xml文件中,如下:

1
2
3
4
5
6
7
<declare-styleable name="MyView">
<attr name="showText" format="boolean" />
<attr name="position" format="enum">
<enum name="left" value="0"/>
<enum name="right" value="1"/>
</attr>
</declare-styleable>

上面的文件声明了两个自定义属性,showTextposition,他们都是MyView控件的属性,我们可以在布局文件中使用他们:

1
2
3
4
5
6
7
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res/res-auto">

<com.customview.MyView
app:showText="true"
app:position="left" />

</LinearLayout>

在使用自定义属性时需要指定命名空间,在Android Studio中所有的自定义命名空间都可使用http://schemas.android.com/apk/res/res-auto.
在布局文件中使用了自定义属性后,我们就需要在代码中对自定义属性在View上进行一些相应的设置,否则自定义属性便没有意义了.
首先我们可以通过下面的代码获取自定义属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.MyView,
0, 0);

try {
mShowText = a.getBoolean(R.styleable.MyView_showText, false);
mTextPos = a.getInteger(R.styleable.MyView_labelPosition, 0);
} finally {
a.recycle();
}
}

当我们在布局文件中使用一个自定义View时,所有使用到的属性都会被传入到View的构造方法中的AttributeSet类型的参数中,通过ContextobtainStyledAttributes()方法返回一个TypedArray对象,可以通过这个对象获取自定义属性的值.由于TypedArray对象是共享的资源,所以在获取完值之后必须要调用recycle()方法来回收。
自定义属性只有在View被初始化时才能获取,若我们想要在运行时对自定义的属性进行修改就需要对外提供修改的方法.

1
2
3
4
5
6
7
8
9
public boolean isShowText() {
return mShowText;
}

public void setShowText(boolean showText) {
mShowText = showText;
invalidate();
requestLayout();
}

上面的代码对外提供了获取和修改mShowText属性的方法.在setShowText()方法中,修改了mShowText属性后分别调用了invalidate()requestLayout()方法,因为当我们的控件属性发生改变时,控件的展现外观也可能会发生改变,在这种情况下就需要调用invalidate()方法让系统去调用view的onDraw()重新绘制。同样的,控件属性的改变可能导致控件所占的大小和形状发生改变,所以我们需要调用requestLayout()来请求测量获取一个新的布局位置。

处理View的布局

要完成View的布局就必须要知道View的大小,这时我们就需要对View进行测量.Android系统通过onMeasure()方法来完成View的测量.

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

}

onMeasure()方法有两个参数,分别是widthMeasureSpecheightMeasureSpec.它们包含了View的modesize,可以通过下面代码获取:

1
2
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);

mode代表了我们当前控件的父控件告诉我们控件,你应该按怎样的方式来布局。
mode有三个可选值:EXACTLY, AT_MOST, UNSPECIFIED,它们的含义是:

  • EXACTLY:父控件告诉我们子控件了一个确定的大小,你就按这个大小来布局。比如我们指定了确定的dp值和macth_parent的情况.
  • AT_MOST:当前控件不能超过一个固定的最大值,一般是wrap_content的情况.
  • UNSPECIFIED:当前控件没有限制,要多大就有多大,这种情况很少出现.

size其实就是父布局传递过来的一个大小,父布局希望当前布局的大小.

绘制View

当View被创建并测量结束后,我们就可以实现onDraw()方法来完成View的绘制了,这个方法比较简单,大家可以去查看一下Android提供的API.

处理View的事件监听

如果你的自定义控件需要与用户进行交互,那么我们就需要给我们的View实现相应的事件方法了,比较常用的有点击事件和触摸事件等.

优化View

通过一些技巧我们可以对View进行一些优化.

总结

上面所介绍的就是自定义View的一些基础知识,更详细的实现我希望后面能通过一些例子和大家分享.

参考

Android自定义控件1