Android 自定义 View 学习笔记

  • Paint.setStyle(Style style) 设置绘制模式,FILL, STROKE 和 FILL_AND_STROKE,填充,线条和两者的结合
  • Paint.setColor(int color) 设置颜色
  • Paint.setStrokeWidth(float width) 设置线条宽度
  • Paint.setTextSize(float textSize) 设置文字大小
  • Paint.setAntiAlias(boolean aa) 设置抗锯齿开关 或者 new Paint(ANTI_ALIAS_FLAG)
  • paint.setStrokeCap(cap) 设置线条端点的形状,Round=圆, Square=方形, Butt=平行
  • paint.setTextSize(float textSize)

Canvas

形状

  • canvas.drawText(text, x, y, paint) 绘制文字

    x,y 绘制的起点坐标,text:绘制的文本内容,其实文字的绘制不简单。

  • drawRect(left, top, right, bottom,paint) 矩形

    一般情况下 left 和 bottom 的 y 坐标一致;top 和 right x 的坐标一致。

  • drawPoint(x,y,paint) 单点

  • drawPoints(float[] pts, int offset, int count,Paint paint) 多个点

    offset:排除数组 pts 中的前多少个数值。count:取出数组中多少个数值来组合成坐标)

  • drawOval(float left, float top, float right, float bottom, Paint paint) 椭圆

    默认只能绘制横着的或者竖着的椭圆,不能绘制斜的

  • drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, Paint paint) 圆角矩形

  • drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, Paint paint) 弧形或扇形

    left, top, right, bottom 绘制了一个椭圆;startAngle:弧形的起始角度,x 坐标作为水平线,顺时针为正角度。sweepAngle:弧形划过的角度。useCenter:是否连接到椭圆圆心,如果不连接到椭圆圆心,就是弧形,如果连接到椭圆圆心,就是扇形。

路径 Path

通过使用 path ,我们可以绘制各种规则或不规则图形

主要通过 canvas.drawPath(Path path, Paint paint) 来绘制,path 确定绘制的形状

  • addCircle(float x, float y, float radius, Direction dir) 圆

    dir : 当这个圆和别的 View 重叠时,绘制重叠部分还是不重叠部分,一般画笔设置为填充时有用。

  • lineTo(float x, float y) 画直线

    绝对坐标来绘制

  • rLineTo(float x, float y) 画直线

    相对坐标来绘制

    1
    2
    3
    paint.setStyle(Style.STROKE);  
    path.lineTo(100, 100); // 由当前位置 (0, 0) 向 (100, 100) 画一条直线
    path.rLineTo(100, 0); // 由当前位置 (100, 100) 向正右方 100 像素的位置画一条直线
  • quadTo(float x1, float y1, float x2, float y2) 二阶贝塞尔曲线

  • cubicTo(float x1, float y1, float x2, float y2, float x3, float y3) 三阶贝塞尔

  • moveTo(float x, float y) 将路径移动到某个位置

  • arcTo(RectF oval, float startAngle, float sweepAngle, boolean forceMoveTo) 弧形(不连接圆心)

    forceMoveTo :绘制是要「抬一下笔移动过去」,还是「直接拖着笔过去」,区别在于是否留下移动的痕迹,true直接跳过去,false 连一条直线到该弧线的起点,然后开始绘制这个弧形。一般在绘制该图形之前已经绘制了别的图形会用到。

  • addArc(RectF oval, float startAngle, float sweepAngle) 弧形

    和 arcTo 的区别是,addArc 的 forceMoveTo 默认为 true

  • close() 封闭当前子图形(两个路径存在连接)

    例如我们用 path 描述了 两条连接但方向不同的直线,如果调用 close,那么会形成一个三角形。 或者理解为 close() = linTo(起点坐标)。当画笔为填充状态时,path 默认执行 close()

Bitmap

必须得先有一个 Bitmap 对象,然后把这个bitmap对象的像素放到新的 Bitmap 上,在绘制到新的 Bitmap 时,你可以控制绘制的坐标

  • drawBitmap(Bitmap bitmap, float left, float top, Paint paint) 画 Bitmap

    left, top 左上角的坐标

  • drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint) 包含 dst 的重叠规则绘制

    RectF 比 Rect 的精度更高

  • drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) Matrix 矩阵变换

重叠

  • Path.setFillType(Path.FillType ft) 设置填充方式

填充重叠部分还是不重叠部分,或者全部填充绘制
【WINDING:全填充】
【EVEN_ODD:交叉填充】
【INVERSE_WINDING:反色全填充】
【INVERSE_EVEN_ODD:反色交叉填充】
path 默认值是 WINDING。

以上的解释并不全面,

  • EVEN_ODD 奇偶数

    EVEN_ODD 翻译过来就是: 奇偶数
    在图形内任意地方向图形外画一条射线,该直线会与图形相交,记录相交的点数,当相交点的个数为偶数时,表明射线的起点所在区域不是填充区。奇数则是填充区。【主要看射线起点所在的位置,偶数取反】

  • WINDING 非零环绕数原则

    根据画笔的绘制方向,然后和上面一样先画一条射线,该射线碰到顺时针,点数+1;遇到逆时针-1,计算最后的点数,当最后的点数不为0时(包括负数),说明射线起点所在的区域就是填充区;当点数为0时填充该射线起点所在区域的外部。【遇0取反】

PorterDuff.Mode

效果-颜色

简单的颜色设置有 paint.setColor、canvas.drawColor(),复杂颜色:Shader,Paint 设置 Shader 后,setColor 不会生效

Shader

一般的我们使用的都是 Shader 的子类,【LinearGradient 线性渐变】【 RadialGradient 辐射渐变】 【SweepGradient】 【BitmapShader】【 ComposeShader】.

CLAMP: 单色减弱渐变
MIRROR: 镜面,先将单色反射成双色,然后渐变
REPEAT: 重复,两种颜色的边界比较明显

  • LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1, Shader.TileMode tile)

    两个端点的坐标和颜色,tile:渐变的模式 CLAMP, MIRROR 和 REPEAT

  • RadialGradient(float centerX, float centerY, float radius, int centerColor, int edgeColor, TileMode tileMode) 辐射渐变

    类似于圆圈的渐变,由中心想四周渐变

  • SweepGradient(float cx, float cy, int color0, int color1) 扫描渐变

    类似雷达屏幕的那种

  • BitmapShader(Bitmap bitmap, Shader.TileMode tileX, Shader.TileMode tileY) Bitmpa 着色

    将 bitmap 按照TileMode两个方向进行着色和拉伸,我们可以利用这个绘制圆形状 Bitmap,先把这个 bitmapShader 生成出来,然后set 给 Paint,最后让 canvas.Circle(paint) 即可

  • ComposeShader(Shader shaderA, Shader shaderB, PorterDuff.Mode mode) 混合两个 Shader

    假如这两个 shader 都是 Bitmap,那最后的绘制就可能会碰到重叠的场景,最后一个参数 PorterDuff.Mode 就是绘制重叠的策略,直接看官网的图片示例:https://developer.android.com/reference/android/graphics/PorterDuff.Mode.html

  1. ComposeShader : 混合两个 Shader
  2. PorterDuffColorFilter :增加一个单色的 ColorFilter
  3. Xfermode :设置绘制内容和 View 中已有内容的混合计算方式(只能使用其子类 PorterDuffXfermode)

Clip 裁切

  • clipRect()

    1
    2
    canvas.clipRect(left, top, right, bottom);  
    canvas.drawBitmap(bitmap, x, y, paint);
  • clipPath()

    1
    2
    3
    4
    canvas.save();  
    canvas.clipPath(path1);
    canvas.drawBitmap(bitmap, point1.x, point1.y, paint);
    canvas.restore();

变换

简单二维

位移 旋转

  • translate(float dx, float dy) 平移

    1
    2
    3
    4
    canvas.save();  
    canvas.translate(200, 0);
    canvas.drawBitmap(bitmap, x, y, paint);
    canvas.restore();
  • rotate(float 角度, float px, float py) 旋转

    1
    2
    3
    4
    canvas.save();  
    canvas.clipRect(left, top, right, bottom);
    canvas.drawBitmap(bitmap, x, y, paint);
    canvas.restore();
  • scale(float sx, float sy, float px, float py) 放缩

    1
    2
    3
    4
    canvas.save();  
    canvas.scale(1.3f, 1.3f, x + bitmapWidth / 2, y + bitmapHeight / 2);
    canvas.drawBitmap(bitmap, x, y, paint);
    canvas.restore();

Matrix 二维变换

Camera 三维变换

旋转、平移、移动相机

  • Camera.rotateX()

    1
    2
    3
    4
    5
    canvas.save();
    camera.rotateX(30); // 旋转 Camera 的三维空间
    camera.applyToCanvas(canvas); // 把旋转投影到 Canvas
    canvas.drawBitmap(bitmap, point1.x, point1.y, paint);
    canvas.restore();
  • Camera.translate(float x, float y, float z) 移动

  • Camera.setLocation(x, y, z) 设置虚拟相机的位置