1 简介
当Android系统内置的View无法实现我们的需求,需要针对业务定制我们想要的View。自定义view分为两种:自定义view和定义viewgroup。
自定义View需重写三个函数:onMeasure()、onLayout()、onDraw()。
- onMeasure
onMeasure(int widthMeasureSpec, int heightMeasureSpec)是View让其父节点知道它想要多大的尺寸。方法的参数widthMeasureSpec和heightMeasureSpec代表 测量模式和宽高的大小。它本身是一个32位的数据,前两位代表模式,后30位代表尺寸大小。如何获取。参考附件代码:
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
onMeasure包含了3种测量模式:UNSPECIFIED,EXACTLY,AT_MOST。
1. EXACTLY:父view已经强制设置了子view的大小,一般是MATCH_PARENT和固定值
2. UNSPECIFIED:父view对子view没有任何限制,子view可以是任何大小
3. AT_MOST:子view限制在一个最大范围内,一般是WARP_CONTENT-包裹内容
- onLayout ()
onLayout 让Android知道View在其父控件中的位置,即距父控件四边的距离left、right、top、bottom。布局是绘图的基础,只有完成了布局,才能对View进行绘图。 - onDraw()
onDraw()绘图的前提是已经对View进行了量算和布局,View通过调用draw()方法进行绘图,绘图的目的就是让View在UI界面上呈现出来。
2 onMeasure
首页自定义一个viewgroup 一个button
<?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-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<com.example.test.SelftLayout
android:background="@color/colorPrimary"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.example.test.MyButton
android:id="@+id/result"
android:layout_width="100dp"
android:layout_height="100dp"
android:background="@color/colorAccent"
android:text="Hello World!"
/>
</com.example.test.SelftLayout>
</LinearLayout>
案例—通过改变自定义layout的宽高模式,获取到的数据不同
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
final int mode = MeasureSpec.getMode(widthMeasureSpec);
final int size = MeasureSpec.getSize(widthMeasureSpec);
switch (mode) {
case MeasureSpec.AT_MOST:
Log.d(TAG, "onMeasure: AT_MOST=" );
break;
case MeasureSpec.EXACTLY:
Log.d(TAG, "onMeasure: EXACTLY=" );
break;
case MeasureSpec.UNSPECIFIED:
Log.d(TAG, "onMeasure: UNSPECIFIED=" );
break;
}
int width = SystemUtil.dp2px(getContext(),200);
setMeasuredDimension(width,width);
Log.d(TAG, "onMeasure: AT_MOST=" +size+"---width="+getWidth()+"---height="+getHeight());
}
3.onLayout
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
final int childCount = getChildCount();
int left = 0,top = 0,right = 0,bottom = 0;
for (int x=0;x<childCount;x++){
final View childAt = getChildAt(x);
final int measuredWidth = childAt.getMeasuredWidth();
final int measuredHeight = childAt.getMeasuredHeight();
// 计算距离屏幕左边距离(getLeft())
left = (getWidth()-measuredWidth)/2;
// 计算距离屏幕顶部距离(getTop())
top = (getHeight()-measuredHeight)/2;
// 计算子元素右边缘 距离左边屏幕距离(getRight())
right = getWidth()-left;
// 计算顶部边缘距离顶部距离
bottom = top+measuredHeight;
Log.d(TAG, "onLayout: "+left+"--"+top+"--");
// 布局
childAt.layout(left,top,right,bottom);
}
}
4 onDraw
绘制的意思,绘制的内容,形状。
4.1 Canvas 画布
Canvas我们可以称之为画布,能够在上面绘制各种东西,是安卓平台2D图形绘制的基础,非常强大。
4.2 常用api
操作类型 | 相关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比较难以理解和使用,故封装了一些常用的方法。 |
4.3 使用
4.3.1 绘制点
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//创建画笔
paint = new Paint();
// 画笔为填充模式
paint.setStyle(Paint.Style.FILL);
//画笔颜色
paint.setColor(Color.RED);
// 画笔宽度
paint.setStrokeWidth(11f);
canvas.drawPoint(300,300, paint);
}
4.3.2 绘制线
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//创建画笔
paint = new Paint();
// 画笔为填充模式 STROKE //描边 FILL //填充 FILL_AND_STROKE //描边加填充
paint.setStyle(Paint.Style.FILL);
//画笔颜色
paint.setColor(Color.RED);
// 画笔宽度
paint.setStrokeWidth(11f);
canvas.drawLine(300,300,500,600,paint);
}
4.3.3 绘制矩形
确定一个矩形最少需要四个数据,就是对角线的两个点的坐标值,这里一般采用左上角和右下角的两个点的坐标。
// 第一种
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);
4.3.4 绘制圆
canvas.drawCircle(500,500,400,mPaint); // 绘制一个圆心坐标在(500,500),半径为400 的圆。
4.3.5 绘制圆角矩形
// 第一种
RectF rectF = new RectF(100,100,800,400);
canvas.drawRoundRect(rectF,30,30,mPaint);
// 第二种
canvas.drawRoundRect(100,100,800,400,30,30,mPaint);