Android绘制之Canvas

来源:互联网 发布:沈颢事件知乎 编辑:程序博客网 时间:2024/04/28 18:51

我们经常在一个view上绘制一些图像或者形状的时候,都是使用canvas来实现的。我们可以在view中的onDraw方法中获取到canvas的对象。当绘制一些自定义图像的时候,调用view.invalidate方法对view进行重新刷新,然后会绘制一个新的图像。下面我们主要来了解下canvas的一些基本使用的方法。

canvas对象获取的途径有两种,一种是通过重新view.onDraw方法获取到canvas对象;另外一种是自己创建canvas对象,你可以像下面一样创建canvas对象:

Bitmap b = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);Canvas c = new Canvas(b);

首先我们新写一个类CustomView继承View,然后我们重新onDraw方法,具体代码如下:

CustomView.java文件:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);    }}

activity_main.xml文件:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context="com.dylan.canvasdemo.MainActivity">    <com.dylan.canvasdemo.CustomView        android:layout_width="wrap_content"        android:layout_height="wrap_content" /></LinearLayout>

当我们运行代码时,发现页面一片空白,是因为我们没在onDraw方法里进行任何绘制的操作。canvas提供绘制图像的api都是以draw开头的我们可以查看下它的api:


绘制颜色

drawARGB(int a, int r, int g, int b)

a,r,g,b的取值范围在[0,255]

    private void drawARGB(Canvas canvas) {        canvas.drawARGB(125, 255, 0, 0);    }

效果图如下:

drawRGB(int r, int g, int b)

r,g,b的取值范围在[0,255]

    private void drawRGB(Canvas canvas) {        canvas.drawRGB(0, 255, 0);    }
效果图如下:

drawColor(int color)

Color.parseColor(String color)中的color格式可为"#aarrggbb"或者"#rrggbb"

    private void drawColor(Canvas canvas) {        canvas.drawColor(Color.parseColor("#0000ff"));    }
效果图如下:


绘制基本图形

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

该方法是以left top为顶点(相当于(x,y)),将位图画在画布上

    private void drawBitmap(Canvas canvas) {        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);        canvas.drawBitmap(bitmap, 100, 100, new Paint());    }
效果图如下:


drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, Paint paint)
drawArc(RectF rectF, float startAngle, float sweepAngle, boolean useCenter, Paint paint)

该方法是以left top right bottom或者rectF构成的矩阵,绕其中心画弧,以startAngle为开始角度,画角度为sweepAngle的弧(当sweepAngle为正数是按顺时针画弧,为负数是逆时针)。当useCenter为true时,表示矩阵中心跟圆弧首尾相连,false表示圆弧首尾相连

    private void drawArc(Canvas canvas) {        Paint paint = new Paint();        paint.setColor(Color.GREEN);        canvas.drawArc(100, 100, 400, 400, 90, -90, true, paint);        canvas.drawArc(100, 100, 500, 500, 90, -90, false, paint);    }

    private void drawArc(Canvas canvas) {        Paint paint = new Paint();        paint.setColor(Color.GREEN);        RectF rectF1 = new RectF(100, 100, 400, 400);        RectF rectF2 = new RectF(100, 100, 500, 500);        canvas.drawArc(rectF1, 90, -90, true, paint);        canvas.drawArc(rectF2, 90, -90, false, paint);    }

效果图如下:

drawCircle(float x, float y, float radius, Paint paint)

该方法是以(x,y)为圆形,以radius为半径画圆

    private void drawCircle(Canvas canvas) {        Paint paint = new Paint();        paint.setColor(Color.GREEN);        canvas.drawCircle(200, 200, 100, paint);    }
效果图如下:


drawLine(float startX, float startY, float stopX, float stopY, Paint paint)

该方法是以点(startX,stratY)为起点,以点(stopX,stopY)为终点画线

drawLine(float[] pts, Paint paint)

该方法是以pts直线端点数据画直线,每4个点构成一条直线

drawLine(float[] pts, int offset, int count, Paint paint)

该方法是以pts直线端点数据画直线,且忽略点前offset个点,从offset个点之后的count个点进行画线,每4个点构成一条直线

    private void drawLine(Canvas canvas) {        Paint paint = new Paint();        paint.setColor(Color.GREEN);        paint.setStrokeWidth(5);//设置线的宽度        canvas.drawLine(100, 100, 200, 300, paint);        canvas.drawLine(400, 500, 600, 700, paint);    }
   private void drawLines(Canvas canvas) {        Paint paint = new Paint();        paint.setColor(Color.GREEN);        //设置线的宽度        paint.setStrokeWidth(5);        //最小的大小是4,大小必须是4的倍数        float[] pts1 = new float[]{100, 100, 200, 300,                400, 500, 600, 700};        canvas.drawLines(pts1, paint);    }
   private void drawLines(Canvas canvas) {        Paint paint = new Paint();        paint.setColor(Color.GREEN);        //设置线的宽度        paint.setStrokeWidth(5);        float[] pts2 = new float[]{100, 200, 300, 400,                100, 100, 200, 300,                400, 500, 600, 700};        canvas.drawLines(pts2, 4, 8, paint);    }
以上三个方法,呈现出来的效果是一样的,效果图如下:


drawOval(float left, float top, float right, float bottom, Paint paint)
drawOval(RectF rectF, Paint paint)

该方法是以left top right bottom或者rectF构成的矩阵,进行椭圆的绘制

    private void drawOval(Canvas canvas) {        Paint paint = new Paint();        paint.setColor(Color.GREEN);        canvas.drawOval(100, 100, 300, 200, paint);    }
    private void drawOval(Canvas canvas) {        Paint paint = new Paint();        paint.setColor(Color.GREEN);        RectF rectF = new RectF(100, 100, 300, 200);        canvas.drawOval(rectF, paint);    }
效果图如下:


drawPaint(Paint paint)

用画笔设置的属性填充画布

    private void drawPaint(Canvas canvas) {        Paint paint = new Paint();        paint.setColor(Color.GREEN);        canvas.drawPaint(paint);    }

效果图跟drawRGB方法的一样,这里不再给出


drawPoint(float x, float y, Paint paint)
drawPoint(float[] pts, Paint paint)
drawPoint(float[] pts, int offset, int count, Paint paint)

这三个方法跟drawLine和drawLines比较类似,只是pts数组控制为2的倍数就行


drawRect(float left, float top, float right, float bottom, Paint paint)
drawRect(Rect rect, Paint paint)
drawRect(RectF rectF, Paint paint)

该方法是以left top right bottom或者rectF或者rect构成的矩阵

    private void drawRect(Canvas canvas) {        Paint paint = new Paint();        paint.setColor(Color.GREEN);        canvas.drawRect(100, 100, 200, 300, paint);    }
    private void drawRect(Canvas canvas) {        Paint paint = new Paint();        paint.setColor(Color.GREEN);        Rect rect = new Rect(100, 100, 200, 300);        canvas.drawRect(rect, paint);    }
    private void drawRect(Canvas canvas) {        Paint paint = new Paint();        paint.setColor(Color.GREEN);        RectF rectF = new RectF(100, 100, 200, 300);        canvas.drawRect(rectF, paint);    }
效果图如下:


drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, Paint paint)

drawRoundRect(RectF rectF, float rx, float ry, Paint paint)

该方法是以left top right bottom或者rectF构成的矩阵,并以rx为x轴的半径,以ry为y轴的半径画弧

    private void drawRoundRect(Canvas canvas) {        Paint paint = new Paint();        paint.setColor(Color.GREEN);        canvas.drawRoundRect(100, 100, 300, 200, 50, 50, paint);    }
    private void drawRoundRect(Canvas canvas) {        Paint paint = new Paint();        paint.setColor(Color.GREEN);        RectF rectF = new RectF(100, 100, 300, 200);        canvas.drawRoundRect(rectF, 50, 50, paint);    }
效果图如下:


drawPath(Path path, Paint paint)

该方法只需要传入一个路径跟画笔即可实现

    private void drawPath(Canvas canvas) {        Paint paint = new Paint();        paint.setColor(Color.GREEN);        paint.setStyle(Paint.Style.STROKE);        paint.setStrokeWidth(5);        Path path = new Path();        path.moveTo(100, 100);        path.lineTo(600, 500);        path.lineTo(400, 180);        path.addCircle(300, 300, 100, Path.Direction.CW);        canvas.drawPath(path, paint);    }
效果图如下:

绘制文本

drawText(String text, float x, float y, Paint paint)

这里特别注意的是y这个参数,它是以文本的baseline作为基准的,baseline的具体位置见下图:


    private void drawText(Canvas canvas) {        Paint paint = new Paint();        paint.setColor(Color.GREEN);        paint.setTextSize(200);        canvas.drawText("MfgiA", 200, 200, paint);    }
效果图如下:



drawPosText(char[] text, int index, int count, float[] pos, Paint paint)
drawPosText(String text, float[] pos, Paint paint)=drawPosText(text.toCharArray(), 0, text.length(), pos, paint)

该方法表示,截取text数组的前index,在获取从index到count区间内的字符,并将其画出,pos的大小必须大于或者等于count的2倍

    private void drawPosText(Canvas canvas) {        Paint paint = new Paint();        paint.setColor(Color.GREEN);        paint.setTextSize(200);        char[] text = new char[]{'d', 'r', 'a', 'w', 'P', 'o', 's'};        float[] pos = new float[]{100, 150,                200, 300,                300, 450,                400, 600,                500, 750,                600, 900,                700, 1050};        canvas.drawPosText(text, 2, 5, pos, paint);//        canvas.drawPosText("drawPos", pos, paint);    }
效果图如下:


drawTextOnPath(String text, Path path, int hOffset, int vOffset, Paint paint)

hOffset代表偏移原始位置的距离

vOffset为正数时,代表在路径上方的距离,为负数时,代表在路径下方的距离

    private void drawTextOnPath(Canvas canvas) {        Paint paint = new Paint();        paint.setTextSize(80);        Path path = new Path();        path.addCircle(540, 600, 400, Path.Direction.CCW);//        path.addRect(100, 100, 600, 400, Path.Direction.CCW);    }

效果图如下:



drawTextRun 没弄清这个的用法

画布裁剪

clipPath(Path path)// 对路径进行裁剪
clipRect(Rect rect)// 对矩阵进行裁剪
clipRegion(Region region)// 对区域进行裁剪

    private void clipPath(Canvas canvas) {        Path path = new Path();        path.addRect(100, 100, 500, 500, Path.Direction.CW);        canvas.clipPath(path);        canvas.drawColor(Color.GREEN);    }

    private void clipRect(Canvas canvas) {        canvas.clipRect(100, 100, 500, 500);        canvas.drawColor(Color.GREEN);    }
这两个方法表现的效果图是一样的,效果图如下:


   private void clipRegion(Canvas canvas) {        Rect rect = new Rect(100, 100, 500, 500);        Region region = new Region(rect);        canvas.clipRegion(region);        canvas.drawColor(Color.GREEN);    }
该方法从运行出来的结果看,是以屏幕左上角为起点,进行绘制的,效果图如下:



画布裁剪涉及到一个Region.Op区域组合,具体代码如下:

假设用A去组合B

   public enum Op {        DIFFERENCE(0),          //A-B        INTERSECT(1),           //A与B的交集        UNION(2),               //A与B的合集        XOR(3),                 //A与B的补集        REVERSE_DIFFERENCE(4),  //B-A        REPLACE(5);             //B替换A的区域    }

变换

translate(float x, float y)

相对于当前位置进行平移,x为正是向右平移,为负是向左平移;y为正向下平移,y为负向上平移

    private void translate(Canvas canvas) {        Paint paint = new Paint();        paint.setColor(Color.GREEN);        paint.setStyle(Paint.Style.STROKE);        paint.setStrokeWidth(10);        canvas.drawRect(100, 100, 500, 500, paint);        canvas.translate(100, 100);        paint.setColor(Color.YELLOW);        canvas.drawRect(100, 100, 500, 500, paint);    }

效果图如下:



scale(float sx, float sy)
scale(float sx, float sy, float px, float py)

sx和sy代表缩放比例,取值为[0, 1];px和py代表缩放的中心点,默认为左上角

    private void scale(Canvas canvas) {        Paint paint = new Paint();        paint.setColor(Color.GREEN);        paint.setStyle(Paint.Style.STROKE);        paint.setStrokeWidth(10);        canvas.drawRect(100, 100, 500, 500, paint);        canvas.scale(0.5f, 0.5f, 300, 300);        paint.setColor(Color.YELLOW);        canvas.drawRect(100, 100, 500, 500, paint);    }

效果图如下:



rotate(float degrees)
rotate(float degrees, float px, float py)

degrees代表旋转角度,正代表顺时针,负代表逆时针,px和py代表旋转的中心点,默认为左上角

   private void rotate(Canvas canvas) {        Paint paint = new Paint();        paint.setColor(Color.GREEN);        paint.setStyle(Paint.Style.STROKE);        paint.setStrokeWidth(10);        canvas.drawRect(100, 100, 600, 500, paint);        canvas.rotate(90, 350, 300);        paint.setColor(Color.YELLOW);        canvas.drawRect(100, 100, 600, 500, paint);    }

效果图如下:


skew(float sx, float sy)

sx和sy分别代表画布错切的值,sx和sy都是tan值,例如sx=1时,代表画布在x轴顺时针错切45度角

   private void skew(Canvas canvas) {        Paint paint = new Paint();        paint.setColor(Color.GREEN);        paint.setStyle(Paint.Style.STROKE);        paint.setStrokeWidth(10);        canvas.drawRect(100, 100, 600, 500, paint);        canvas.skew(1, 0);        paint.setColor(Color.YELLOW);        canvas.drawRect(100, 100, 600, 500, paint);    }

效果图如下:


状态

save()和restore()

save和restore要成对使用,如果restore比save次数多会引起崩溃,但是restore可以比save的次数要少。

save 保存canvas状态,save之后可以进行变换平移、缩放、旋转、错切等操作

restore 恢复save之前的状态,防止save之后的变换操作对后面的绘制产生影响

 private void saveAndRestore(Canvas canvas) {        Paint paint = new Paint();        paint.setColor(Color.GREEN);        paint.setStyle(Paint.Style.STROKE);        paint.setStrokeWidth(10);        canvas.drawRect(100, 100, 500, 500, paint);        paint.setColor(Color.WHITE);        canvas.drawRect(150, 150, 600, 600, paint);        canvas.save();        canvas.translate(100, 100);        paint.setColor(Color.YELLOW);        canvas.drawRect(100, 100, 500, 500, paint);        canvas.save();        canvas.scale(0.5f, 0.5f);        paint.setColor(Color.BLUE);        canvas.drawRect(100, 100, 500, 500, paint);        int saveCount = canvas.getSaveCount();        Log.i("CustomView", "saveCount = " + saveCount);//=3  save的次数+1//        canvas.restoreToCount(2);//表现跟canvas.restore()一样,后面进行translate的变换        canvas.restoreToCount(3);//跟没执行restore()一样,以为3以处于栈顶,后面进行translate和scale的变换        paint.setColor(Color.RED);        canvas.drawRect(150, 150, 450, 450, paint);        canvas.restore();        paint.setColor(Color.BLACK);        canvas.drawRect(200, 200, 400, 400, paint);    }

效果图如下:



参考文章

https://developer.android.com/reference/android/graphics/Canvas.html
http://blog.csdn.net/xsf50717/article/details/51527140

0 0