最近,一直再看《Android群英传》,于是打算将最近的所经历的记录下来。
View的测量
想要实现自定义View的效果,我们首先需要进行的就是View的测量了。当然,这不是必须要去做的,只是大部分情况下会进行的一个操作。就像现实生活中,我们要去裁剪,也是需要先测量一下长和宽。在Android中通过继承View类,重写其onMeasure(int widthMeasureSpec, int heightMeasureSpec)方法。系统提供了一个MeasureSpec类,帮助我们进行View的测量。MeasureSpec是一个32位的int值,其中高两位为测量模式,低30位是测量的大小。这里,我们只需要关注一下测量的模式。
测量模式
- EXACTLY
按照字义,即是精确测量模式。当我们在布局中写成android:layout_width="match_parent"
android:layout_height="match_parent"这个样子或者直接android:layout_width="300dp"
android:layout_height="300dp"。这时系统就会使用EXACTLY模式。
- AT_MOST
最大值模式,当在布局中android:layout_width="wrap_content” android:layout_height="wrap_content"。即包裹内容,控件的大小会随着控件内部内容的多少而变化。但是,只要这个尺寸不超过父控件允许的最大尺寸就可以了。
- UNSPECIFIED
不确定模式,View想多大就多大。类似的系统中的ScrollView、ListView就是应用的这种模式。因为我们并不能确定你要向其中添加多少内容。
下面是onMeasure方法:
|
|
我们进到onMeasure方法里面会发现系统显示调用了:
|
|
再来看一下这个getDefaultSize()方法:
|
|
看到了吧,假如我们没有重写onMeasure方法,系统会默认进行处理。我们平时布局宽高中用到的比较多的就是match_parent和wrap_content。也就是对应的MeasureSpec.AT_MOST和 MeasureSpec.EXACTLY。看到这里MeasureSpec.AT_MOST和 MeasureSpec.EXACTLY是一起处理的。所以,如果我们不进行重写onMeasure方法。那么,最终当我们布局宽高指定为wrap_content时就会出现默认填充整个屏幕的情况,也就是和match_parent一样了。所以,这里重写onMeasure方法就是为了让一个View在wrap_content属性下有一个默认的大小,而不是直接填充整个屏幕。
再往下面看会发现setMeasuredDimension(int measuredWidth, int measuredHeight)这一方法。看一下注释:
This method must be called by onMeasure(int, int) to store the
measured width and measured height
所以,系统就是调用这一方法将测量的宽高值传进去,完成的测量工作。
在这里,给出一个模板代码:
|
|
View的绘制
现实中,我们想要进行绘画的话,是需要纸和笔的。同样在Android中,我们也需要纸和笔来进行绘制。这里我们只需要重写onDraw(Canvas canvas)方法。
画布Canvas
什么是Canvas呢?Canvas就是画布,也就相当于一张纸。Android中已经封装好了许多常用的绘图的API。只要在onDraw(Canvas canvas)中,利用canvas.drawXXX()就可以做出相应的形状了。比如:
|
|
当然,画布的作用远不止这些。还有,诸如,平移,旋转等功能……
画笔Paint
正如现实生活中没有笔肯定是不能绘画的。在自定义View中,我们需要用到Paint来绘制View。Paint有颜色,粗细,绘制形状等属性。
绘制
继承系统控件,扩展属性
这种就是系统控件缺少我们想要的某一个属性,我们就是可以继承系统的控件,重写相应的方法进行绘制。比如,可以重写onDraw()方法,进行UI的修改。
继承View,实现新的控件
这种就是Android中没有控件满足我们的需求时,就可以自己自定义View实现自己的效果。
自定义属性
- 在values文件夹下新建 attrs.xml 文件。在其中实现自己想要的属性代码。
|
|
name属性就是一个名字,相当于id。而format则是定义这个属性是什么类型的,string就是文字,dimension就是尺寸,reference引用(@drawable/xxx)。
使用属性值
|
|
这里可以在第一个构造方法中调用第二个,第二个调用第三个。最后在第三个构造方法中执行TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.TopBar);。就获得了我们在attrs.xml中定义的属性的TypedArray,然后通过一系列的ta.getXxx方法,就可以得到我们的自定义属性。最后在获取完所有的属性值时一定要调用ta.recycle();方法。
设计
前面的操作已经准备好了工具,接下来就是设计阶段。也就是按照自己的想要的效果设计了。其实,再复杂的自定义View,只要一部分,一部分的绘制总能完成。而在绘制中,通常都是对坐标的计算了。只要我们坐标计算无误,总能实现自己的自定义View。当然,我也只是一个小白。。。
引用
最后,一切OK。我们就可以在布局中应用我们的自定义View了。
首先一定要写全路径com.example.customviewtest.TopBar。然后,最重要的是xmlns:custom="http://schemas.android.com/apk/res-auto"。这里custom可以写我们想写的名空间,然后就可以引用我们的自定义的属性了。
Enjoy it
以上就是自定义View的大体流程。