android canvas的一些知识

来源:互联网 发布:php如何获取js的变量 编辑:程序博客网 时间:2024/06/02 11:09

Android中使用图形处理引擎,2D部分是android SDK内部自己提供,3D部分是用Open GL ES 1.0。今天我们主要要了解的是2D相关的,如果你想看3D的话那么可以跳过这篇文章。

大部分2D使用的api都在android.graphics和android.graphics.drawable包中。他们提供了图形处理相关的: Canvas、ColorFilter、Point(点)和RetcF(矩形)等,还有一些动画相关的:AnimationDrawable、 BitmapDrawable和TransitionDrawable等。以图形处理来说,我们最常用到的就是在一个View上画一些图片、形状或者自定义的文本内容,这里我们都是使用Canvas来实现的。你可以获取View中的Canvas对象,绘制一些自定义形状,然后调用View. invalidate方法让View重新刷新,然后绘制一个新的形状,这样达到2D动画效果。下面我们就主要来了解下Canvas的使用方法。

Canvas对象的获取方式有两种:一种我们通过重写View.onDraw方法,View中的Canvas对象会被当做参数传递过来,我们操作这个Canvas,效果会直接反应在View中。另一种就是当你想创建一个Canvas对象时使用的方法:

1
2
Bitmap b = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);  
Canvas c = newCanvas(b);

上面代码创建了一个尺寸是100*100的Bitmap,使用它作为Canvas操作的对象,这时候的Canvas就是使用创建的方式。当你使用创建的Canvas在bitmap上执行绘制方法后,你还可以将绘制的结果提交给另外一个Canvas,这样就可以达到两个Canvas协作完成的效果,简化逻辑。但是android SDK建议使用View.onDraw参数里提供的Canvas就好,没必要自己创建一个新的Canvas对象。接下来我们看看Canvas提供我们哪些绘制图形的方法。我们创建一个自定义View对象,使用onDraw方法提供的Canvas进行绘制图形。

CanvasDemoActivity.java:

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
package com.android777.demo.uicontroller.graphics;  
                                                                                                                                  
import android.app.Activity;  
import android.content.Context;  
import android.graphics.Canvas;  
import android.graphics.Color;  
import android.graphics.Paint;  
import android.os.Bundle;  
import android.view.View;  
                                                                                                                                  
public class CanvasDemoActivity extends Activity {  
                                                                                                                                  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
                                                                                                                                  
        setContentView(newCustomView1(this));  
                                                                                                                                  
    }  
                                                                                                                                  
    /**  
     * 使用内部类 自定义一个简单的View  
     * @author Administrator  
     *  
     */
    class CustomView1 extends View{  
                                                                                                                                  
        Paint paint;  
                                                                                                                                  
        public CustomView1(Context context) {  
            super(context);  
            paint = newPaint(); //设置一个笔刷大小是3的黄色的画笔  
            paint.setColor(Color.YELLOW);  
            paint.setStrokeJoin(Paint.Join.ROUND);  
            paint.setStrokeCap(Paint.Cap.ROUND);  
            paint.setStrokeWidth(3);  
        }  
                                                                                                                                  
        //在这里我们将测试canvas提供的绘制图形方法  
        @Override  
        protected void onDraw(Canvas canvas) {  
                                                                                                                                  
        }  
                                                                                                                                  
    }  
                                                                                                                                  
}


执行结果是一片黑色的区域,因为在自定义的CustomView1中,我们没有做任何的绘制操作。canvas提供的绘制图形的方法都是以draw开头的,我们可以查看api:

Css_bugtester

从上面方法的名字看来我们可以知道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的一些简单用法:

1
2
3
4
protected void onDraw(Canvas canvas) {  
                                                                                                                                  
    canvas.drawCircle(100, 100, 90, paint);  
}


效果是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override  
protected void onDraw(Canvas canvas) {  
                                                                                                                                  
    //绘制弧线区域  
                                                                                                                                  
    RectF rect = newRectF(0, 0, 100, 100);  
                                                                                                                                  
    canvas.drawArc(rect,//弧线所使用的矩形区域大小  
            0, //开始角度  
            90,//扫过的角度  
            false,//是否使用中心  
            paint);  
                                                                                                                                  
}

使用下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
protected void onDraw(Canvas canvas) {  
                                                                                                                                  
    //绘制弧线区域  
                                                                                                                                  
    RectF rect = newRectF(0, 0, 100, 100);  
                                                                                                                                  
    canvas.drawArc(rect,//弧线所使用的矩形区域大小  
            0, //开始角度  
            90,//扫过的角度  
            true,//是否使用中心  
            paint);  
                                                                                                                                  
}


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

1
2
3
4
5
protected void onDraw(Canvas canvas) {  
                                                                                                                                  
    canvas.drawColor(Color.BLUE);  
                                                                                                                                  
}

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

1
2
3
4
5
6
7
@Override  
protected void onDraw(Canvas canvas) {  
                                                                                                                                  
    //画一条线  
    canvas.drawLine(10, 10, 100, 100, paint);  
                                                                                                                                  
}


Canvas.drawOval:

1
2
3
4
5
6
7
8
9
@Override  
protected void onDraw(Canvas canvas) {  
                                                                                                                                  
    //定义一个矩形区域  
    RectF oval = newRectF(0,0,200,300);  
    //矩形区域内切椭圆  
    canvas.drawOval(oval, paint);  
                                                                                                                                  
}


canvas.drawPosText:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Override  
protected void onDraw(Canvas canvas) {  
                                                                                                                                  
    //按照既定点 绘制文本内容  
    canvas.drawPosText("Android777",newfloat[]{  
            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);  
                                                                                                                                  
}


canvas.drawRect:

1
2
3
4
5
6
7
8
9
10
@Override  
    protected void onDraw(Canvas canvas) {  
                                                                                                                                  
        RectF rect = newRectF(50, 50, 200, 200);  
                                                                                                                                  
        canvas.drawRect(rect, paint);  
                                                                                                                                  
    }  
                                                                                                                                  
}


canvas.drawRoundRect:

1
2
3
4
5
6
7
8
9
10
11
@Override  
protected void onDraw(Canvas canvas) {  
                                                                                                                                  
    RectF rect = newRectF(50, 50, 200, 200);  
                                                                                                                                  
    canvas.drawRoundRect(rect,  
                        30,//x轴的半径  
                        30,//y轴的半径  
                        paint);  
                                                                                                                                  
}


canvas.drawPath:

1
2
3
4
5
6
7
8
9
10
11
12
@Override  
protected void onDraw(Canvas canvas) {  
                                                                                                                                  
    Path path = newPath(); //定义一条路径  
    path.moveTo(10, 10); //移动到 坐标10,10  
    path.lineTo(50, 60);  
    path.lineTo(200,80);  
    path.lineTo(10, 10);  
                                                                                                                                  
    canvas.drawPath(path, paint);  
                                                                                                                                  
}

canvas.drawTextOnPath:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Override  
        protected void onDraw(Canvas canvas) {  
                                                                                                                                  
            Path path = newPath(); //定义一条路径  
            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);  
                                                                                                                                  
        }


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


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
@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 = newPath();  
    path.addArc(newRectF(0,0,150,150), -180, 180);  
    Paint citePaint = newPaint(paint);  
    citePaint.setTextSize(14);  
    citePaint.setStrokeWidth(1);  
    canvas.drawTextOnPath("http://www.android777.com", path, 28, 0, citePaint);  
    canvas.restore();  
                                                                                                                                  
    Paint tmpPaint = newPaint(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);  
                                                                                                                                  
}



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



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
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(newCustomView1(this));  
                                                                                                                                  
    }  
                                                                                                                                  
    /**  
     * 使用内部类 自定义一个简单的View  
     * @author Administrator  
     *  
     */
    class CustomView1 extends View{  
                                                                                                                                  
        Paint paint;  
        private ArrayList<PointF> graphics = newArrayList<PointF>();  
        PointF point;  
                                                                                                                                  
        public CustomView1(Context context) {  
            super(context);  
            paint = newPaint(); //设置一个笔刷大小是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(newPointF(event.getX(),event.getY()));  
                                                                                                                                  
            invalidate();//重新绘制区域  
                                                                                                                                  
            returntrue;  
        }  
                                                                                                                                  
        //在这里我们将测试canvas提供的绘制图形方法  
        @Override  
        protected void onDraw(Canvas canvas) {  
            for(PointF point : graphics) {  
                canvas.drawPoint(point.x, point.y, paint);  
            }  
//          super.onDraw(canvas);  
                                                                                                                                  
        }  
    }  
                                                                                                                                  
}

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


canvas的应用


canva还可以制作很多自定义控件,比如google日历的monthview就是用canvas绘制出来的,github上有很多使用canva的项目,所有的图表库都是用canvas绘制的。



以上转自  http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2012/1212/703.html


另外。canvas的drawText方法字体是单行画出的,需要居中显示,或者换行输出需要分别用到矩形的baseline 和 StaticLayout。

StaticLayout的构造函数有三个:

public StaticLayout(CharSequence source,                    TextPaint paint,                    int width,                    Layout.Alignment align,                    float spacingmult,                    float spacingadd,                    boolean includepad)                   public StaticLayout(CharSequence source,                    int bufstart,                    int bufend,                    TextPaint paint,                    int outerwidth,                    Layout.Alignment align,                    float spacingmult,                    float spacingadd,                    boolean includepad)                   public StaticLayout(CharSequence source,                    int bufstart,                    int bufend,                    TextPaint paint,                    int outerwidth,                    Layout.Alignment align,                    float spacingmult,                    float spacingadd,                    boolean includepad,                    TextUtils.TruncateAt ellipsize,                    int ellipsizedWidth)

android StaticLayout参数解释

StaticLayout(CharSequence source, int bufstart, int bufend,
           TextPaint paint, int outerwidth,
           Alignment align,
           float spacingmult, float spacingadd,
           boolean includepad,
           TextUtils.TruncateAt ellipsize, int ellipsizedWidth)


1.需要分行的字符串

2.需要分行的字符串从第几的位置开始

3.需要分行的字符串到哪里结束

4.画笔对象

5.layout的宽度,字符串超出宽度时自动换行。

6.layout的对其方式,有ALIGN_CENTER, ALIGN_NORMAL, ALIGN_OPPOSITE 三种。

7.相对行间距,相对字体大小,1.5f表示行间距为1.5倍的字体高度。

8.在基础行距上添加多少

实际行间距等于这两者的和。

9.参数未知

10.从什么位置开始省略

11.超过多少开始省略

需要指出的是这个layout是默认画在Canvas的(0,0)点的,如果需要调整位置只能在draw之前移Canvas的起始坐标
canvas.translate(x,y);



public void addClass(int day,int when,int span,String className,String classroom,Canvas canvas){int top,left,bottom,right,baseline,cutRectHeight,cutButtomHeight;RectF targetRect;Paint paint = new Paint();paint.setAntiAlias(true);paint.setDither(true);FontMetricsInt fontMetrics;left = (2*day - 1 )*width / 15;right = (2*day + 1 )*width / 15;top = (when - 1)*height / 13;bottom = (when + span - 1)*height / 13;cutButtomHeight = bottom - height / 13 / 15;cutRectHeight = bottom - height / 13 / 3;if(className.length() > 24){className = className.substring(0, 23); //+ "...";}if(day == dayOfWeek){paint.setColor(Color.parseColor("#F4F4F4"));}else{paint.setColor(Color.parseColor("#FFFFFF"));}canvas.drawRect(left + 2, top + 5, right, cutRectHeight, paint);//canvas.translate(left + 3, top + 5);TextPaint tp = new TextPaint();tp.setAntiAlias(true);tp.setDither(true);tp.setColor(Color.parseColor("#666666"));tp.setStyle(Style.FILL);tp.setTextSize(aTextSize);StaticLayout slayout = new StaticLayout(className, tp, right-left - 3, Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);int h = (cutRectHeight-slayout.getHeight()-top) / 2;
<span style="white-space:pre"></span>//移动canvas,否则canvas默认在(0,0)。canvas.translate(left + 3, top + 5 + h);slayout.draw(canvas);
<span style="white-space:pre"></span>//绘制完毕,需要返回原点,避免出错。(其实不回也可以)canvas.translate(-left - 3, -top - 5 - h);RectF surround = new RectF(left + 3, cutRectHeight , right - 3, cutButtomHeight);    paint.setColor(Color.parseColor("#20c2a9"));    canvas.drawRoundRect(surround, 15, 15, paint);targetRect = new RectF(left + 6, cutRectHeight + 3, right - 6, cutButtomHeight - 3);if(day == dayOfWeek){paint.setColor(Color.parseColor("#20c2a9"));    canvas.drawRoundRect(targetRect, 15, 15, paint);    paint.setColor(Color.parseColor("#ffffff"));}else{paint.setColor(Color.parseColor("#ffffff"));    canvas.drawRoundRect(targetRect, 15, 15, paint);    paint.setColor(Color.parseColor("#20c2a9"));}paint.setTextSize(bTextSize);fontMetrics = paint.getFontMetricsInt();
<span style="white-space:pre"></span>//baseline的计算方法。baseline = (int) (targetRect.top + (targetRect.bottom - targetRect.top - fontMetrics.bottom + fontMetrics.top) / 2 -fontMetrics.top);  paint.setTextAlign(Paint.Align.CENTER);      canvas.drawText(classroom , targetRect.centerX(), baseline, paint); }



JAVA中,Math.sin 和 cos 里的值需要为弧度(eg:Math.PI/2)

  1. //画贝塞尔曲线  
  2.         canvas.drawText("画贝塞尔曲线:"10250, p);  
  3.         p.setStyle(Paint.Style.STROKE);  
  4.         p.setColor(Color.GREEN);  
  5.         Path path=new Path();  
  6.         path2.moveTo(100100);//设置Path的起点  
  7.         path2.quadTo(150150170400); //设置贝塞尔曲线的控制点坐标和终点坐标  
  8.         canvas.drawPath(path, p);//画出贝塞尔曲线 

quadTo,前两个参数是控制点的x,y,后两个参数是终点x,y。

private Path getPath(int arc){Path path = new Path();path.moveTo(width/2, height/2);path.quadTo(edgeSize/2*(float)Math.sin(25*Math.PI/18 - arc*Math.PI/180)+width/2,edgeSize/2*(float)Math.cos(25*Math.PI/18 - arc*Math.PI/180)+height/2,edgeSize/2*(float)Math.sin(23*Math.PI/18 - arc*Math.PI/180)+width/2, edgeSize/2*(float)Math.cos(23*Math.PI/18 - arc*Math.PI/180)+height/2);path.arcTo(rectf, -140 + arc, 100);path.quadTo(edgeSize/2*(float)Math.sin(11*Math.PI/18 - arc*Math.PI/180)+width/2,edgeSize/2*(float)Math.cos(11*Math.PI/18 - arc*Math.PI/180)+height/2,width/2, height/2);return path;}


面讲到的贝塞尔曲线到底是什么呢?下面一一讲解。想要用好贝塞尔曲线就得先理解它的表达式,为了形象描述,我从网上盗了些动图。

首先看1阶贝塞尔曲线的表达式:

                             

随着t的变化,它实际是一条P0到P1的直线段:

                                

Android中Path的quadTo是3点的2阶贝塞尔曲线,那么2阶的表达式是这样的:

    

看起来很复杂,我把它拆分开来看:

        

然后再合并成这样:

      

看到什么了吧?如果看不出来再替换成这样:

     

      

     

B0和B1分别是P0到P1和P1到P2的1阶贝塞尔曲线。而2阶贝塞尔曲线B就是B0到B1的1阶贝塞尔曲线。显然,它的动态图表示出来就不难理解了:

                                          

红色点的运动轨迹就是B的轨迹,这就是2阶贝塞尔曲线了。当P1位于P0和P2的垂直平分线上时,B就是开口向上或向下的抛物线了。而在WaveView中就是用的开口向上和向下的抛物线模拟水波。在Android里用Path的方法,首先path.moveTo(P0),然后path.quadTo(P1, P2),canvas.drawPath(path, paint)曲线就出来了,如果想要绘制多个贝塞尔曲线就不断的quadTo吧。


转自   http://blog.csdn.net/vrix/article/details/39206975    很详细。


surfaceView每次恢复成未画过的状态除了最原始的方法canvas.drawRect画成纯色的方法。

还有

Paint mPaint = new Paint();
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
canvas.drawPaint(mPaint);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));



另外surfaceview还会遇到的问题是闪烁。

0 0
原创粉丝点击