
首先纠正下上篇的一个描述性错误,对于invalidate和postInvalidate的区别:
invalidate:在主线程内部更新时使用;postInvalidate:在非UI线程更新时使用,postInvalidate会把更新要求回传至ViewRootImpl中进行响应;
延续上文内容,进一步针对View的基本概念做详细介绍,接下来我们将学习编写自己的第一个自定义View,一起开始吧!
相信大家在实际生活中都或多或少的画过一些东西,那么我们画一个圆需要做什么准备工作呢?
圆的呈现体:一张纸或地面的一片区域等等,用于圆的展示;
绘制圆的工具:铅笔,圆珠笔,钢笔等等,用于绘制圆;
圆的圆心及半径:确定圆绘制在哪儿,绘制多大;
同样的,在自定义View的开发过程中,我们也需要相似的东西(随后文章中简称为绘制类型自定义View的三要素):
Canvas:画布,用于绘制View所要显示的内容,一般来自onDraw()函数的传入,onDraw()函数原型如代码片段-onDraw()所示;Paint:画笔,用于绘制View所需要绘制的内容,相当于笔,在其内部可以设置颜色,粗细,是否实心等信息,一般通过new的方式获取该类对象;Point:点,用于确定View所需要绘制的内容大小及位置等相关信息,当然这里的Point不具有实际意义,也有可能是线段,矩形等,只不过大多数是通过点的组合关系确定而已;
/** onDraw() **/protected void onDraw(Canvas canvas) {}获取宽高
上面我们已经简单了解到View三要素的前两个要素的获取方式,那么点又该如何得到呢?相信细心的朋友要吐槽我了,上一篇不就说过了么?左上角是坐标原点,向下Y正向,向右X正向,就是这么简单。
那么对于一些关键点呢?例如说View的中心,小伙伴们估计又要心急了,有啥好说的,View#getWidth()/2,View#getHeight()/2。
但是真的可以做到么?答案是必须等到View走完onMeasure流程后再使用这种方式,否则数据不准确。那么有什么办法呢?
好,大招来了,上篇中刚讲过View的Top,left,right,bottom,仔细看图,监听这四个值是不是可以得到View的宽高,很显然可行。系统已经将这一块帮我们做好了,在setLeft(),setRight(),setTop(),setBottom()中均调用了onSizeChanged方法,所以我们只需重写onSizeChanged就可以获得View的宽高了,详情见代码片段-setTop(),同事我们可以看到系统中计算View的宽高方式。
width = right - left
height = bottom - top
/** setTop **/ public final void stTop(int top) { if (top != mTop) { .... //获取View宽度 int width = mRight - mLeft; int oldHeight = mBottom - mTop; mTop = top; mRenderNode.setTop(mTop); //sizeChange内部调用了onSizeChanged sizeChange(width, mBottom - mTop, width, oldHeight); .... } } private void sizeChange(int newWidth, int newHeight, int oldWidth, int oldHeight) { //调用onSizeChanged onSizeChanged(newWidth, newHeight, oldWidth, oldHeight); .... rebuildOutline(); }这里要注意一点,宽高只是限制了显示区域(如上文View边界图所示),和画布大小无关,在View内部绘制区域理论是说是无限大的。
第一个自定义View
知道了如何获取View的宽高,接下来我们就可以开始一次简单的绘制了(在View最中心绘制一个半径200的圆):
打开Android Studio,新建Android Studio,在其内创建Java class 并使其继承自View,重写View的构造方法,代码如片段-Constructor:
/* Constructor */public class PaintView extends View { public PaintView(Context context) { super(context); } public PaintView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public PaintView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); }}声明画笔和宽高
1.在该类中添加片段-Paint Point中的成员属性:
/* Paint Point */ /* 画笔,用于View内部内容的绘制 */ private Paint mPaint; /* 用于存储View的宽高 */ private int mWidth,mHeight;2.初始化宽高,重写onSizeChanged方法,代码如片段-onSizeChanged:
/* onSizeChanged */ @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); if (w > 0 && h > 0){ mWidth = w; mHeight = h; } }3.初始化画笔,并为画笔设置颜色,实心等属性,代码如片段-Paint init:
/* Paint init */ //在View构造函数中调用 private void init(){ //新建画笔对象 mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); //为画笔设置颜色 mPaint.setColor(Color.BLUE); //设置画笔实心空心,Style.FILL--实心,Style.STROKE--空心 mPaint.setStyle(Style.FILL); }获取画布绘制圆
如上文所述,重写View#onDraw方法获取画布,并绘制圆,代码如片段-onDraw Method:
/* onDraw Method *//*** 前两个参数分别代表圆心的X坐标和Y坐标* 第三个参数是圆半径* 第四个参数是绘制圆所使用的画笔*/canvas.drawCircle(mWidth/2,mHeight/2,200,mPaint);这里我们调用了画布的画圆方法,相关参数说明在注释上。
XML中声明使用
在Android体系内部View是通过包名的方式反射生成View对象的(后续View树的生成部分会做详细说明),所以针对自定义View,我们要使用全包名的方式在XML中引用,当然也可以通过new的方式生成,代码如片段-activity_main.xml:
<!-- activity_main.xml --><?xml version="1.0" encoding="utf-8"?><android.support.constraint.ConstraintLayout 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.example.code.customview.MainActivity"> <com.example.code.customview.PaintView android:id="@+id/four_arc_view" android:layout_width="400dp" android:layout_height="400dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent"/></android.support.constraint.ConstraintLayout>好了点击三角运行一下吧,如果你没出错,效果应该是图-结果那样的。

举一反三,那么请朋友们尝试下,修改画笔颜色,修改画笔样式,在界面左上角200,200位置绘制一个空心圆环吧!
精力更多的亲们,现在可以尝试绘制图-水波纹中的效果了,其中
Paint#setStrokeWidth()可用于设置画笔粗细Paint#getStrokeWidth()可用于获取画笔宽度Paint#setAlpha() 可用于改变颜色值的透明度,取值在0-255之间
完成的你们可以在后台秀绘制结果给我哦!
