本章目录
- Part One:自定义View的draw(Canvas canvas)方法
- Part Two:自定义View的onDraw(Canvas canvas)方法
在上一节内容中讲了自定义View的构造方法,现在,只需要重写一个简单的方法,就可以绘制出一个基础的图形。
当然,自定义View绘制不仅仅要考虑内容,还要处理大小,位置,动画等等,需要重写许多其它的方法,目前只掌握绘制即可,后面再补充。
从字面意思就可以看出,View中有关绘制的方法有两个:
- public void draw(Canvas canvas) {} (不重要)
- protected void onDraw(Canvas canvas) {} (非常重要)
Part One:自定义View的draw(Canvas canvas)方法
draw.png从源码注释可以看出,必须手动给View指定一个Canvas去渲染,并且在调用前必须完成背景测量。所以说已经不推荐使用了,使用onDraw来代替。
这里只需要了解下这个方法的执行流程即可。
drawStep.png
从注释可以看出,这些步骤是一步一步顺序执行的:
1. Draw the background(绘制背景)
2. If necessary, save the canvas' layers to prepare for fading(如果需要,保存canvas层为边缘绘制作准备)
3. Draw view's content(绘制内容)
4. Draw children(绘制子View)
5. If necessary, draw the fading edges and restore layers(如果需要,绘制边缘并重新保存canvas层)
6. Draw decorations (scrollbars for instance)(绘制边框)
了解到这即可,剩下的具体方法调用就不细看了。如果感兴趣的话,可以细读这篇文章
Android View 绘制流程(Draw)源码解析
Part Two:自定义View的onDraw(Canvas canvas)方法
其实在draw()内部也是调用了onDraw()方法,draw()方法的使用其实帮我们绘制一些比较特别的东西,比如view边缘滑动时的阴湿,和滚动条之类的。
但是在实际开发中,这些东西我们一般用不到,因此官方建议我们使用onDraw方法。
我们先来看看onDraw()方法的源码:
从源码可以看出,这个方法是一个空方法,没有一行代码。但是它提供了一个空白的画布(canvas),不像draw()方法一样需要手动调用。
就像我们作画一样,画纸已经提供好了,我们要做的就是new个画笔,然后画我们自己想要的图案即可。
接下来,我们用之前提过的画圆案例巩固一下这块的知识。
第一步,创建画笔:
public class CircleView extends View{
private Paint circlePaint;
public CircleView(Context context) {
this(context, null);
}
public CircleView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public CircleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public CircleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initVariables();
}
private void initVariables() {
//创建画圆的画笔
circlePaint = new Paint();
circlePaint.setAntiAlias(true);//画笔去除锯齿
circlePaint.setColor(Color.RED);//画笔颜色为红色
circlePaint.setStyle(Paint.Style.STROKE);//画的圆是空心圆,FILL为实心圆
circlePaint.setStrokeWidth(2);//设置圆的线条宽度为2
}
}
声明一个全局变量,并在四个参数的构造方法里初始化。
有人可能会疑问了,从代码优化的角度来讲,应该尽量使用局部变量而不是全局变量,也就是说可以在onDraw方法里使用paint时再初始化。
如果你这么做了,系统会出现如下警告:
You should avoid allocating objects during a drawing or layout operation. These are called frequently, so a smooth UI can be interrupted by garbage collection pauses caused by the object allocations. The way this is generally handled is to allocate the needed objects up front and to reuse them for each drawing operation. Some methods allocate memory on your behalf (such as Bitmap.create), and these should be handled in the same way.
这句话的意思是说尽量不要在布局(onLayout)和绘制(onDraw)阶段初始化对象,因为这些方法会被频繁调用,损耗性能。所以要在之前就将这些对象初始化好,在这些方法里直接调用。
第二步,画圆:
画圆的方法很简单,canvas有响应的api,直接设定圆心的x,y坐标,半径和画笔,调用就可以了,具体代码如下:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(getWidth() / 2, getHeight() / 2, 100, circlePaint);
}
其中100是圆的半径,getWidth()是获取View的宽度,getHeight()是获取View的高度,分别除以2后,圆心就是View的正中央,这部分内容会在自定义View的布局那细说。
canvas的drawCircle,顾名思义就是画圆了。还有其它什么drawLine,drawRect之类的方法,有兴趣可以自己试验,参数都大同小异。跟我们画画一样,确定好要画的图形,坐标位置,画笔,开始话就可以了。
第三步,在XML布局中引用此控件:
到了第二步,我们的一个简单的自定义View其实已经画好了。我们可以像系统控件一样,直接在布局文件中使用了。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.terana.customview.MainActivity">
<com.terana.customview.CircleView
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
至此,一个简单的画圆就完成了。效果图如下,老实说,挺low的,过程也尽量讲的比较详细。不管有没基础,希望大家都能看懂。后来会逐步的把它包装一下。
RawCircle.png
这一节,我们完成了一个最简单的圆。打个比方说,就像我们小时候用的尺子一样,尺子上有一个圆洞,我们照着画出来就好。
下一节,会详细说下自定义View的属性,这样我们就把尺子升级为圆规,可以自定义大小了。
网友评论