1.构造函数css
构造函数是View的入口,能够用于初始化一些的内容,和获取自定义属性。java
View的构造函数有四种重载分别以下:android
public void SloopView(Context context) {} public void SloopView(Context context, AttributeSet attrs) {} public void SloopView(Context context, AttributeSet attrs, int defStyleAttr) {} public void SloopView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {}
有三个参数的构造函数中第三个参数是默认的Style,这里的默认的Style是指它在当前Application或Activity所用的Theme中的默认Style,且只有在明确调用的时候才会生效,以系统中的ImageButton为例说明:canvas
public ImageButton(Context context, AttributeSet attrs) { //调用了三个参数的构造函数,明确指定第三个参数 this(context, attrs, com.android.internal.R.attr.imageButtonStyle); } public ImageButton(Context context, AttributeSet attrs, int defStyleAttr) { //此处调了四个参数的构造函数,无视便可 this(context, attrs, defStyleAttr, 0); }
排除了两个以后,只剩下一个参数和两个参数的构造函数,他们的详情以下:ide
//通常在直接New一个View的时候调用。 public void SloopView(Context context) {} //通常在layout文件中使用的时候会调用,关于它的全部属性(包括自定义属性)都会包含在attrs中传递进来。 public void SloopView(Context context, AttributeSet attrs) {}
如下方法调用的是一个参数的构造函数:函数
//在Avtivity中 SloopView view new SloopView(this);
如下方法调用的是两个参数的构造函数:oop
//在layout文件中 - 格式为: 包名.View名 <com.sloop.study.SloopView android:layout_width"wrap_content" android:layout_height"wrap_content"/>
2.测量View大小(onMeasure)布局
View的大小不只由自身所决定,同时也会受到父控件的影响,为了咱们的控件能更好的适应各类状况,通常会本身进行测量。this
测量View大小使用的是onMeasure函数,咱们能够从onMeasure的两个参数中取出宽高的相关数据:spa
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthsize = MeasureSpec.getSize(widthMeasureSpec); //取出宽度的确切数值 int widthmode = MeasureSpec.getMode(widthMeasureSpec); //取出宽度的测量模式 int heightsize = MeasureSpec.getSize(heightMeasureSpec); //取出高度的确切数值 int heightmode = MeasureSpec.getMode(heightMeasureSpec); //取出高度的测量模式 }
从上面能够看出 onMeasure 函数中有 widthMeasureSpec 和 heightMeasureSpec 这两个 int 类型的参数, 毫无疑问他们是和宽高相关的, 但它们其实不是宽和高, 而是由宽、高和各自方向上对应的测量模式来合成的一个值:
测量模式一共有三种, 被定义在 Android 中的 View 类的一个内部类View.MeasureSpec中:
模式 | 二进制数值 | 描述 |
---|---|---|
UNSPECIFIED | 00 | 默认值,父控件没有给子view任何限制,子View能够设置为任意大小。 |
EXACTLY | 01 | 表示父控件已经确切的指定了子View的大小。 |
AT_MOST | 10 | 表示子View具体大小没有尺寸限制,可是存在上限,上限通常为父View大小。 |
用 MeasureSpec 的 getSize是获取数值, getMode是获取模式。
若是对View的宽高进行修改了,不要调用 super.onMeasure( widthMeasureSpec, heightMeasureSpec); 要调用 setMeasuredDimension( widthsize, heightsize)这个函数。
这个函数在视图大小发生改变时调用。
由于View的大小不只由View自己控制,并且受父控件的影响,因此咱们在肯定View大小的时候最好使用系统提供的onSizeChanged回调函数。
onSizeChanged以下:
@Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); }
四个参数,分别为 宽度,高度,上一次宽度,上一次高度。
只需关注 宽度(w), 高度(h) 便可,这两个参数就是View最终的大小。
肯定布局的函数是onLayout,它用于肯定子View的位置,在自定义ViewGroup中会用到,他调用的是子View的layout函数。
在自定义ViewGroup中,onLayout通常是循环取出子View,而后通过计算得出各个子View位置的坐标值,而后用如下函数设置子View位置。
child.layout(l, t, r, b);
5.绘制内容(onDraw)
onDraw是实际绘制的部分,也就是咱们真正关心的部分,使用的是Canvas绘图。
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); }
自定义完View以后,通常会对外暴露一些接口,用于控制View的状态等,或者监听View的变化.
操做类型 |
相关API |
备注 |
---|---|---|
绘制颜色 |
drawColor, drawRGB, drawARGB |
使用单一颜色填充整个画布 |
绘制基本形状 |
drawPoint, drawPoints, drawLine, drawLines, drawRect, drawRoundRect, drawOval, drawCircle, drawArc |
依次为 点、线、矩形、圆角矩形、椭圆、圆、圆弧 |
绘制图片 |
drawBitmap, drawPicture |
绘制位图和图片 |
绘制文本 |
drawText, drawPosText, drawTextOnPath |
依次为 绘制文字、绘制文字时指定每一个文字位置、根据路径绘制文字 |
绘制路径 |
drawPath |
绘制路径,绘制贝塞尔曲线时也须要用到该函数 |
顶点操做 |
drawVertices, drawBitmapMesh |
经过对顶点操做可使图像形变,drawVertices直接对画布做用、 drawBitmapMesh只对绘制的Bitmap做用 |
画布剪裁 |
clipPath, clipRect |
设置画布的显示区域 |
画布快照 |
save, restore, saveLayerXxx, restoreToCount, getSaveCount |
依次为 保存当前状态、 回滚到上一次保存的状态、 保存图层状态、 回滚到指定状态、 获取保存次数 |
画布变换 |
translate, scale, rotate, skew |
依次为 位移、缩放、 旋转、错切 |
Matrix(矩阵) |
getMatrix, setMatrix, concat |
实际上画布的位移,缩放等操做的都是图像矩阵Matrix, 只不过Matrix比较难以理解和使用,故封装了一些经常使用的方法。 |
private Paint mPaint = new Paint();初始化画笔
private void initPaint() {
mPaint.setColor(Color.BLACK);//设置画笔颜色
mPaint.setStyle(Paint.Style.FILL);//设置画笔模式为填充
mPaint.setStrokeWidth(10f); //设置画笔宽度为10px
}
public SloopView(Context context, AttributeSet attrs) {
super(context, attrs); initPaint();
}
绘制点: canvas.drawPoint(200, 200, mPaint);
绘制线: canvas.drawLine(300,300,500,600,mPaint);
绘制矩形: canvas.drawRect(100,100,800,400,mPaint);
Rect rect = new Rect(100,100,800,400); canvas.drawRect(rect,mPaint);
RectF rectF = new RectF(100,100,800,400); canvas.drawRect(rectF,mPaint);
绘制圆: canvas.drawCircle(500,500,400,mPaint);
绘制圆弧: public void drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint){}
一、在 xml中建立了一个view时,全部在xml中声明的属性都会被传入到view的构造方法中的AttributeSet类型的参数当中。 经过调用Context的obtainStyledAttributes()方法返回一个TypedArray对象。而后直接用TypedArray对象获取自定义属性的值。TypedArray对象是共享的资源,因此在获取完值以后必需要调用recycle()方法来回收。
二、自定义控件的属性发生改变以后,控件的样子也可能发生改变,在这种状况下就须要调用invalidate()方法让系统去调用view的onDraw()从新绘制。
三、在onDraw方法中开始绘制以前,应该让画笔Paint对象的信息初始化完毕。这是由于View的从新绘制是比较频繁的,这就可能屡次调用onDraw,因此初始化的代码不该该放在onDraw方法里。