paint canvas绘图使用方法

canvas类:

Class Overview
The Canvas class holds the "draw" calls. To draw something, you need 4 basic components: A Bitmap to hold the pixels, a Canvas to host the draw calls (writing into the bitmap), a drawing primitive (e.g. Rect, Path, text, Bitmap), and a paint (to describe the colors and styles for the drawing). 
 
 
  • 1
  • 2
  • 1
  • 2

这个类相当于一个画布,你可以在里面画很多东西;

我们可以把这个Canvas理解成系统提供给我们的一块内存区域(但实际上它只是一套画图的API,真正的内存是下面的Bitmap),而且它还提供了一整套对这个内存区域进行操作的方法,所有的这些操作都是画图API。也就是说在这种方式下我们已经能一笔一划或者使用Graphic来画我们所需要的东西了,要画什么要显示什么都由我们自己控制。

这种方式根据环境还分为两种:一种就是使用普通View的canvas画图,还有一种就是使用专门的SurfaceView的canvas来画图。两种的主要是区别就是可以在SurfaceView中定义一个专门的线程来完成画图工作,应用程序不需要等待View的刷图,提高性能。前面一种适合处理量比较小,帧率比较小的动画,比如说象棋游戏之类的;而后一种主要用在游戏,高品质动画方面的画图。

下面是Canvas类常用的方法

  • drawRect(RectF rect, Paint paint) //绘制区域,参数一为RectF一个区域

  • drawPath(Path path, Paint paint) //绘制一个路径,参数一为Path路径对象

  • drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) //贴图,参数一就是我们常规的Bitmap对象,参数二是源区域(这里是bitmap),参数三是目标区域(应该在canvas的位置和大小),参数四是Paint画刷对象,因为用到了缩放和拉伸的可能,当原始Rect不等于目标Rect时性能将会有大幅损失。

  • drawLine(float startX, float startY, float stopX, float stopY, Paintpaint) //画线,参数一起始点的x轴位置,参数二起始点的y轴位置,参数三终点的x轴水平位置,参数四y轴垂直位置,最后一个参数为Paint 画刷对象。

  • drawPoint(float x, float y, Paint paint) //画点,参数一水平x轴,参数二垂直y轴,第三个参数为Paint对象。

  • drawText(String text, float x, floaty, Paint paint) //渲染文本,Canvas类除了上面的还可以描绘文字,参数一是String类型的文本,参数二x轴,参数三y轴,参数四是Paint对象。

  • drawOval(RectF oval, Paint paint)//画椭圆,参数一是扫描区域,参数二为paint对象;

  • drawCircle(float cx, float cy, float radius,Paint paint)// 绘制圆,参数一是中心点的x轴,参数二是中心点的y轴,参数三是半径,参数四是paint对象;

  • drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)//画弧,参数一是RectF对象,一个矩形区域椭圆形的界限用于定义在形状、大小、电弧,参数二是起始角(度)在电弧的开始, 
    参数三扫描角(度)开始顺时针测量的,参数四是如果这是真的话,包括椭圆中心的电弧,并关闭它,如果它是假这将是一个弧线,参数五是Paint对象;

还要理解一个paint类:

Class Overview
The Paint class holds the style and color information about how to draw geometries, text and bitmaps. 
paint类拥有风格和颜色信息如何绘制几何学,文本和位图。
Paint 代表了Canvas上的画笔、画刷、颜料等等;
Paint类常用方法:
setARGB(int a, int r, int g, int b) // 设置 Paint对象颜色,参数一为alpha透明值
setAlpha(int a) // 设置alpha不透明度,范围为0~255
setAntiAlias(boolean aa) // 是否抗锯齿
setColor(int color)  // 设置颜色,这里Android内部定义的有Color类包含了一些常见颜色定义
setTextScaleX(float scaleX)  // 设置文本缩放倍数,1.0f为原始
setTextSize(float textSize)  // 设置字体大小
setUnderlineText(booleanunderlineText)  // 设置下划线
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

案例

看一下效果图:

CustomActivity.java

public class CustomActivity extends Activity {  
    @Override  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.main);  
        init();  
    }  

    private void init() {  
        LinearLayout layout=(LinearLayout) findViewById(R.id.root);  
        final DrawView view=new DrawView(this);  
        view.setMinimumHeight(500);  
        view.setMinimumWidth(300);  
        //通知view组件重绘    
        view.invalidate();  
        layout.addView(view);  

    }  
}  
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

重要的类自定义View组件要重写View组件的onDraw(Canvase)方法,接下来是在该 Canvas上绘制大量的几何图形,点、直线、弧、圆、椭圆、文字、矩形、多边形、曲线、圆角矩形,等各种形状! 
DrawView.Java

public class DrawView extends View {  

    public DrawView(Context context) {  
        super(context);  
    }  

    @Override  
    protected void onDraw(Canvas canvas) {  
        super.onDraw(canvas);  
        /* 
         * 方法 说明 drawRect 绘制矩形 drawCircle 绘制圆形 drawOval 绘制椭圆 drawPath 绘制任意多边形 
         * drawLine 绘制直线 drawPoin 绘制点 
         */  
        // 创建画笔  
        Paint p = new Paint();  
        p.setColor(Color.RED);// 设置红色  

        canvas.drawText("画圆:", 10, 20, p);// 画文本  
        canvas.drawCircle(60, 20, 10, p);// 小圆  
        p.setAntiAlias(true);// 设置画笔的锯齿效果。 true是去除,大家一看效果就明白了  
        canvas.drawCircle(120, 20, 20, p);// 大圆  

        canvas.drawText("画线及弧线:", 10, 60, p);  
        p.setColor(Color.GREEN);// 设置绿色  
        canvas.drawLine(60, 40, 100, 40, p);// 画线  
        canvas.drawLine(110, 40, 190, 80, p);// 斜线  
        //画笑脸弧线  
        p.setStyle(Paint.Style.STROKE);//设置空心  
        RectF oval1=new RectF(150,20,180,40);  
        canvas.drawArc(oval1, 180, 180, false, p);//小弧形  
        oval1.set(190, 20, 220, 40);  
        canvas.drawArc(oval1, 180, 180, false, p);//小弧形  
        oval1.set(160, 30, 210, 60);  
        canvas.drawArc(oval1, 0, 180, false, p);//小弧形  

        canvas.drawText("画矩形:", 10, 80, p);  
        p.setColor(Color.GRAY);// 设置灰色  
        p.setStyle(Paint.Style.FILL);//设置填满  
        canvas.drawRect(60, 60, 80, 80, p);// 正方形  
        canvas.drawRect(60, 90, 160, 100, p);// 长方形  

        canvas.drawText("画扇形和椭圆:", 10, 120, p);  
        /* 设置渐变色 这个正方形的颜色是改变的 */  
        Shader mShader = new LinearGradient(0, 0, 100, 100,  
                new int[] { Color.RED, Color.GREEN, Color.BLUE, Color.YELLOW,  
                        Color.LTGRAY }, null, Shader.TileMode.REPEAT); // 一个材质,打造出一个线性梯度沿著一条线。  
        p.setShader(mShader);  
        // p.setColor(Color.BLUE);  
        RectF oval2 = new RectF(60, 100, 200, 240);// 设置个新的长方形,扫描测量  
        canvas.drawArc(oval2, 200, 130, true, p);  
        // 画弧,第一个参数是RectF:该类是第二个参数是角度的开始,第三个参数是多少度,第四个参数是真的时候画扇形,是假的时候画弧线  
        //画椭圆,把oval改一下  
        oval2.set(210,100,250,130);  
        canvas.drawOval(oval2, p);  

        canvas.drawText("画三角形:", 10, 200, p);  
        // 绘制这个三角形,你可以绘制任意多边形  
        Path path = new Path();  
        path.moveTo(80, 200);// 此点为多边形的起点  
        path.lineTo(120, 250);  
        path.lineTo(80, 250);  
        path.close(); // 使这些点构成封闭的多边形  
        canvas.drawPath(path, p);  

        // 你可以绘制很多任意多边形,比如下面画六连形  
        p.reset();//重置  
        p.setColor(Color.LTGRAY);  
        p.setStyle(Paint.Style.STROKE);//设置空心  
        Path path1=new Path();  
        path1.moveTo(180, 200);  
        path1.lineTo(200, 200);  
        path1.lineTo(210, 210);  
        path1.lineTo(200, 220);  
        path1.lineTo(180, 220);  
        path1.lineTo(170, 210);  
        path1.close();//封闭  
        canvas.drawPath(path1, p);  
        /* 
         * Path类封装复合(多轮廓几何图形的路径 
         * 由直线段*、二次曲线,和三次方曲线,也可画以油画。drawPath(路径、油漆),要么已填充的或抚摸 
         * (基于油漆的风格),或者可以用于剪断或画画的文本在路径。 
         */  

        //画圆角矩形  
        p.setStyle(Paint.Style.FILL);//充满  
        p.setColor(Color.LTGRAY);  
        p.setAntiAlias(true);// 设置画笔的锯齿效果  
        canvas.drawText("画圆角矩形:", 10, 260, p);  
        RectF oval3 = new RectF(80, 260, 200, 300);// 设置个新的长方形  
        canvas.drawRoundRect(oval3, 20, 15, p);//第二个参数是x半径,第三个参数是y半径  

        //画贝塞尔曲线  
        canvas.drawText("画贝塞尔曲线:", 10, 310, p);  
        p.reset();  
        p.setStyle(Paint.Style.STROKE);  
        p.setColor(Color.GREEN);  
        Path path2=new Path();  
        path2.moveTo(100, 320);//设置Path的起点  
        path2.quadTo(150, 310, 170, 400); //设置贝塞尔曲线的控制点坐标和终点坐标  
        canvas.drawPath(path2, p);//画出贝塞尔曲线  

        //画点  
        p.setStyle(Paint.Style.FILL);  
        canvas.drawText("画点:", 10, 390, p);  
        canvas.drawPoint(60, 390, p);//画一个点  
        canvas.drawPoints(new float[]{60,400,65,400,70,400}, p);//画多个点  

        //画图片,就是贴图  
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);  
        canvas.drawBitmap(bitmap, 250,360, p);  
    }  
}  
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112

参考链接

Android利用canvas画各种图形(点、直线、弧、圆、椭圆、文字、矩形、多边形、曲线、圆角矩形) - 任海丽(3G/移动开发) - 博客频道 - CSDN.NET

道Canvas可以绘制的对象有:

弧线(arcs)、填充颜色(argb和color)、 Bitmap、圆(circle和oval)、点(point)、线(line)、矩形(Rect)、图片(Picture)、圆角矩形 (RoundRect)、文本(text)、顶点(Vertices)、路径(path)。通过组合这些对象我们可以画出一些简单有趣的界面出来,但是光有这些功能还是不够的,如果我要画一个仪表盘(数字围绕显示在一个圆圈中)呢? 幸好Android还提供了一些对Canvas位置转换的方法:rorate、scale、translate、skew(扭曲)等,而且它允许你通过获得它的转换矩阵对象(getMatrix方法,不知道什么是转换矩阵?看这里) 直接操作它。这些操作就像是虽然你的笔还是原来的地方画,但是画纸旋转或者移动了,所以你画的东西的方位就产生变化。为了方便一些转换操作,Canvas 还提供了保存和回滚属性的方法(save和restore),比如你可以先保存目前画纸的位置(save),然后旋转90度,向下移动100像素后画一些图形,画完后调用restore方法返回到刚才保存的位置。下面我们就演示下canvas的一些简单用法:

protected void onDraw(Canvas canvas) {   

    canvas.drawCircle(100, 100, 90, paint);   
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

效果

@Override   
protected void onDraw(Canvas canvas) {   

    //绘制弧线区域   

    RectF rect = new RectF(0, 0, 100, 100);   

    canvas.drawArc(rect, //弧线所使用的矩形区域大小   
            0,  //开始角度   
            90, //扫过的角度   
            false, //是否使用中心   
            paint);   

}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

效果

protected void onDraw(Canvas canvas) {   

    //绘制弧线区域   

    RectF rect = new RectF(0, 0, 100, 100);   

    canvas.drawArc(rect, //弧线所使用的矩形区域大小   
            0,  //开始角度   
            90, //扫过的角度   
            true, //是否使用中心   
            paint);   

}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

两图对比我们可以发现,当 drawArcs(rect,startAngel,sweepAngel,useCenter,paint)中的useCenter为false时,弧线区域是用弧线开始角度和结束角度直接连接起来的,当useCenter为true时,是弧线开始角度和结束角度都与中心点连接,形成一个扇形。

protected void onDraw(Canvas canvas) {   

    canvas.drawColor(Color.BLUE);   

}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

canvas.drawColor是直接将View显示区域用某个颜色填充满。

@Override   
protected void onDraw(Canvas canvas) {   

    //画一条线   
    canvas.drawLine(10, 10, 100, 100, paint);   

}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

Canvas.drawOval:

@Override   
protected void onDraw(Canvas canvas) {   

    //定义一个矩形区域   
    RectF oval = new RectF(0,0,200,300);   
    //矩形区域内切椭圆   
    canvas.drawOval(oval, paint);   

}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

canvas.drawPosText:

@Override   
protected void onDraw(Canvas canvas) {   

    //按照既定点 绘制文本内容   
    canvas.drawPosText("Android777", new float[]{   
            10,10, //第一个字母在坐标10,10   
            20,20, //第二个字母在坐标20,20   
            30,30, //....   
            40,40,   
            50,50,   
            60,60,   
            70,70,   
            80,80,   
            90,90,   
            100,100   
    }, paint);   

}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

canvas.drawRect:

@Override   
    protected void onDraw(Canvas canvas) {   

        RectF rect = new RectF(50, 50, 200, 200);   

        canvas.drawRect(rect, paint);   

    }   

}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

rect的参数是表示左上角与右下角的坐标

canvas.drawRoundRect:

@Override   
protected void onDraw(Canvas canvas) {   

    RectF rect = new RectF(50, 50, 200, 200);   

    canvas.drawRoundRect(rect,   
                        30, //x轴的半径   
                        30, //y轴的半径   
                        paint);   

}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

canvas.drawPath:

@Override   
protected void onDraw(Canvas canvas) {   

    Path path = new Path(); //定义一条路径   
    path.moveTo(10, 10); //移动到 坐标10,10   
    path.lineTo(50, 60);   
    path.lineTo(200,80);   
    path.lineTo(10, 10);   

    canvas.drawPath(path, paint);   

}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

canvas.drawTextOnPath:

@Override   
        protected void onDraw(Canvas canvas) {   

            Path path = new Path(); //定义一条路径   
            path.moveTo(10, 10); //移动到 坐标10,10   
            path.lineTo(50, 60);   
            path.lineTo(200,80);   
            path.lineTo(10, 10);   

//          canvas.drawPath(path, paint);   
            canvas.drawTextOnPath("Android777开发者博客", path, 10, 10, paint);   

        }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

位置转换方法,canvas.rorate和canvas.translate:

@Override   
protected void onDraw(Canvas canvas) {   

    paint.setAntiAlias(true);   
    paint.setStyle(Style.STROKE);   
    canvas.translate(canvas.getWidth()/2, 200); //将位置移动画纸的坐标点:150,150   
    canvas.drawCircle(0, 0, 100, paint); //画圆圈   

    //使用path绘制路径文字   
    canvas.save();   
    canvas.translate(-75, -75);   
    Path path = new Path();   
    path.addArc(new RectF(0,0,150,150), -180, 180);   
    Paint citePaint = new Paint(paint);   
    citePaint.setTextSize(14);   
    citePaint.setStrokeWidth(1);   
    canvas.drawTextOnPath("http://www.android777.com", path, 28, 0, citePaint);   
    canvas.restore();   

    Paint tmpPaint = new Paint(paint); //小刻度画笔对象   
    tmpPaint.setStrokeWidth(1);   

    float  y=100;   
    int count = 60; //总刻度数   

    for(int i=0 ; i <count ; i++){   
        if(i%5 == 0){   
            canvas.drawLine(0f, y, 0, y+12f, paint);   
            canvas.drawText(String.valueOf(i/5+1), -4f, y+25f, tmpPaint);   

        }else{   
            canvas.drawLine(0f, y, 0f, y +5f, tmpPaint);   
        }   
        canvas.rotate(360/count,0f,0f); //旋转画纸   
    }   

    //绘制指针   
    tmpPaint.setColor(Color.GRAY);   
    tmpPaint.setStrokeWidth(4);   
    canvas.drawCircle(0, 0, 7, tmpPaint);   
    tmpPaint.setStyle(Style.FILL);   
    tmpPaint.setColor(Color.YELLOW);   
    canvas.drawCircle(0, 0, 5, tmpPaint);   
    canvas.drawLine(0, 10, 0, -65, paint);   

}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

上面几个例子基本已经将常用的canvas.draw*方法测试过了,我们结合一些事件,做一些有用户交互的应用:

package com.android777.demo.uicontroller.graphics;   

import java.util.ArrayList;   

import android.app.Activity;   
import android.content.Context;   
import android.graphics.Canvas;   
import android.graphics.Color;   
import android.graphics.Paint;   
import android.graphics.PointF;   
import android.os.Bundle;   
import android.view.MotionEvent;   
import android.view.View;   

public class CanvasDemoActivity extends Activity {   

    @Override   
    protected void onCreate(Bundle savedInstanceState) {   
        super.onCreate(savedInstanceState);   

        setContentView(new CustomView1(this));   

    }   

    /**   
     * 使用内部类 自定义一个简单的View   
     * @author Administrator   
     *   
     */
    class CustomView1 extends View{   

        Paint paint;   
        private ArrayList<PointF> graphics = new ArrayList<PointF>();   
        PointF point;   

        public CustomView1(Context context) {   
            super(context);   
            paint = new Paint(); //设置一个笔刷大小是3的黄色的画笔   
            paint.setColor(Color.YELLOW);   
            paint.setStrokeJoin(Paint.Join.ROUND);   
            paint.setStrokeCap(Paint.Cap.ROUND);   
            paint.setStrokeWidth(3);   

        }   

        @Override   
        public boolean onTouchEvent(MotionEvent event) {   

            graphics.add(new PointF(event.getX(),event.getY()));   

            invalidate(); //重新绘制区域   

            return true;   
        }   

        //在这里我们将测试canvas提供的绘制图形方法   
        @Override   
        protected void onDraw(Canvas canvas) {   
            for (PointF point : graphics) {   
                canvas.drawPoint(point.x, point.y, paint);   
            }   
//          super.onDraw(canvas);   

        }   
    }   

}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67

当用户点击时将出现一个小点,拖动时将画出一条用细点组成的虚线:

参考链接

Android Canvas绘图详解(图文) - 泡在网上的日子

Android利用canvas画各种图形(点、直线、弧、圆、椭圆、文字、矩形、多边形、曲线、圆角矩形) - 任海丽(3G/移动开发) - 博客频道 - CSDN.NET

完成






































前言:厚积方能薄发


相关文章:

《Android自定义控件三部曲文章索引》http://blog.csdn.net/harvic880925/article/details/50995268

经过前几篇,我们基本把paint和canvas的基本用法就看完了,今天我们来个大汇总,列举一个paint的所有函数,然后一个一个的过。经过这几篇,你将能学会paint中所有处理函数的用法。

一、基本用法

1、概述

我们先来看一下paint中基本设置的函数都有哪些: 
reset() 
重置画笔 
setColor(int color) 
给画笔设置颜色值 
setARGB(int a, int r, int g, int b) 
同样是设置颜色,但是利用ARGB分开设置 
setAlpha(int a) 
设置画笔透明度 
setStyle(Paint.Style style) 
设置画笔样式,取值有
  • Paint.Style.FILL :填充内部
  • Paint.Style.FILL_AND_STROKE :填充内部和描边
  • Paint.Style.STROKE :仅描边
setStrokeWidth(float width) 
设置画笔宽度 
setAntiAlias(boolean aa) 
设置画笔是否抗锯齿 
上面这些函数,我们在前面几篇已经详细讲过了,难度也都不大,不再细讲。下面几个函数我们是没有讲到过的,下面做下补充 
setStrokeCap(Paint.Cap cap) 
设置线冒样式,取值有Cap.ROUND(圆形线冒)、Cap.SQUARE(方形线冒)、Paint.Cap.BUTT(无线冒) 
setStrokeJoin(Paint.Join join) 
设置线段连接处样式,取值有:Join.MITER(结合处为锐角)、Join.Round(结合处为圆弧)、Join.BEVEL(结合处为直线) 
setStrokeMiter(float miter) 
设置笔画的倾斜度,90度拿画笔与30拿画笔,画出来的线条样式肯定是不一样的吧。(事实证明,根本看不出来什么区别好吗……囧……) 
setPathEffect(PathEffect effect) 
设置路径样式;取值类型是所有派生自PathEffect的子类:ComposePathEffect, CornerPathEffect, DashPathEffect, DiscretePathEffect, PathDashPathEffect, SumPathEffect
这四个函数中,setStrokeMiter(float miter)就不再讲了,我做过试验,没什么变化,也就是没啥屌用……,我们分别来看看另外三个函数的具体用法。

2、setStrokeCap(Paint.Cap cap)

设置线帽样式,取值有Cap.ROUND(圆形线帽)、Cap.SQUARE(方形线帽)、Paint.Cap.BUTT(无线帽) 
我先不讲什么叫做线冒,大家先来看看下面这段代码以及它的效果: 
有关自定义View控件的部分就不再讲了,我们已经啰嗦了六篇文章了,如果现在还不知道如何自定义一个控件让其显示绘图效果的话,可以去撞豆腐了
[java]   view plain  copy
  1. Paint paint = new Paint();  
  2. paint.setStrokeWidth(80);  
  3. paint.setAntiAlias(true);  
  4. paint.setColor(Color.GREEN);  
  5. paint.setStyle(Paint.Style.STROKE);  
  6.   
  7.   
  8. paint.setStrokeCap(Paint.Cap.BUTT);  
  9. canvas.drawLine(100,200,400,200,paint);  
  10.   
  11. paint.setStrokeCap(Paint.Cap.SQUARE);  
  12. canvas.drawLine(100,400,400,400,paint);  
  13.   
  14. paint.setStrokeCap(Paint.Cap.ROUND);  
  15. canvas.drawLine(100,600,400,600,paint);  
  16.   
  17. //垂直画出x=100这条线  
  18. paint.reset();  
  19. paint.setStrokeWidth(2);  
  20. paint.setColor(Color.RED);  
  21. canvas.drawLine(100,50,100,750,paint);  
在这里,我们水平画了三条线,他们的线冒类型分别是Cap.BUTT(无线帽)、Cap.SQUARE(方形线帽)、Cap.ROUND(圆形线冒) 
最后,垂直画出x=100的那条起始线:

从效果图中可以明显看出,从无线冒多出来的那块区域就是线帽!就相当于给原来的直线加上一个帽子一样,所以叫线帽 
android的线冒样式是很少的,只有方形和圆形两种,而在Windows SDK中,线冒样式多达十几种。


具体详情见博客:《GDI+学习及代码总结之—–画笔》

3、setStrokeJoin(Paint.Join join)

参数取值有:

  • Join.MITER(结合处为锐角)
  • Join.Round(结合处为圆弧)
  • Join.BEVEL(结合处为直线)

网上说,他们三个的区别如下:


但我运行出来的效果却不是如此,Join.Round和 Join.BEVEL没有明显的区别: 
我们画出来三个锐角的path,分别给这三段Path设置不同的连接方式:

[java]   view plain  copy
  1. Paint paint = new Paint();  
  2. paint.setStrokeWidth(40);  
  3. paint.setColor(Color.GREEN);  
  4. paint.setStyle(Paint.Style.STROKE);  
  5. paint.setAntiAlias(true);  
  6.   
  7. Path path  = new Path();  
  8. path.moveTo(100,100);  
  9. path.lineTo(450,100);  
  10. path.lineTo(100,300);  
  11. paint.setStrokeJoin(Paint.Join.MITER);  
  12. canvas.drawPath(path,paint);  
  13.   
  14. path.moveTo(100,400);  
  15. path.lineTo(450,400);  
  16. path.lineTo(100,600);  
  17. paint.setStrokeJoin(Paint.Join.BEVEL);  
  18. canvas.drawPath(path,paint);  
  19.   
  20. path.moveTo(100,700);  
  21. path.lineTo(450,700);  
  22. path.lineTo(100,900);  
  23. paint.setStrokeJoin(Paint.Join.ROUND);  
  24. canvas.drawPath(path,paint);  
效果为:

4、setPathEffect(PathEffect effect)

设置路径样式;取值类型是所有派生自PathEffect的子类


我们一个个来看他们的效果: 
(1)、CornerPathEffect——圆形拐角效果 
它的作用就是将原来Path生硬的直线拐角,变成圆形拐角:


上面的那条是Path默认的直线拐角,下面的那条是圆形拐角。 
其构造函数为:

[java]   view plain  copy
  1. public CornerPathEffect(float radius)  
它只有一个参数radius:即当前连接两条直线所使用的圆的半径。

上面这个图,很清晰的展示了利用半径R=50的圆来代替原来两条直线间的夹角。 
我们利用代码,再来看看具体效果:

[java]   view plain  copy
  1. Paint paint = new Paint();  
  2. paint.setStrokeWidth(4);  
  3. paint.setColor(Color.GREEN);  
  4. paint.setStyle(Paint.Style.STROKE);  
  5.   
  6. Path path = new Path();  
  7. path.moveTo(100,600);  
  8. path.lineTo(400,100);  
  9. path.lineTo(700,900);  
  10.   
  11. canvas.drawPath(path,paint);  
  12.   
  13. paint.setColor(Color.RED);  
  14. paint.setPathEffect(new CornerPathEffect(100));  
  15. canvas.drawPath(path,paint);  
  16.   
  17. paint.setColor(Color.YELLOW);  
  18. paint.setPathEffect(new CornerPathEffect(200));  
  19. canvas.drawPath(path,paint);  
在这里,我利用Path构造了一个夹角,在同一个位置画了三遍,第一遍是没有添加任何PathEffect的;第二遍,CornerPathEffect的圆半径为100;第三遍CornerPathEffect的圆半径为200;

很明显能看出在半径不同情况下,连接位置也是不一样的。 
(2)、DashPathEffect——虚线效果 
这个功能能够实现虚线段的效果:


它的函数声名如下:

[java]   view plain  copy
  1. public DashPathEffect(float intervals[], float phase)  
其中: 
intervals[]: 表示组成虚线的各个线段的长度;整条虚线就是由intervals[]中这些基本线段循环组成的。比如,我们定义new float[] {20,10};那这个虚线段就是由两段线段组成的,第一个可见的线段长为20,每二个线段不可见,长度为10;

对于intervals[]数组的有两个限定:

  • 长度必须大于等于2;因为必须有一个实线段和一个空线段来组成虚线。
  • 个数必须为偶数,如果是基数,最后一个数字将被忽略;这个很好理解,因为一组虚线的组成必然是一个实线和一个空线成对组成的。
我们再做一个假设,如果我们定义intervals[]为new float[] {20,10,100,100};那么这条虚线将是由四条子线段循环组成的,第一条实线长度为20,第二个空线长度为10,第三个实线长为100,第四条空线长充为100; 
phase: 开始绘制的偏移值 
我们来看看代码的运行效果来验证我们想的是否正确:
[java]   view plain  copy
  1. Paint paint = getPaint();  
  2. Path path = new Path();  
  3. path.moveTo(100,600);  
  4. path.lineTo(400,100);  
  5. path.lineTo(700,900);  
  6.   
  7. canvas.drawPath(path,paint);  
  8. paint.setColor(Color.RED);  
  9.   
  10. //使用DashPathEffect画线段  
  11. paint.setPathEffect(new DashPathEffect(new float[]{20,10,100,100},0));  
  12. canvas.translate(0,100);  
  13. canvas.drawPath(path,paint);  
  14.   
  15. //画同一条线段,偏移值为15  
  16. paint.setPathEffect(new DashPathEffect(new float[]{20,10,50,100},15));  
  17. paint.setColor(Color.YELLOW);  
  18. canvas.translate(0,100);  
  19. canvas.drawPath(path,paint);  
其中把paint封装成一个getPaint方法来获取基本的画笔设置。
[java]   view plain  copy
  1. private Paint getPaint(){  
  2.     Paint paint = new Paint();  
  3.     paint.setStrokeWidth(4);  
  4.     paint.setColor(Color.GREEN);  
  5.     paint.setStyle(Paint.Style.STROKE);  
  6.     paint.setAntiAlias(true);  
  7.     return paint;  
  8. }  
效果图如下:

从这个效果图中可以看到两点: 
第一:红线段的基本组成部分,分别长度为20,10,100,100实线段和空线段组成的 
第二:黄线段位移了15,从开始处就可以明显看出效果。原来20的线段,只剩5,所以看起来就像一个点一样。大家也可以发挥想象,利用动画设置偏移量让这条虚线段动起来:


给大家一个提示,使用ValueAnimator,动画长度值设为一个虚线的一个基线的长度,这里的基线是由20,10,100,100组成的,所以一个基线长度是230,然后设置成无限循环,把拿到的值设置为DashPathEffect偏移量即可; 
如果看完了我的动画系列,这个小动画是不会成问题的,这里就不再讲了,不懂的同学,看源码吧,源码里有这个动画。 
(3)、DiscretePathEffect——离散路径效果


同样,图中第一条线是原生的,第二条线加上离散路径效果后的样式。 
DiscretePathEffect就是将原来路径分隔成定长的线段,然后将每条线段随机偏移一段位置,我们可以用它来模拟一种类似生锈铁丝的效果; 
它的构造函数如下:

[java]   view plain  copy
  1. public DiscretePathEffect(float segmentLength, float deviation)  
第一个参数segmentLength:表示将原来的路径切成多长的线段。如果值为2,那么这个路径就会被切成一段段由长度为2的小线段。所以这个值越小,所切成的小线段越多;这个值越大,所切成的小线段越少。 
第二参数deviation:表示被切成的每个小线段的可偏移距离。值越大,就表示每个线段的可偏移距离就越大,就显得越凌乱,值越小,每个线段的可偏移原位置的距离就越小。 
我们看下代码效果:
[java]   view plain  copy
  1. Paint paint = getPaint();  
  2. Path path = getPath();  
  3. //第一条原生Path  
  4. canvas.drawPath(path,paint);  
  5. //第二条Path  
  6. canvas.translate(0,200);  
  7. paint.setPathEffect(new DiscretePathEffect(2,5));  
  8. canvas.drawPath(path,paint);  
  9. //第三条Path  
  10. canvas.translate(0,200);  
  11. paint.setPathEffect(new DiscretePathEffect(6,5));  
  12. canvas.drawPath(path,paint);  
  13. //第四条Path  
  14. canvas.translate(0,200);  
  15. paint.setPathEffect(new DiscretePathEffect(6,15));  
  16. canvas.drawPath(path,paint);  
我们这里涉及到一个函数getPath函数就是随机生成一条路径;我们先来看效果,然后再回来看getPath()的实现

从第二条和第三条相比,可以明显看出,在仅增大segmentLength的情况下,很明显第三条线段所切的子线段要大一些,所以就没有第二条曲线的那么多线段相交所产生的折点,所以相比第二条更顺滑一些,当然铁锈效果就没有第二条那么明显了。 
第三条和第四条相比,在segmentLength都是6的情况下,在第四条仅增大了deviation参数(偏移距离),从效果图中也明显可以看出每个子线段向外偏移的距离也增大了。 
从效果图中很明显可以看出各个参数的作用,这里就不多讲了,下面回过头来看看getPath()得到一个随机路径的方法:

[java]   view plain  copy
  1. private Path getPath(){  
  2.     Path path = new Path();  
  3.     // 定义路径的起点  
  4.     path.moveTo(00);  
  5.   
  6.     // 定义路径的各个点  
  7.     for (int i = 0; i <= 40; i++) {  
  8.         path.lineTo(i*35, (float) (Math.random() * 150));  
  9.     }  
  10.     return path;  
  11. }  
就是利用Math.random()产生一个随机数,然后每隔35px构造出一个点。代码难度不大,不再细讲。 
(4)、PathDashPathEffect——印章路径效果 
这个名字是我自己取的……,因为这个功能就相当于Photoshop中的印章功能。 
它的作用就是用另一个路径图案做为印章,沿着指定路径一个个盖上去。 
我们先来看看PathDashPathEffect的声明:
[java]   view plain  copy
  1. public PathDashPathEffect(Path shape, float advance, float phase,Style style)   
其中:
  • Path shape:表示印章路径,比如我们下面示例中的三角形加右上角一个点;
  • float advance:表示两个印章路径间的距离,很容易理解,印章间距离越大,间距就越大。
  • float phase:路径绘制偏移距离,与上面DashPathEffect中的float phase参数意义相同
  • Style style:表示在遇到转角时,如何操作印章以使转角平滑过渡,取值有:Style.ROTATE,Style.MORPH,Style.TRANSLATE;Style.ROTATE表示通过旋转印章来过渡转角;Style.MORPH表示通过变形印章来过渡转角;Style.TRANSLATE表示通过位移来过渡转角。这三个效果的具体意义,上面会通过具体示例来分别讲解。
然后我们看看如何来实现下面这个效果:

如上图所示,印章路径就是一个三角形加右上角一个点。然后就拿这个印章路径在原来的路径上每隔一定距离盖章。 
上图的对应代码为:

[java]   view plain  copy
  1. Paint paint = getPaint();  
  2.   
  3. //画出原始路径  
  4. Path path  = new Path();  
  5. path.moveTo(100,600);  
  6. path.lineTo(400,100);  
  7. path.lineTo(700,900);  
  8. canvas.drawPath(path,paint);  
  9.   
  10. //构建印章路径  
  11. Path stampPath  = new Path();  
  12. stampPath.moveTo(0,20);  
  13. stampPath.lineTo(10,0);  
  14. stampPath.lineTo(20,20);  
  15. stampPath.close();  
  16. stampPath.addCircle(0,0,3, Path.Direction.CCW);  
  17.   
  18. //使用印章路径效果  
  19. canvas.translate(0,200);  
  20. paint.setPathEffect(new PathDashPathEffect(stampPath,35,0, PathDashPathEffect.Style.TRANSLATE));  
  21. canvas.drawPath(path,paint);  
最重要的是最后几句
[java]   view plain  copy
  1. //构建印章路径  
  2. Path stampPath  = new Path();  
  3. stampPath.moveTo(0,20);  
  4. stampPath.lineTo(10,0);  
  5. stampPath.lineTo(20,20);  
  6. stampPath.close();  
  7. stampPath.addCircle(0,0,3, Path.Direction.CCW);  
  8.   
  9. //使用印章路径效果  
  10. canvas.translate(0,200);  
  11. paint.setPathEffect(new PathDashPathEffect(stampPath,35,0, PathDashPathEffect.Style.TRANSLATE));  
首先构造一个印章路径,然后利用
[java]   view plain  copy
  1. paint.setPathEffect(new PathDashPathEffect(stampPath,35,0, PathDashPathEffect.Style.TRANSLATE));  
来构造一个印章画笔,然后就利用这个画笔在指定路径上画图
[java]   view plain  copy
  1. canvas.drawPath(path,paint);  
在示例中我们有两个路径,大家一定要把路径和印章路径分清楚,一个路径是path,这个是原始路径;另一个路径是stampPath,这个是印章路径。印章路径是用来构造画笔的(paint),而原始的路径就是用这个画笔来做画的。 
下面我们就来看看,在Style不同的情况下,在转角处都如何处理的:
[java]   view plain  copy
  1. private void drawPathDashPathEffect(Canvas canvas){  
  2.     Paint paint = getPaint();  
  3.     Path path = getPath();  
  4.     canvas.drawPath(path,paint);  
  5.   
  6.     canvas.translate(0,200);  
  7.     paint.setPathEffect(new PathDashPathEffect(getStampPath(),35,0, PathDashPathEffect.Style.MORPH));  
  8.     canvas.drawPath(path,paint);  
  9.   
  10.     canvas.translate(0,200);  
  11.     paint.setPathEffect(new PathDashPathEffect(getStampPath(),35,0, PathDashPathEffect.Style.ROTATE));  
  12.     canvas.drawPath(path,paint);  
  13.   
  14.     canvas.translate(0,200);  
  15.     paint.setPathEffect(new PathDashPathEffect(getStampPath(),35,0, PathDashPathEffect.Style.TRANSLATE));  
  16.     canvas.drawPath(path,paint);  
  17. }  
  18.   
  19. private Path getStampPath(){  
  20.     Path path  = new Path();  
  21.     path.moveTo(0,20);  
  22.     path.lineTo(10,0);  
  23.     path.lineTo(20,20);  
  24.     path.close();  
  25.   
  26.     path.addCircle(0,0,3, Path.Direction.CCW);  
  27.     return path;  
  28. }  
这段代码通过getPath()函数随机生成一条路径,并将原始路径和各个Style的路径画出来。第一条是原始路径,第二条的Style是Style.MORPH,第三条是Style.ROTATE,第四条是Style.TRANSLATE;

大家第一眼估计就看得眼花缭乱了,仔细看一下红线处的三角形的样式。


同样是转角,如上图,当Style.MORPH时,就是通过对印章进行变形来过渡转角的


当Style为Style.ROTATE时就是靠旋转印章角度来过渡转角的


当Style为Style.TRANSLATE时,即不会对印章变形也不会对旋转印章角度,而只是通过变改印章的位置来过渡 
其中的一个参数float phase,表示路径绘制偏移距离我们还没讲,这个与上面的偏移距离的意义一样,通过改变偏移距离同样可以实现动画


(5)、ComposePathEffect与SumPathEffect 
这两个都是用来合并两个特效的。但它们之间是有区别的:

[java]   view plain  copy
  1. public ComposePathEffect(PathEffect outerpe, PathEffect innerpe)  
ComposePathEffect合并两个特效是有先后顺序的,它会先将第二个参数的PathEffect innerpe的特效作用于路径上,然后再在此加了特效的路径上作用第二个特效。
[java]   view plain  copy
  1. public SumPathEffect(PathEffect first, PathEffect second)  
而SumPathEffect是分别对原始路径分别作用第一个特效和第二个特效。然后再将这两条路径合并,做为最终结果。 
我们来看看效果:
[java]   view plain  copy
  1. //画原始路径  
  2. Paint paint = getPaint();  
  3. Path path = getPath();  
  4. canvas.drawPath(path,paint);  
  5.   
  6. //仅应用圆角特效的路径  
  7. canvas.translate(0,200);  
  8. CornerPathEffect cornerPathEffect = new CornerPathEffect(100);  
  9. paint.setPathEffect(cornerPathEffect);  
  10. canvas.drawPath(path,paint);  
  11.   
  12. //仅应用虚线特效的路径  
  13. canvas.translate(0,200);  
  14. DashPathEffect dashPathEffect = new DashPathEffect(new float[]{2,5,10,10},0);  
  15. paint.setPathEffect(dashPathEffect);  
  16. canvas.drawPath(path,paint);  
  17.   
  18. //利用ComposePathEffect先应用圆角特效,再应用虚线特效  
  19. canvas.translate(0,200);  
  20. ComposePathEffect composePathEffect = new ComposePathEffect(dashPathEffect,cornerPathEffect);  
  21. paint.setPathEffect(composePathEffect);  
  22. canvas.drawPath(path,paint);  
  23.   
  24. //利用SumPathEffect,分别将圆角特效应用于原始路径,然后将生成的两条特效路径合并  
  25. canvas.translate(0,200);  
  26. paint.setStyle(Paint.Style.STROKE);  
  27. SumPathEffect sumPathEffect = new SumPathEffect(cornerPathEffect,dashPathEffect);  
  28. paint.setPathEffect(sumPathEffect);  
  29. canvas.drawPath(path,paint);  
代码很简单,就是画几条特效路径 ,效果图如下:

特别注意路径D和路径E: 
路径D的生成方法为:

[java]   view plain  copy
  1. ComposePathEffect(dashPathEffect,cornerPathEffect);  
表示先将圆角特效应用于原始路径,得到路径B,然后再在路径B的基础上应用虚线特效得到最终的效果D; 
而路径E的生成方法则比较弱智:
[java]   view plain  copy
  1. SumPathEffect(cornerPathEffect,dashPathEffect);  
它的原理是,先将圆角特效应用于原始路径A得到路径B,然后将虚线特效应依然应用于原始路径,得到路径C.然后将路径B和路径C合并(即画在一起……囧),就得到路径E 
好了,到这里,所有的路径效果都讲完了,真TM累死哥了…… 
源码在文章底部给出

二、字体相关

setTextSize(float textSize) 
设置文字大小 
setFakeBoldText(boolean fakeBoldText) 
设置是否为粗体文字 
setStrikeThruText(boolean strikeThruText) 
设置带有删除线效果 
setUnderlineText(boolean underlineText) 
设置下划线 
setTextAlign(Paint.Align align) 
设置开始绘图点位置 
setTextScaleX(float scaleX) 
水平拉伸设置 
setTextSkewX(float skewX) 
设置字体水平倾斜度,普通斜体字是-0.25,可见往右斜 
setTypeface(Typeface typeface) 
字体样式 
上面的这些方法,我们在 《android Graphics(二):路径及文字》 《android Graphics( 五):drawText()详解》 已经详细讲过,这里就不再细讲 
下面补充几个没讲到的函数:

1、setLinearText(boolean linearText)

设置是否打开线性文本标识;由于文本想要快速绘制出来,必然是需要提前缓存在显存中的,一般而言每个文字需要一个字节的大小来存储它(当然具体需要多少字节与编码方式有关),那如果是长篇文章,可见所需的大小可想而知。我们可以通过setLinearText (true)告诉Android我们不需要这样的文本缓存。但如果我们不用文本缓存,虽然能够省去一些内存空间,但这是以显示速度为代价的。 
由于这个是API 1的函数,由于当时的android手机的内存大小还是很小的,所以尽量减少内存使用是每个应用的头等大事,在当时的的环境下这个函数还是很有用的。 
但在今天,内存动不动就是4G以上了,文本缓存的所占的那点内存就微不足道了,没有哪个APP会牺牲性能来减少这点这内存占用了,所以这个函数基本没用了。

2、setSubpixelText(boolean subpixelText)

表示是否打开亚像素设置来绘制文本。亚像素的概念比较难理解,首先,我们都知道像素,比如一个android手机的分辨率是1280*720,那就是指它的屏幕在垂直方向有1280个像素点,水平方向上有720个像素点。我们知道每个像素点都是一个独立显示一个颜色的个体。所以如果一副图片,在一个屏幕上用了300*100个相素点,而在另一个屏幕上却用了450*150个像素来显示。那么,请问在哪个屏幕上这张图片显示的更清晰?当然是第二个屏幕,因为它使用的像素点更多,所显示的细节更精细。 
那么问题来了,android设置在出厂时,设定的像素显示都是固定的几个范围:320*480,480*800,720*1280,1080*1920等等;那么如何在同样的分辨率的显示器中增强显示清晰度呢? 
亚像素的概念就油然而生了,亚像素就是把两个相邻的两个像素之间的距离再细分,再插入一些像素,这些通过程序加入的像素就是亚像素。在两个像素间插入的像素个数是通过程序计算出来的,一般是插入两个、三个或四个。 
所以打开亚像素显示,是可以在增强文本显示清晰度的,但由于插入亚像素是通过程序计算而来的,所以会耗费一定的计算机性能。注意:亚像素是通过程序计算出来模拟插入的,在没有改变硬件构造的情况下,来改善屏幕分辨率大小。 
亚像素显示,是仅在液晶显示器上使用的一种增强字体清晰度的技术。但这种技术有时会出现问题,用投影仪投射到白色墙壁上,会出出字体显示不正常的情况,而且对于老式的CRT显示器是根本不支持的。 
在android还没有出现时,windows已经能够支持亚像素显示了,在windows机器中,这个功能叫做ClearType,在以前讲述windows的GDI绘图时,也曾经讲过ClearType的应用效果。

这个图片中的最后一行文字,就是在windows上应用CLearType技术后的显示效果。此图片摘自《 GDI+学习及代码总结之——文本与字体》可见在windows的显示中,还是有明显差别的 
但我放在android里面来试,并未发现明显的差别,可能是我的显示器分辨率太高了……汗……

[java]   view plain  copy
  1. Paint paint = new Paint();  
  2. paint.setColor(Color.GREEN);  
  3. String text = "乌龟&梦想";  
  4. paint.setTextSize(200);  
  5.   
  6. paint.setSubpixelText(false);  
  7. canvas.drawText(text,0,200,paint);  
  8.   
  9. canvas.translate(0,300);  
  10. paint.setSubpixelText(true);  
  11. canvas.drawText(text,0,200,paint);  
效果图如下:

三、其它

接下来还剩几个跟图片和测量相关的函数,我们接下来分篇慢慢讲解。

1、图像处理:

[java]   view plain  copy
  1. setShader(Shader shader)  
  2. setShadowLayer(float radius, float dx, float dy, int shadowColor)  
  3. setDither(boolean dither)  
  4. setColorFilter(ColorFilter filter)  
  5. setXfermode(Xfermode xfermode)  
  6. setFilterBitmap(boolean filter)  
  7. clearShadowLayer()  

2、measure测量相关

[java]   view plain  copy
  1. breakText(char[] text, int index, int count, float maxWidth, float[] measuredWidth)  
  2. measureText(String text)  
好了,本文就到这了




























Paint setStrokeWidth方法:设置空心线宽

原创  2015年03月05日 09:26:49

setStrokeWidth方法:设置空心线宽

【功能说明】该方法用于设置画笔的空心线宽。该方法在矩形、圆形等图形上有明显的效果。

【基本语法】public void setStrokeWidth (float width)

其中,参数width为线宽,浮点型数据。

【实例演示】下面通过代码来演示如何设置宽线宽的空心画笔。

protected void onDraw(Canvas canvas) {                  //重载onDraw方法  

super.onDraw(canvas);  

paint.setColor(Color.RED);                          //设置画笔颜色  

 paint.setStyle(Style.STROKE);                       //设置画笔为空心  

 paint.setStrokeWidth((float) 10.0);             //设置线宽  

canvas.drawColor(Color.WHITE); 

 canvas.drawLine(50, 50, 450, 50, paint);            //绘制直线   

canvas.drawRect(100, 100, 200, 600, paint);         //绘制矩形  

 canvas.drawRect(300, 100, 400, 600, paint);         //绘制矩形  

}

在这段代码中,首先设置了画笔的颜色,然后通过setStyle设置画笔为空心,接着通过setStrokeWidth方法设置线宽。最后,用此画笔来绘制直线和矩形。读者运行这段代码,可以在手机屏幕上看到如图显示效果。

   设置空心显示效果

 设置空心线宽

























///慕课网  D3制作图表

https://www.imooc.com/learn/103



*///   慕课网Canvas绘图详解

https://www.imooc.com/learn/185

*///   慕课网------程序员的梦工厂

https://www.imooc.com/






  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中的Canvas绘图源码实例,主要是线条、弧线、圆角矩形、三角形、文字、圆形等简单的几何图形,主要是通过创建DrawingCanvas对象canvas,为canvas设置命令监听者,实现接口CommandListener的方法,把缓冲区图像的内容绘制到布上,图部分代码:   int w = getWidth(); // 布的宽度   int h = getHeight(); // 布的高度   Image buffer = Image.createImage(w, h); // 用于绘图的缓冲图像   Graphics gc = buffer.getGraphics(); // 获取缓冲图像的图形环境   // 清除布   public void clearScreen() {    gc.setColor(255,255,255); // 设置绘图颜色为白色    gc.fillRect(0,0,w,h); // 把缓冲图像填充为白色    gc.setColor(255,0,0); // 设置绘图颜色为红色   }   // 绘制直线   public void drawLine() {    setTitle("直线"); // 设置布的标题    clearScreen(); // 清除布    gc.drawLine(10,10,w-20,h-20); // 绘制黑色直线    gc.setColor(0,0,255); // 设置绘图颜色为蓝色    gc.drawLine(10,h/2,w-10,h/2); // 绘制蓝色直线   }   // 绘制弧   public void drawArc() {    setTitle("弧线和填充弧");    clearScreen();    gc.drawArc(5,5,w/2-20,h/2-20,60,216); // 绘制弧线    gc.drawArc(5,h/2-10,w/2-20,h/2-20,0,360); // 绘制圆    gc.setColor(0,0,255);    gc.fillArc(w/2,5,w/2-20,h/2-20,60,216); // 绘制填充弧线    gc.fillArc(w/2,h/2-10,w/2-20,h/2-20,0,360); // 绘制填充圆   }   // 绘制矩形   public void drawRect() {    setTitle("矩形和填充矩形");    clearScreen();    gc.drawRect(25,25,w/2-30,h/2-30); // 绘制矩形    gc.fillRect(w/2 25,25,w/2-30,h/2-30); // 绘制填充矩形   }   // 绘制圆角矩形   public void drawRoundRect() {    setTitle("圆角矩形和填充圆角矩形");    clearScreen();    gc.drawRoundRect(5,5,w-5-30,h/2-30,20,20); // 绘制圆角矩形    gc.setColor(0,0,255);    gc.fillRoundRect(5,h/2,w-30,h/2-30,20,20); // 绘制填充圆角矩形   }   // 绘制三角形   public void drawTriangle() {    setTitle("填充三角形");    clearScreen();    gc.fillTriangle(w/2, h/6, w/6, h/2, w/2, h/2);   }   // 绘制文字   public void drawText() {    setTitle("文字"); //设置标题    clearScreen();    gc.setFont(Font.getFont(Font.FACE_SYSTEM,Font.STYLE_BOLD,Font.SIZE_SMALL)); // 设置字体    gc.drawString("Hello World!",0,0,gc.TOP|gc.LEFT); // 使用当前字体绘制文字    gc.setFont(Font.getFont(Font.FACE_SYSTEM,Font.STYLE_BOLD|Font.STYLE_UNDERLINED,Font.SIZE_LARGE));    gc.drawString("Hello
要在PaintCanvas上绘制动态的打勾和打叉效果,可以按照以下步骤进行: 1. 创建一个自定义的View,重写它的onDraw()方法。 2. 在onDraw()方法中,使用Canvas绘制一个圆圈,并设置圆圈的颜色和边框宽度。 3. 绘制打勾或打叉的动。对于打勾效果,可以使用Path对象来绘制一条斜线和一条水平线组成的打勾形状。对于打叉效果,可以使用Path对象来绘制两条斜线组成的打叉形状。 4. 在onDraw()方法中,使用Paint绘制动效果,设置绘制的颜色和宽度。 5. 在View中设置动效果,使用ValueAnimator或ObjectAnimator来控制动的进度,然后在onDraw()方法使用进度来绘制动效果。 以下是一个简单的示例代码,可以绘制一个打勾的动态效果: ```java public class TickView extends View { private Paint mPaint; private Path mPath; private float mProgress; public TickView(Context context) { super(context); init(); } public TickView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public TickView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { mPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(8); mPaint.setColor(Color.GREEN); mPath = new Path(); mPath.moveTo(50, 150); mPath.lineTo(100, 200); mPath.lineTo(200, 100); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 绘制圆圈 canvas.drawCircle(150, 150, 100, mPaint); // 绘制打勾效果 Path dst = new Path(); PathMeasure measure = new PathMeasure(mPath, false); measure.getSegment(0, measure.getLength() * mProgress, dst, true); canvas.drawPath(dst, mPaint); } public void startAnimation() { ValueAnimator animator = ValueAnimator.ofFloat(0, 1); animator.setDuration(1000); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mProgress = (float) animation.getAnimatedValue(); invalidate(); } }); animator.start(); } } ``` 这个示例代码中,TickView继承自View,并重写了它的onDraw()方法。在init()方法中,我们创建了一个Paint对象和一个Path对象,用来绘制打勾的形状。在onDraw()方法中,我们首先绘制了一个圆圈,然后使用PathMeasure对象来截取Path对象的一部分,根据动进度来绘制打勾的效果。最后,在startAnimation()方法中,我们创建了一个ValueAnimator对象来控制动的进度,并在onAnimationUpdate()方法中更新动进度,然后调用invalidate()方法来重绘View。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值