Android开发 - Canvas类与Paint画笔与Draw绘制方法详解与使用

Canvas类是什么

  • AndroidCanvas类常用于自定义View等操作中,Canvas则如同一张画布可以在上面绘制内容,然后这张画布也可以叠加其他的图层或者平移旋转等操作。Canvas对象的获取方式有两种:一种我们通过重写onDraw方法View中重写onDraw(Canvas canvas)Canvas对象会被当做参数传递过来,我们操作这个Canvas,效果会直接反应在View中(我们也可以在ViewGroup中对Canvas做操作,然后将这个Canvas通过diapatchDraw(Canvas canvas)的方式传递给子View,子View将会按照Canvas的规则去绘制);另一种就是new的方式去创建。Canvas类持有“draw”调用。要绘制内容需要4个基本组件:一个用于持有像素的Bitmap,一个用于承载绘制调用的Canvas写入位图),一个绘制图元(例如矩形(Rect)路径(Path)文本(text)位图(Bitmap))和一个画笔(Paint)描述图纸的颜色和样式

绘制需要什么

paint(画笔)

  • 画笔提供了最基础的绘画功能,颜色、画笔大小、样式等。我们只需要简单的设计几个参数就可以使用
    • setColor();:设置画笔的颜色
    • setAntiAlias();:设置画笔的锯齿效果
    • setARGB();:设置画笔的ARGB颜色值
    • setAlpha();:设置画笔的Alpha值
    • setTextSize();:设置字体的尺寸
    • setStyle();:设置画笔的风格(空心或实心)
    • setStrokeWidth();:设置空心边框的宽度(线宽)

canvas(画布)

canvas 常用的API

绘制画笔:drawPaint

  • drawPaint(Paint paint)

    • 参数解析
      • paint:这个画笔绘制出来就是当前view的颜色,填充满整个画布
    代码实例
    private void drawPaint(Canvas canvas) {
        canvas.drawPaint(paint);
    }
    

绘画圆弧:drawArc

  • canvas:drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)
    • 参数解析
      • RectF:画布的大小
      • startAngle: 开始角度,这个值只能决定图形的开始角度,不能改变图的开角角度。
      • sweepAngle:扫描的角度,正常就是图形的角度
      • useCenter:是否以中心旋转,如果是,画中的扇形中心在圆中心,否则就是一个普通的扇形
      • paint:画笔

拼色扇形实例
  • 像一些统计图其实是通过绘制圆弧完成的,但是绘制的时候需要注意图层问题Canvas是一层一层绘制的,如果处理多图形拼图,最大层应该在最下方

    代码实例
    RectF rectF = new RectF(0, 0, getMeasuredWidth(), getMeasuredHeight());
    
    
    if (paint == null) {
        paint = new Paint();
    }
    
    paint.setColor(Color.RED);
    paint.setStrokeWidth(2);
    paint.setStyle(Paint.Style.FILL);
    
    canvas.drawArc(rectF, 0, 360, true, paint);
    canvas.save();
    paint.setColor(Color.YELLOW);
    canvas.drawArc(rectF, 0, 150, true, paint);
    paint.setColor(Color.BLUE);
    canvas.drawArc(rectF, 0, 90, true, paint);
    
    paint.setColor(Color.GRAY);
    canvas.drawArc(rectF, 0, 30, true, paint);
    
    • 这里面有四种扇形,红色、黄色、绿色、灰色

      • 灰色开角:30

      • 绿色开角:90

      • 黄色开角:150

      • 红色开角:360(圆形)

    • 视觉上

      • 灰色:30°
        • 绿色:60°
        • 黄色:60°
        • 红色:210°
    技巧
    • 先算出各个角度,再计算出各个扇形的开角。注意先绘制大图形,再绘制小的,否则360°先绘制会导致图形的遮挡问题

绘制路径:drawPath

  • canvas.drawPath(Path path, Paint paint)

    • 参数解析
      • path:路径值,又因为path可以完成各种路径的构建

      • paint:画笔

  • 正常path绘制很多东西,如果一个画面由三个点组成

    代码实例
    private void drawPath(Canvas canvas) {
        Path path = new Path();
        path.moveTo(100, 100);
        path.lineTo(300, 300);
        path.lineTo(200,300);
        path.close();
    
        canvas.drawPath(path, paint);
    }
    

组合绘图:drawPicture

  • canvas.drawPicture(Picture picture):绘制复杂的图形或者需要耗时较长,可以通过该方法
    • 参数解析
      • picture:图片或者海报

绘制色块:drawARGB

  • canvas.drawARGB(int a, int r, int g, int b):这个色块类似一个填充色,以ARGB颜色来完成。值可以参考色值:0-255,2^8-1

    • 参数解析
      • a:透明度
      • r:红色
      • g:绿色
      • b:蓝色

绘制bitmap资源:drawBitmap

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

    • 参数解析
      • bitmapbitmap资源
      • left:从布局左边的坐标点(类似ViewX轴)开始绘制
      • top:从布局的顶部的坐标点(类似ViewY轴)开始绘制
      • paint:画笔

    代码实例
    private void drawBitmap(Canvas canvas)
    {
        canvas.drawBitmap(getBitmap(),0,0,paint);
        canvas.save();
    }
    

图片扭曲特效:drawBitmapMesh

  • canvas.drawBitmapMesh(Bitmap bitmap, int meshWidth, int meshHeight, float[] verts, int vertOffset, colors, int colorOffset, Paint paint)

    • 参数解析

      • bitmap:指定需要扭曲的源位图。
      • meshwidth:该参数控制在横向上把该源位图划分成多少格
      • meshheight:该参数控制在纵向上把该源位图划分成多少格
      • verts:该参数是一个长度为(meshWidth + 1)*(meshHeight+1)* 2的数组,它记录了扭曲后的位图各"顶点"位置。虽然它是个一维数组,实际上它记录的数据是形如(x0, y0),(x1, y1),(x2, y2)......(xN, yN)格式的数据,这些数组元素控制对bitmap位图的扭曲效果
      • vertOffset:vert的偏移,也就是控制verts数组中从第几个数组元素开始才对bitmap进行扭曲(忽略veroffset之前数据的扭曲效果)
    • drawBitmapMesh方法对源位图扭曲时最关键的参数是meshWidthmeshHeightverts这三个参数对扭曲的控制,如下图:

    • 从上图可以看出,当程序希望调用drawBitmapMesh方法对位图进行扭曲时,关键是计算verts数组值(该数组的值记录了扭曲后的位图上各"顶点"的坐标)

    代码实例
    • 下面的实例是通过drawBitmapMesh方法来控制图片的扭曲

      1. 自定义MeshView组件

        package com.example.matrixdemo.view;
         
        import com.example.matrixdemo.R;
         
        import android.content.Context;
        import android.graphics.Bitmap;
        import android.graphics.Canvas;
        import android.graphics.Color;
        import android.graphics.drawable.BitmapDrawable;
        import android.util.AttributeSet;
        import android.view.MotionEvent;
        import android.view.View;
         
        /***
         * 通过Canvas的drawBitmapMesh方法控制图片的扭曲效果
         * @author Administrator
         *
         */
        public class MyMeshView extends View {
        	/** 源图片 */
        	private Bitmap bitmap;
        	/** 定义常量,常量指定该图片横向上被分为20格 */
        	private final int MESH_WIDTH = 20;
        	/** 定义常量,常量指定该图片纵向上被分为20格 */
        	private final int MESH_HEIGHT = 20;
        	/** 记录该图片上包含的441个顶点 */
        	private final int COUNT = (MESH_WIDTH + 1) * (MESH_HEIGHT + 1);
        	/** 定义一个数组,保存Bitmap上的21*21个点的坐标 */
        	private float[] verts = new float[COUNT * 2];
        	/** 定义一个数组,保存Bitmap上的21*21个点经过扭曲后的坐标 */
        	// 对图片进行扭曲的关键就是修改该数组里元素的值
        	private float[] orig = new float[COUNT * 2];
         
        	public MyMeshView(Context context) {
        		this(context, null);
        	}
         
        	public MyMeshView(Context context, AttributeSet attrs) {
        		this(context, attrs, 0);
        	}
         
        	public MyMeshView(Context context, AttributeSet attrs, int defStyleAttr) {
        		super(context, attrs, defStyleAttr);
        		setFocusable(true);
        		// 加载Bitmap资源
        		bitmap = ((BitmapDrawable) getResources().getDrawable(
        				R.drawable.bg02)).getBitmap();
        		// 获取图片的宽度,高度
        		float bitmapWidth = bitmap.getWidth();
        		float bitmapHeight = bitmap.getHeight();
        		int index = 0;
        		for (int i = 0; i <= MESH_HEIGHT; i++) {
        			float fy = bitmapHeight * i / MESH_HEIGHT;
        			for (int j = 0; j < MESH_WIDTH; j++) {
        				float fx = bitmapWidth * i / MESH_WIDTH;
        				// 初始化orig,verts数组,初始化后,orig,verts两个数组均匀的保存了21*21个点的坐标
        				orig[index * 2 + 0] = verts[index * 2 + 0] = fx;	//偶数
        				orig[index * 2 + 1] = verts[index * 2 + 1] = fy;	//奇数
        				index += 1;
        			}
        		}
        		// 设置背景色
        		setBackgroundColor(Color.WHITE);
        	}
         
        	@Override
        	protected void onDraw(Canvas canvas) {
        		super.onDraw(canvas);
        		canvas.drawBitmapMesh(bitmap, MESH_WIDTH, MESH_HEIGHT, verts, 0,
        				null, 0, null);
        	}
         
        	@Override
        	public boolean onTouchEvent(MotionEvent event) {
        		//调用warp方法根据触摸屏事件的坐标来扭曲verts数组
        		warp(event.getX(), event.getY());
        		return true;
        	}
         
        	/***
        	 * 根据触摸事件的位置计算verts数组里个元素的值
        	 * 
        	 * @param x
        	 *            X坐标
        	 * @param y
        	 *            Y坐标
        	 */
        	private void warp(float cx, float cy) {
        		for (int i = 0; i < MESH_WIDTH * 2; i += 2) {
        			float dx = cx - orig[i + 0];
        			float dy = cy - orig[i + 1];
        			float dd = dx * dx + dy * dy;
        			//计算每个坐标点与当前坐标点(cx,cy)之间的距离
        			float d = (float) Math.sqrt(dd);
        			//计算扭曲度,距离当前点(cx,cy)越远,扭曲度越小
        			float pull = 80000 / (dd * d);
        			//对verts数组(保存bitmap上21*21进过扭曲后的坐标)重新赋值
        			if(pull >= 1){
        				verts[i + 0] = cx;
        				verts[i + 1] = cy;
        			}else{
        				//控制各个顶点向触摸事件发生点进行偏移
        				verts[i + 0] = orig[i + 0] * dx * pull;
        				verts[i + 1] = orig[i + 1] * dy * pull;
        			}
        		}
        		//通知view组件重绘
        		invalidate();
        	}
        	
        }
        
      2. activity调用以及布局

        • Java调用

          package com.example.matrixdemo;
           
          import android.app.Activity;
          import android.content.Context;
          import android.graphics.Bitmap;
          import android.graphics.Canvas;
          import android.graphics.Color;
          import android.graphics.drawable.BitmapDrawable;
          import android.os.Bundle;
          import android.util.AttributeSet;
          import android.view.MotionEvent;
          import android.view.View;
           
          public class MeshActivity extends Activity {
          	@Override
          	protected void onCreate(Bundle savedInstanceState) {
          		super.onCreate(savedInstanceState);
          		setContentView(R.layout.activity_mesh);
          	}
          }
          
        • xml布局

          <?xml version="1.0" encoding="utf-8"?>
          <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical" >.
              
              <com.example.matrixdemo.view.MyMeshView
                  android:layout_width="wrap_content"
                  android:layout_height="wrap_content"/>
              
          </LinearLayout>
          
    • 上面的程序中,warp方法会根据触摸点的位置动态修改verts数组里所有数组元素的,这样就控制了drawBitmapMesh方法的扭曲效果

画圆:drawCircle

  • canvas.drawCircle(float cx, float cy, float radius, Paint paint)

    • 参数解析
      • cx:圆心x轴坐标
      • cy:圆心y轴坐标
      • radius:半径
      • paint:画笔
    代码实例
    canvas.drawCircle(100, 100, 100, paint);
    

划线:drawLine

  • canvas.drawLine(float startX, float startY, float stopX, float stopY, Paint paint)

    • 参数解析:

      • startX:起点x坐标

      • startY:起点y坐标

      • stopX:终点x坐标

      • stopY:终点y坐标

      • paint:画笔

    代码实例
    private void drawLine(Canvas canvas) {
        paint.setStrokeWidth(100);
        canvas.drawLine(0, 500, 500, 500, paint);
    }
    

    • 由于线段是两点确定,所以(startX,startY)是起点(stopX,stopY)是终点
    • 如果想这条线很宽。通过设置画笔的宽度 paint.setStrokeWidth(100);

画椭圆:drawOval

  • canvas.drawOval(RectF oval, Paint paint)

    • 参数解析
      • oval:设置一个矩形区域,扫描测量
      • paint:画笔
  • canvas.drawOval(float left, float top, float right, float bottom, Paint paint)

    • 参数解析

      • left:左边坐标;在绘制中常表示为起点的Y轴坐标

      • top:上边左边;在绘制中常表示为起点的X轴坐标

      • right:右边坐标;在绘制中常表示为终点的Y轴坐标

      • bottom:下边坐标;在绘制中常表示为终点的Y轴坐标

      • paint:画笔

  • 第二种方法的前4个参数实际上就是RectF类的参数,同样是创建一个矩形扫描测量区域进行放置一个椭圆

代码实例
private void drawOval(Canvas canvas) {
    int width=getMeasuredWidth();
    int height=getMeasuredHeight()/3;

    canvas.drawOval(0,0,width,height,paint);
}

绘制补丁:drawPatch

  • canvas.drawPatch(NinePatch patch, Rect dst, Paint paint)

    • 参数详解
      • patch:点九资源(.9.png),
      • dst:绘制区域
      • paint:画笔
  • 此方法资料很少,估计是不太常用,简单了解一下即可:[点九资源](android draw9 patch介绍_android studio canvas drawpatch-CSDN博客)

    代码实例
    Bitmap bitmap = getBitmap();
    NinePatch patch = new NinePatch(bitmap,bitmap.getNinePatchChunk());
    canvas.drawPatch(patch, dst, paint)
    

绘制点:drawPoint

  • canvas.drawPoint(float x, float y, Paint paint)

    • 参数解析
      • x:点的x轴坐标位置
      • y:点的y轴坐标位置
      • paint:画笔
  • 点的大小取决于paint的画笔大小

    代码实例
    private void drawPoint(Canvas canvas) {
        paint.setStrokeWidth(20);
        canvas.drawPoint(100, 100, paint);
    }
    

绘制文字从点pos出发:drawPosText

  • canvas.drawPosText(String text, @Size(multiple = 2) float[] pos, Paint paint)

    • 参数解析
      • text:文本
      • pospos坐标(x,y)
      • paint:画笔
    代码实例
        private void drawPosText(Canvas canvas) {
     
            float[] pos = new float[]{100, 100};
            canvas.drawPosText("Hellow", pos, paint);
     
        }
    

绘制矩形区域:drawRect

  • canvas.drawRect(RectF rect, Paint paint)

    • 参数解析
      • RectF:绘制区域
      • paint:画笔
    代码实例
    private void drawRect(Canvas canvas) {
        RectF rectF=new RectF(0,0,500,500);
        canvas.drawRect(rectF,paint);
    
    }
    

绘制色域:drawRGB、drawColor

  • canvas.drawRGB(int r, int g, int b)

    • 参数解析
      • rrgb颜色值的r值
      • grgb颜色值的g值
      • b:rgb颜色值的b值
  • canvas.drawColor(int color)

    • 参数解析
      • color
  • 以下只举例其一

    代码实例
    public void drawRGB(Canvas canvas) {
        canvas.drawRGB(200, 200, 200);
    }
    

在矩形内绘制圆角:drawRoundRect

  • canvas.drawRoundRect(RectF rect, float rx, float ry, Paint paint)

    • 参数解析

      • RectF :绘制区域

      • rx:圆角角度在矩形内的坐标x轴

      • ry:圆角角度在矩形内的坐标y轴

      • paint:画笔

    注意事项
    • 如果rx和ry为0,那么绘制出来的view就是矩形,如果rx,ry在矩形正中心,那么绘制出来的圆角就是圆形
    • 如果x和y都小于矩形的宽和高,那么显示就是矩形四个角被裁剪
    • 如果x和y都大于矩形的宽和高,那么显示就是
    • 如果x和y其中一个小于宽和高,那么显示可能是一个不规则图形
    代码实例
    public void drawRoundRect(Canvas canvas)
    {
        RectF rectF=new RectF(0,0,200,200);
        canvas.drawRoundRect(rectF,10,10,paint);
    }
    

绘制字符:drawText

  • canvas.drawText(String text, float x, float y, Paint paint)

    • 参数解析
      • text:文本
      • x:位置的x轴坐标
      • y:位置的y轴坐标
      • paint:画笔
    注意事项
    • 绘制的时候paint style(画笔风格)必须设置成paint.setStyle(Paint.Style.FILL);否则会出现一团
    • 文字的样式和大小,都是通过paint(画笔)来设置,关于paint可以自行查看
    代码实例
    private void drawText(Canvas canvas)
    {
        paint.setTextSize(100);
        canvas.drawText("你好",100,100,paint);
    }
    

绘制文本按路径显示:drawTextOnPath

  • canvas.drawTextOnPath(String text, Path path, float hOffset, float vOffset, Paint paint)

    • 参数解析

      • text :文本

      • path:路径,最终文本文字会按路径显示

      • hOffset :横向偏移,沿路径添加到文本起始位置的距离

      • vOffset :纵向偏移,定位文本的路径上方(-)下方(+)的距离

      • paint:画笔

    代码实例
    private void drawTextOnPath(Canvas canvas) {
        paint.setTextSize(100);
        Path path=new Path();
        path.moveTo(0,500);
        path.lineTo(500,500);
        path.lineTo(500,0);
        path.close();
        canvas.drawTextOnPath("Hello word just do it",path,100,100,paint);
    }
    

绘制运行中的文本:drawTextRun

  • canvas.drawTextRun(CharSequence text, int start, int end, int contextStart, int contextEnd, float x, float y, boolean isRtl, Paint paint)

    • 参数解析

      • text:文本

      • start:内容开始索引

      • end:内容结束的索引

      • contextStart:塑造语句的起点索引,默认从0开始

      • contextEnd:塑造语句的终结索引默认text的长度

      • x:位置x轴坐标

      • y:位置y轴坐标

      • isRtl:运行是否为镜像

      • paint:画笔

    代码实例
    private void drawTextRun(Canvas canvas)
    {
        paint.setTextSize(100);
        CharSequence text="Hello word just do it";
        canvas.drawTextRun(text,0,text.length(),0,text.length(),100,100,true,paint);
    
        canvas.drawTextRun(text,0,text.length(),0,text.length(),100,600,false,paint);
    
    }
    

绘制多边形:drawVertices

  • canvas.drawVertices(VertexMode mode, int vertexCount, float[] verts, int vertOffset, float[] texs, int texOffset, @Nullable int[] colors, int colorOffset, short[] indices, int indexOffset, int indexCount, Paint paint)
    • 参数解析
      • mode:如何解释顶点数组
      • vertexCount顶点数组中的值数如果非空,则为相应的texs和颜色数组)。每个逻辑顶点都是两个值(x,y)vertexCount必须是2的倍数
      • verts网格的顶点数组
      • vertOffset绘制前要跳过的顶点中的值数
      • texs:可能为空。如果不为空,则指定要采样到当前着色器中的坐标(例如位图平铺或渐变
      • texOffset:绘制前要跳过的texs中的值数
      • colors:可为空。如果不为空,则为每个顶点指定一种颜色,以便在三角形上进行插值。
      • colorOffset绘制前要跳过的颜色值的数目
      • indices:如果不为null,则引用到顶点(texs,colors)数组的索引数组
      • indexCount如果不为空,索引数组中的条目数
      • paint:画笔
    • 由于资料很少,做一下简单的了解,在Android开发程序中应该是不常用的
posted @ 2024-07-25 22:05  阿俊学JAVA  阅读(579)  评论(0编辑  收藏  举报