图形绘制 Canvas Paint Path 详解


图形绘制简介
      
      
      
      
        Android中使用图形处理引擎,2D部分是android SDK内部本身提供,3D部分是用Open GL ES 1.0。 大部分2D使用的api都在android. graphics和android.graphics.drawable包中。他们提供了图形处理相关的Canvas、ColorFilter、Point、RetcF等类,还有一些动画相关的AnimationDrawable、BitmapDrawable、TransitionDrawable等。
      以图形处理来讲,咱们最经常使用到的就是在一个View上画一些图片、形状或者自定义的文本内容,这些都是使用Canvas来实现的。
       另外,咱们 能够获取View中的Canvas对象,在绘制一些内容后调用 View.invalidate 方法让View从新刷新,而后再绘制一个新的内容,以此屡次以后,就实现了2D动画的效果。

画图须要四大基本要素:
一、一个用来保存像素的Bitmap
二、一个或多个画笔Paint
三、须要绘制的内容
四、一个Canvas画布,用来在Bitmap上使用Paint绘制内容

Canvas对象的获取方式
        
        
        
        
Canvas对象的获取方式有三种:
一、经过重写View.onDraw方法获取Canvas对象
这种方式根据环境还分为两种:一种是普通View的Canvas,还有一种是SurfaceView的Canvas。
两种的主要是区别就是,能够在SurfaceView中定义一个专门的线程来完成画图工做,应用程序不须要等待View的刷图,提升性能。
前面一种适合处理量比较小,帧率比较低的动画方面的绘图,好比说象棋游戏之类的;然后一种主要用在游戏或高品质动画方面的绘图。
二、直接建立一个Canvas对象:
Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);  
Canvas c = new Canvas(bitmap);
上面代码建立了一个尺寸是100*100的Bitmap,使用它做为Canvas操做的对象,这时候的Canvas就是使用建立的方式。
当你使用建立的Canvas在bitmap上执行绘制方法后,你还能够将绘制的结果提交给另一个Canvas,这样就能够达到两个Canvas协做完成的效果,简化逻辑。
            可是android SDK建议使用View.onDraw参数里提供的Canvas就好,不必本身建立一个新的Canvas对象。
三、调用SurfaceHolder.lockCanvas()也会返回一个Canvas对象,能够在 surfaceView 或 TextureView中使用。

Canvas位置装换、保存、恢复
        
        
        
        
         Android还提供了一些对Canvas位置转换的方法:rorate、scale、translate、skew(扭曲)等,并且它容许你经过 getMatrix 得到它的转换矩阵对象,并能够直接操做它。这些操做就像是虽然你的笔仍是在原来的地方画,可是画纸(坐标原点)旋转或者移动了,因此你画的东西的方位就产生了变化。
      为了方便 使用 这些转换操做,Canvas 还提供了保存和回滚属性的方法 save和restore save用来保存Canvas的状态,save以后,能够调用Canvas的平移、放缩、旋转、错切、裁剪等操做。 restore 用来恢复Canvas以前保存的状态,防止save后对Canvas执行的操做对后续的绘制有影响。注意:save和restore要配对使用,若是restore调用次数比save多,会引起Error。

小细节
上图,最初始的状况是栈里只有 0 1 2 3 4 , 而后执行 save 方法两次,则 5 和 6 出如今栈中

Canvas可绘制的图形种类
        
        
        
        
一、绘制背景(填充)
      drawARGB(int a, int r, int g, int b)
      drawColor(int color)
      drawRGB(int r, int g, int b)
      drawColor(int color, PorterDuff.Mode mode)
二、 绘制 几何图形
     canvas.drawArc (扇形,弓形, 弧线区域
     canvas.drawCircle(圆)
     canvas.drawOval(圆和椭圆)
     canvas.drawLine(线)
     canvas.drawPoint(点)
     canvas.drawRect(矩形)
     canvas.drawRoundRect(圆角矩形)
     canvas.drawVertices(顶点)
     cnavas.drawPath(路径,可用来绘制任意图形)
三、 绘制 图片
       canvas.drawBitmap (位图)
       canvas.drawPicture (图片)
四、 绘制 文本
       canvas.drawText
     

Paint画笔工具相关设置
        
        
        
        
Paint即画笔,在绘图过程当中起到了极其重要的做用,画笔主要保存了颜色、 样式等绘制信息,指定了如何绘制文本和图形。 画笔对象有不少设置方法,  大致上能够分为两类:
一、图 形相关的设置方法
  • setARGB(int a,int r,int g,int b); 设置绘制的颜色,a表明透明度,r,g,b表明颜色值。 
  • setAlpha(int a); 设置绘制图形的透明度。 
  • setColor(int color); 设置绘制的颜色,使用颜色值来表示,该颜色值包括透明度和RGB颜色。 
  • setAntiAlias(boolean aa); 设置是否使用抗锯齿功能,会消耗较大资源,绘制图形速度会变慢。 
  • setDither(boolean dither); 设定是否使用图像抖动处理,会使绘制出来的图片颜色更加平滑和饱满,图像更加清晰 
  • setFilterBitmap(boolean filter); 若是该项设置为true,则图像在动画进行中会滤掉对Bitmap图像的优化操做,加快显示速度,本设置项依赖于dither和xfermode的设置 
  • setMaskFilter(MaskFilter maskfilter); 设置MaskFilter,能够用不一样的MaskFilter实现滤镜的效果,如滤化,立体等        
  • setColorFilter(ColorFilter colorfilter); 设置颜色过滤器,能够在绘制颜色时实现不用颜色的变换效果 
  • setPathEffect(PathEffect effect); 设置绘制路径的效果,如点画线等 
  • setShader(Shader shader); 设置图像效果,使用Shader能够绘制出各类渐变效果 
  • setShadowLayer(float radius ,float dx,float dy,int color); 在图形下面设置阴影层,产生阴影效果,radius为阴影的角度,dx和dy为阴影在x轴和y轴上的距离,color为阴影的颜色 
  • setStyle(Paint.Style style); 设置画笔的样式。FILL:实心,默认;FILL_OR_STROKE:填充并设置描边;STROKE:描边,空心
  • setStrokeCap(Paint.Cap cap); 定义线段断点形状。当画笔样式为STROKE或FILL_OR_STROKE时,设置咱们的画笔在【离开】画板时候留下的最后一点图形,如圆形样式 Cap.ROUND(有延长),或方形样式Cap.BUTT(默认,没有延长)与Cap.SQUARE(有延长)
  • setSrokeJoin(Paint.Join join); 设置绘制时各图形的结合方式(图形节点的样式),如平滑效果等
  • setStrokeWidth(float width); 当画笔样式为STROKE或FILL_OR_STROKE时,设置笔刷的粗细 
  • setXfermode(Xfermode xfermode); 设置图形重叠时的处理方式,如合并,取交集或并集
2.文本相关的设置方法
  • setFakeBoldText(boolean fakeBoldText); 模拟实现粗体文字,设置在小字体上效果会比较差 
  • setSubpixelText(boolean subpixelText); 设置该项为true,将有助于文本在LCD屏幕上的显示效果 
  • setTextAlign(Paint.Align align); 设置绘制文字的对齐方向 
  • setTextScaleX(float scaleX); 设置绘制文字x轴的缩放比例,能够实现文字拉伸的效果 
  • setTextSize(float textSize); 设置绘制文字的字号大小 
  • setTextSkewX(float skewX); 设置斜体文字,skewX为x轴方向的倾斜弧度 
  • setTypeface(Typeface typeface); 设置字体风格,包括粗体,斜体以及衬线体,非衬线体等 
  • setUnderlineText(boolean underlineText); 设置带有下划线的文字效果
  • setStrikeThruText(boolean strikeThruText); 设置带有删除线的效果

代码-绘制各类图形
     
     
     
     
public class TestPaintView extends View {
    private Context context;
    private Paint paint;
    private RectF rect;//Rect是使用int类型做为数值,RectF是使用float类型做为数值
    private Path path;//主要用于绘制复杂的图形轮廓,好比折线,圆弧以及各类复杂图案
    private Bitmap bitmap;
    private int lefttoprightbottom;

    public TestPaintView(Context context) {
        super(context);
        this.context = context;
        paint = new Paint();
        rect = new RectF(0, 0, 0, 0);//左X、上Y、右X、下Y相应的距离,即左上角、右下角的坐标,系统不会检查数值的有效性
        paint.setStrokeJoin(Paint.Join.ROUND);//设置绘制时图形的结合方式(图形节点的样式)
        paint.setStrokeCap(Paint.Cap.ROUND);//设置画笔在【离开】画板时留下的最后一点的样式
        paint.setAntiAlias(true);
        paint.setDither(true);
        paint.setStrokeWidth(dp2px(0.5f));//设置画笔粗细,单位为像素
        bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.icon);
    }

    @SuppressLint("DrawAllocation")
    @Override
    protected void onDraw(Canvas canvas) {
        //背景颜色
        canvas.drawColor(0x33005500);
        //矩形
        initRect(10, 50, 10, 50);
        paint.setStyle(Paint.Style.STROKE);//描边(空心),默认是FILL(实心、填充)
        canvas.drawRect(rectpaint);
        //绘制弧线区域(扇形或弓形)
        paint.setStyle(Paint.Style.FILL);
        paint.setColor(0xff00ff00);
        canvas.drawArc(rect, 0, 90, falsepaint);//当为false时是一个不通过【圆心】的弓形,当为true时是一个通过圆心的扇形
        paint.setColor(0xffff0000);
        canvas.drawArc(rect, 90, 150, truepaint); //圆弧所在矩形,起始角度90,旋转角度150,顺时针为正
        //矩形内切圆(或椭圆)
        paint.setStyle(Paint.Style.STROKE);
        initRect(10, 30, 10, 50);
        canvas.drawRect(rectpaint);
        paint.setARGB(255, 0, 0, 255);
        canvas.drawOval(rectpaint);
        paint.setStyle(Paint.Style.FILL);
        canvas.drawArc(rect, 0, 90, falsepaint); //椭圆的扇形
        paint.setARGB(128, 255, 0, 255);
        canvas.drawArc(rect.leftrect.toprect.rightrect.bottom, 90f, 150f, truepaint);

        //圆角矩形
        paint.setStyle(Paint.Style.STROKE);
        initRect(10, 50, 10, 50);
        canvas.drawRoundRect(rect, 20, 20, paint);//矩形,两侧圆角弧度的大小,通常都设为相同
        //画直线
        canvas.drawLine(dp2px(120), dp2px(25), dp2px(120), dp2px(55), paint);// 画一条线(首尾两点的坐标)
        paint.setColor(Color.BLUE);
        float[] points = new float[] { dp2px(120), dp2px(15), dp2px(150), dp2px(15),//画多条线。每条线都须要两个坐标(每两个值组成一个坐标)
                dp2px(130), dp2px(25), dp2px(150), dp2px(55), dp2px(150), dp2px(25) };
        canvas.drawLines(points, paint);//最后两个值不够组成一条线,因此被废弃掉了。
        paint.setColor(Color.BLACK);
        canvas.drawLines(points, 2, 8, paint);//指定跳过前2个数据,取出8个数据绘制直线
        // 画圆  
        canvas.drawCircle(dp2px(190), dp2px(35), dp2px(25), paint);//圆心坐标,半径
        //画点
        paint.setStrokeWidth(dp2px(3));
        canvas.drawPoint(dp2px(190), dp2px(35), paint);//画一个点
        paint.setColor(Color.RED);
        canvas.drawPoints(points, paint);//画多个点,每两个值组成一个坐标
        //画图片,就是贴图  
        canvas.drawBitmap(bitmap, dp2px(220), dp2px(10), paint);//坐标指的是左上角的位置
        //canvas.drawCircle(320 + bitmap.getWidth() / 2, 200 + bitmap.getWidth() / 2, bitmap.getWidth() / 2, paint);
        //*************************************************************************************

        //画路径1,不规则封闭图形
        paint.setStrokeWidth(dp2px(0.5f));
        path = new Path();
        path.moveTo(dp2px(10), dp2px(70));//指定初始轮廓点,若没指定默认从(0,0)点开始
        path.lineTo(dp2px(10), dp2px(100));//从当前轮廓点绘制一条线段到指定轮廓点
        path.lineTo(dp2px(50), dp2px(100));
        path.lineTo(dp2px(50), dp2px(80));
        path.close(); // 回到初始点造成封闭的曲线
        canvas.drawPath(pathpaint);
        //画路径2,利用path也能够画各类图形,api使用上和canvas有些许区别
        rect = new RectF(dp2px(60), dp2px(70), dp2px(110), dp2px(120));
        path.addRect(rect, Direction.CW);//利用path画矩形。Diection.CW 顺时针方向,Diection.CCW 逆时针方向
        canvas.drawPath(pathpaint);
        paint.setColor(Color.BLUE);//注意,同一path绘制的图形的颜色必定是相同的
        Path path2 = new Path();//若是这里不从新new一个path,则前面用path绘制的图形的颜色也都会变
        path2.addRoundRect(rectnew float[] { 20, 60, 20, 60, 60, 60, 20, 20 }, Direction.CW);//从左上角顺时针开始,四个角的x轴y轴方向的弧度
        canvas.drawPath(path2, paint);
        //绘制文本
        canvas.drawLine(rect.leftrect.centerY(), rect.rightrect.centerY(), paint);
        paint.reset();//重置
        paint.setTextSize(dp2px(12));//单位是px,只在绘制文字时有效
        paint.setTextAlign(Align.CENTER);//绘制的文字以drawText时指定的 float x 水平居中,默认值是Align.LEFT
        canvas.drawText("1efg"rect.centerX(), rect.centerY(), paint);//注意 float y 表明的是 baseline 的值,也即e和f的下边界,而非g的下边界
        paint.setUnderlineText(true);//带下划线
        canvas.drawText("包青天efg", 0, "包青天efg".length(), rect.right + dp2px(30), rect.top + dp2px(12)paint);//(+textSize)可实现和矩形顶部对齐
        canvas.drawText(new char[] { 'J''!''。''.' }, 0, 4, rect.right + dp2px(30), rect.centerY(), paint);
        //在指定路径上绘制文本
        Path path3 = new Path();
        path3.moveTo(rect.rightrect.bottom);
        path3.lineTo(rect.right + dp2px(150), rect.centerY());
        canvas.drawPath(path3, paint);
        paint.setTextAlign(Align.LEFT);
        canvas.drawTextOnPath("123456789", path3, 30, -10, paint);//float hOffset,相对基准线的向右偏移值, float vOffset向下偏移值
    }

    /**
     * 从新设置矩形边界
     * @param leftAdd, 表明新矩形的左边界距离上一个矩形的右边界的距离
     * @param width,表明矩形的宽度
     * @param topAdd, 表明新矩形的上边界的值
     * @param hight,表明矩形的高度
     */
    private void initRect(int leftAdd, int width, int topValue, int hight) {
        left = right + leftAdd;
        right = left + width;
        top = topValue;
        bottom = top + hight;
        rect.left = dp2px(left);
        rect.right = dp2px(right);
        rect.top = dp2px(top);
        rect.bottom = dp2px(bottom);
    }
    private int dp2px(float dpValue) {
        float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }
}

代码-位置装换、保存、恢复
      
      
      
      
public class TestCanvasView extends View {
    private static final int SAVE_FLAGS = //Canvas.ALL_SAVE_FLAG //= 0x1F,还原全部,restore everything when restore() is called 
    Canvas.MATRIX_SAVE_FLAG//= 0x01,须要还原Matrix。restore the current matrix when restore() is called 
            | Canvas.CLIP_SAVE_FLAG //= 0x02,须要还原Clip。restore the current clip when restore() is called 
            | Canvas.HAS_ALPHA_LAYER_SAVE_FLAG //=0x04, 图层的 clip 标记。the layer needs to per-pixel alpha 
            | Canvas.FULL_COLOR_LAYER_SAVE_FLAG//= 0x08,图层的 color 标记。the layer needs to 8-bits per color component
            | Canvas.CLIP_TO_LAYER_SAVE_FLAG;// = 0x10,图层的 clip 标记,在saveLayer 和 saveLayerAlpha时Android强烈建议加上他
    private Paint mPaint;
    public TestCanvasView(Context context) {
        super(context);
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawColor(Color.WHITE);
        //画一个红色的圆
        mPaint.setColor(Color.RED);
        canvas.drawCircle(100, 100, 100, mPaint);
        canvas.save();//保存Canvas的状态,save以后,能够调用Canvas的平移、放缩、旋转、错切、裁剪等操做。save()默认保存的是matrix和clip
        //画一个绿色的拉长的圆(即椭圆)
        canvas.scale(0.5f, 1);//x、y轴的缩放比例
        mPaint.setColor(Color.GREEN);
        canvas.drawCircle(100, 100, 100, mPaint);
        //画一个黄色的矩形
        canvas.restore();//restore用来恢复Canvas以前保存的状态,防止save后对Canvas执行的指定操做对后续的绘制有影响
        canvas.translate(200, 0);//把画布平移。此操做对下面新建的那个图层依然是有效的
        mPaint.setColor(Color.YELLOW);
        canvas.drawRect(0, 0, 200, 200, mPaint);
        canvas.save(SAVE_FLAGS);//指定哪些须要还原。只有指定matrix或clip才有效,其他几个参数在saveLayer和saveLayerAlpha方法中才有效

        //画一个蓝色的扭曲的矩形
        canvas.skew(2f, 1);//图形变换惟一规则:将x坐标所有变为*2,将y坐标所有变为*1。至因而否平行啦之类的都不去限制。
        mPaint.setColor(Color.BLUE);
        canvas.drawRect(0, 0, 100, 100, mPaint);
        //画一个带透明度的蓝色的旋转的小矩形
        canvas.restore();//取消扭曲。
        canvas.translate(200, 0);
        canvas.rotate(30, 50, 50);//沿指定点为中心顺时针旋转指定角度。默认是以左上角为中心
        int count = canvas.saveLayerAlpha(0, 0, 200, 200, 0x88, SAVE_FLAGS);//在指定边界新建一个透明度为0x88的图层
        canvas.drawRect(0, 0, 100, 100, mPaint);//绘制时使用的颜色还是上次的蓝色,而非save以前的颜色。这个图层的透明度对此图形是有影响的
        //画一个绿色的小圆
        canvas.restore();
        canvas.restore();//若是restore调用的次数大于save的调用次数,会出错。
        canvas.restoreToCount(count);
        mPaint.setColor(Color.GREEN);
        canvas.drawCircle(50, 50, 50, mPaint);// 这个圆是在原来的图层上划的, 没有透明度
    }
}



相关文章
相关标签/搜索