android自定义View——基础绘制Canvas、Path笔记

自定义绘制的4个级别

  1. Canvas 的 drawXXX() 系列方法及 Paint 最常见的使用
  2. Paint 的完全攻略
  3. Canvas 对绘制的辅助——范围裁切和几何变换
  4. 使用不同的绘制方法来控制绘制顺序
小知识点
1.android中坐标系原点: View 左上角的那个点
2.X与Y轴: X:右正左负   Y:下正上负(不是数学学的上正下负)

onDraw()

这里不再给基础方法一一代码编写展示了。文章最后提供一个五角星、饼状图、雷达图的代码示例。
有兴趣的可以一个一个尝试下。

Paint基础

提供一些风格信息:例如:如颜色、线条粗细、阴影等。

  • Paint.setStyle(Style style) 设置绘制模式(控制实心圆还是空心圆)
 /**
     *  Paint.Style.STROKE  设置绘制模式为画线模式
     *  Paint.Style.FILL 是填充模式,
     *  Paint.Style.ILL_AND_STROKE 是两种模式一并使用:既画线又填充
     * */
  • Paint.setColor(int color) 设置颜色
  • Paint.setStrokeWidth(float width) 设置线条宽度
  • Paint.setTextSize(float textSize) 设置文字大小
  • Paint.setAntiAlias(boolean aa) 设置抗锯齿
//直接在构造函数里加入Paint.ANTI_ALIAS_FLAG也是抗锯齿
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); 
Canvas
drawXXX()系列方法
  • .drawColor 颜色填充
 drawColor(Color.BLACK) //会把整个区域染成纯黑色,覆盖掉原有内容
 drawColor(Color.parse("#88880000")) //会在原有的绘制效果上加一层半透明的红色遮罩。
还有drawRGB:使用方式不同,效果是一样的
drawRGB(100, 200, 100); 
drawARGB(100, 100, 200, 100); 
  • drawLine / drawLines 画线

     1. drawLine(float startX, float startY, float stopX, float stopY, Paint paint)
     2. drawLines(float[] pts, int offset, int count,Paint paint) 
        drawLines(float[] pts, Paint paint) //画线(批量)
    
     // 绘制个五角星
       float[] points1 = {
                0,0,300,0,//0 ,0是起点坐标 300,0 目标点坐标  // 画第二条线时以上一次的结束点作为下一次的起点位置,以此类推
                300,0,60,200,
                60,200,150,-150,
                150,-150,250,200 ,
                250,200 ,0,0
        };
        canvas.drawLines(points1, paint);
  • drawCircle 画圆
/**
*centerX、centerY 坐标轴
*radius: 半径
*Paint : 画笔
*         可以提供基本的风格信息。可看上面Panit基础
*/
drawCircle(float centerX, float  centerY,float radius, Paint paint)
//代码示例:
canvas.drawCircle(300, 300, 100, paint);// 画圆
  • drawRect 画矩形
 
1. drawRect(float left, float top, float right, float bottom, Paint paint) 画矩形
 两个重载方法 :drawRect(RectF rect, Paint paint)
              drawRect(Rect rect, Paint paint) ,让你可以直接填写 RectF 或 Rect 对象来绘制矩形。
//  绘圆角矩形       
canvas.drawRoundRect(100,300, 300, 100,30,10, paint)
  • drawPoint 画点
/* Paint.Cap.ROUND  // 圆头
 * Paint.Cap.SQUARE  // 方头
 * Paint.Cap.BUTT // 平头
 * */
  drawPoint(float x, float y, Paint paint) 画点
 
 /**
 * offset:开始绘制之前要跳过的值的数量
 * count: 要处理的值的数目,在跳过它们的偏移量之后。从一个点
 * 使用两个值,绘制的“点”的数量实际上是(计数>> 1)。
 */
  drawPoints(float[] pts, int offset, int count,Paint paint) 
  drawPoints(float[] pts, Paint paint) 画点(批量)
    //代码示例:
    float[] points = {0, 0, 50, 50, 50, 100, 100, 50, 100, 100};
    // 绘制四个点:(50, 50) (50, 100) (100, 50) (100, 100)
    canvas.drawPoints(points, 2 /* 跳过两个数,即前两个 0 */, 8 /* 一共绘制 8 个数(4 个点)*/, paint);
  • drawOval 画椭圆
drawOval(float left, float top, float right, float bottom, Paint paint) //画椭圆
重载方法:drawOval(RectF rect, Paint paint) //让你可以直接填写 RectF 来绘制椭圆。
  • drawArc 画弧 / 画扇形
/*drawArc() 是使用一个椭圆来描述弧形的。left , top , right , bottom 描述的是
         *       这个弧形所在的椭圆;startAngle 是弧形的起始角度(x 轴的正向,即正右的方
         *       向,是 0 度的位置;顺时针为正角度,逆时针为负角度),sweepAngle 是弧形划
         *       过的角度;useCenter 表示是否连接到圆心,如果不连接到圆心,就是弧形,如果
         *       连接到圆心,就是扇形
         * */
 //再次提醒:sweepAngle : 开始绘制时起始角度    sweepAngle 是弧形划过的角度         
drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle,boolean useCenter, Paint paint)
  • drawText
drawText(String text,float  x, float y, Paint paint) 绘制文字
  • drawBitmap
/**
* 绘制 Bitmap 对象,也就是把这个 Bitmap 中的像素内容贴过来。其中 left 和
* top 是要把 bitmap 绘制到的位置坐标。
*/
drawBitmap(Bitmap bitmap, float  left, float  top, Paint paint)

drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint) 
drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) 
drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint)
  • drawPath 一般用于绘制复杂的图形
drawPath( Path path, Paint paint)
Path
小知识点:
  • Direction 路径的方向
Path.Direction.CW  顺时针 
Path.Direction.CCW  逆时针
  • Path.FillTpe
Path.setFillTpe(Path.FillTpe ft) 设置填充
  • Path使用:
// 最后使用时,调用drawPath  把path传入
canvas.drawPath( Path path, Paint paint)
Path.addXXX()系列 : 直接描述路径
  • addCircle 添加圆
addCircle(float x, float  y, float  radius, Direction dir) 添加圆
  • addOval 添加椭圆
addOval(float  left,float  top,float  right,float  bottom, Direction dir) 
addOval(RectF oval, Direction dir) 
  • addRect 矩形
addRect(float left, float top, float right, float bottom, Path.Direction dir)
addRect(RectF rect, Path.Direction dir)
  • addRoundRect 添加圆角矩形
addRoundRect(RectF rect, float[] radii, Path.Direction dir)
addRoundRect(RectF rect, float rx, float ry, Path.Direction dir)
  • addPath 添加另一个 Path
addPath(Path path) 添加另一个 Path

和上面drawxxx用法都差不多。

xxxTo() ;
  • lineTo (绝对坐标) 画线
  • rLineTo (相对坐标)即当前位置,最后一次调用画 Path 的方法的终点位置。初始值为原点 (0, 0)
  • moveTo 设置下一次绘制时的起点位置
lineTo :绝对坐标
rLineTo  :相对坐标
//代码示例
paint.setStyle(Style.STROKE); 
path.lineTo(200, 200); // 由当前位置 (0, 0) 向 (200, 200) 画一条直线 
path.rLineTo(200, 0); // 由当前位置 (200,200) 向正右方 200 像素的位置画

path.lineTo(100,200);
path.moveTo(100,200); // 下次绘制时,起点就是(100,200)的位置

moveTo与rLineTo的区别:rLineTo 是以上一次的终点为原点位置,初始值为原点(00;
                      moveTo 设置下一次绘制时的起点位置

如下图:
path.lineTo(200, 200);
path.rLineTo(200, 0);
在这里插入图片描述

  • quadTo / rQuadTo 画二次贝塞尔曲线 (还在研究中,看官自行略过 )
quadTo(float  x1, float  y1, float  x2,float  y2) 
rQuadTo(float  dx1, float  dy1, float  dx2,float  dy2) 
  • cubicTo / rCubicTo 画三次贝塞尔曲线 (同上 )
cubicTo(float   x1, float   y1, float   x2, float   y2, float   x3, float y3) /
rCubicTo(float   x1, float   y1, float   x2,float   y2, float   x3,float   y3)
  • arcTo() 画弧形
arcTo(float left,float top,float right,float bottom,float startAngle,float sweepAngle,boolean forceMoveTo) 
arcTo(RectF oval, float startAngle, float  sweepAngle, boolean forceMoveTo) 
arcTo(RectF oval, float  startAngle, float  sweepAngle) 

//代码示例
  Path path=new Path();
  path.lineTo(100, 100);
  path.arcTo(100, 100, 400, 400, -180, 180, true);
  canvas.drawPath(path ,paint);

arcTo 和 drawArc() 比起来,只用来画弧形而不画扇形,所以不需要useCenter这个参数,多了一个forceMoveTo
forceMoveTo = true:
在这里插入图片描述
forceMoveTo = false:
在这里插入图片描述
看到区别没:
true:绘制是要 「抬一下笔移动过去」
false :「直接拖着笔过去」
二者的影响就是:是否留下移动的痕迹

  • addArc() 画弧形
addArc(float left,float  top,float  right,float  bottom, float  startAngle,float  sweepAngle)
addArc(RectF oval, float   startAngle, float  sweepAngle)

addArc()相对于 arcTo  只是一个直接使用了 forceMoveTo = true 的简化版
  • close() 封闭当前子图形
当前位置向当前子图形的起点绘制一条直线。
Path的一些判断方法
  • isEmpty
//path中是否包含内容
boolean b=path.isEmpty()
  • isRect
//path是否是一个矩形,如果是一个矩形的话,会将矩形的信息存放进参数rect中
boolean b=path.isRect()
  • set
  将新的path赋值到现有path
  path.set(path1);     // 相当于 path=path1 ;
  • offset
进行平移
offset(float dx,float dy)
offset(float dx,float dy,Path dst)

dst:用来存储平移后的path路径内容,无论dst之前是否存在内容,存储的都是当前的path路径内容
path.offset(300,0,dst);   

五角星

      WindowManager wm1 = mCtx.getWindowManager();
        int width1 = wm1.getDefaultDisplay().getWidth();
        int height1 = wm1.getDefaultDisplay().getHeight();
   
       paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setStrokeWidth(5);
        paint.setColor(Color.GRAY);
     

        canvas.translate(width1/3, height1/3);// 移动原点位置
        //五角星
        Path path = new Path();
        path.lineTo(300, 0);
        path.lineTo(60, 200);
        path.lineTo(150, -150);
        path.lineTo(250, 200);
        path.setFillType(Path.FillType.EVEN_ODD);
        path.close();
       //圆
        Path pathCircle = new Path();
        pathCircle.addCircle(350, 450, 50, Path.Direction.CCW);
        pathCircle.addCircle(350, 450, 100, Path.Direction.CCW);
        pathCircle.setFillType(Path.FillType.EVEN_ODD);
        path.addPath(pathCircle);
        canvas.drawPath(path, paint);

在这里插入图片描述

饼状图

    
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        centerX = w / 2;
        centerY = h / 2;
        radius = Math.min(w, h) / 2 * 0.8f;
        Log.e("三角函数: ", Math.PI * 2 + "   " + centerX + "  radius=" + radius);

        //一旦size发生改变,重新绘制
        postInvalidate();
        super.onSizeChanged(w, h, oldw, oldh);
    }


       // 起始绘制度数
        private float STARTANGLE = -180;
        int radius = 200;// 初始化半径
        int offset = 30;// 离原点的偏移距离( x  y 轴的垂直距离)
        
   @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
   private void drawPieChart(Canvas canvas) {
            // 初始化Paint
        Paint paint_A = new Paint();
        paint_A.setColor(Color.RED);

        Paint paint_B = new Paint();
        paint_B.setColor(Color.GRAY);

        Paint paint_C = new Paint();
        paint_C.setColor(Color.GREEN);

        Paint paint_D = new Paint();
        paint_D.setColor(Color.LTGRAY);

        //1.模拟数据
        List<PieChartBean> mList = new ArrayList<>();
        mList.add(new PieChartBean(200, "A"));
        mList.add(new PieChartBean(100, "B"));
        mList.add(new PieChartBean(200, "C"));
        mList.add(new PieChartBean(300, "D"));

        // 2.计算所有类型的总数值
        float allNum = 0;
        for (int i = 0; i < mList.size(); i++) {
            allNum = allNum + mList.get(i).getNum();
        }
        // 3.求出每个Type占总的比例值,以此来分配所占的饼状图(圆形)的度数值
     
        String type = "";
        for (int i = 0; i < mList.size(); i++) {
            if (i != 0) {
            // 得出当前Type的值  
            //第一条数据无需获取,为后面当前type值时获取绘制的起始角度值使用
                type = mList.get(i - 1).getType();
            }
            switch (mList.get(i).getType()) {
                case "A":
                    canvas.drawArc(centerX - offset - radius, centerY - offset - radius, centerX + radius, centerY + radius, getStartAngle(mList, type, allNum), getSweepAngle(mList.get(i).getNum(), allNum), true, paint_A);
                    break;
                case "B":
                    canvas.drawArc(centerX - radius, centerY - radius, centerX + radius, centerY + radius, getStartAngle(mList, type, allNum), getSweepAngle(mList.get(i).getNum(), allNum), true, paint_B);
                    break;
                case "C":
                    canvas.drawArc(centerX - radius, centerY - radius, centerX + radius, centerY + radius, getStartAngle(mList, type, allNum), getSweepAngle(mList.get(i).getNum(), allNum), true, paint_C);
                    break;
                case "D":
                    canvas.drawArc(centerX - radius, centerY - radius, centerX + radius, centerY + radius, getStartAngle(mList, type, allNum), getSweepAngle(mList.get(i).getNum(), allNum), true, paint_D);
                    break;
            }
        }
    }

    /**
     * 画扇形扫过的度数
     */
    private float getSweepAngle(float sweepAngle, float allNum) {
        return (sweepAngle / allNum) * 360;
    }


    /**
     * 获取每个Type开始绘制的起始度数
     */
    private float getStartAngle(List<PieChartBean> mList, String type, float allNum) {
        float startAngle = 0;// 记录当前Type的偏移度数
        for (int i = 0; i < mList.size(); i++) {
            startAngle = startAngle + (mList.get(i).getNum() / allNum) * 360;
            if (mList.get(i).getType().equals(type)) {
            //匹配到当前type度数值时,结束循环
                break;
            }
        }
        return STARTANGLE + startAngle;
    }

在这里插入图片描述

雷达图

   private int count = 8;
    private float angle = (float) (Math.PI * 2 / count);//初始化正多边形 原点到每两点之间的夹角度数
    private int centerX;
    private int centerY;
    private Activity mCtx;
    private Paint paint;
    private Paint linePaint;
    private Paint drawRegionPaint;
    private float radius;// 总半径 如下图原点到A点的距离
    private float maxValue = 100;
  @Override
  protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        centerX = w / 2;
        centerY = h / 2;
        radius = Math.min(w, h) / 2 * 0.8f;
        Log.e("三角函数: ", Math.PI * 2 + "   " + centerX + "  radius=" + radius);

        //一旦size发生改变,重新绘制
        postInvalidate();
        super.onSizeChanged(w, h, oldw, oldh);
    }

 private void initPaint() {
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setStrokeWidth(5);
        paint.setColor(Color.GRAY);
        paint.setStyle(Paint.Style.STROKE);

        linePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        linePaint.setStrokeWidth(5);
        linePaint.setColor(Color.GREEN);
        linePaint.setStyle(Paint.Style.STROKE);

        drawRegionPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        drawRegionPaint.setStyle(Paint.Style.FILL);
        drawRegionPaint.setStrokeWidth(3);
        drawRegionPaint.setColor(Color.RED);

        regionList = new ArrayList<>();
        regionList.add(100.0);
        regionList.add(60.0);
        regionList.add(20.0);
        regionList.add(30.0);
        regionList.add(70.0);
        regionList.add(20.0);
        regionList.add(40.0);
        regionList.add(55.0);
    }

/**
* 绘制最底层多边形
*/

    private void drawSpiderWeb(Canvas canvas) {
        Path pathPoint = new Path();
        pathPoint.addCircle(centerX, centerY, 4, Path.Direction.CCW); // 绘制中心点的 圆点作为原点
        Path path = new Path();
        //蜘蛛网 按层级划分节点 从原点开始半径依次增大(增大相同距离),有几个节点就有几个半径
        //  相邻两个节点的距离 =radius(蜘蛛网的总半径) / 分成了几个层级
        // 如下图 原点到最外层一共7个正多边形 一共有7个节点层级 
        float r = radius / (count - 1);//得到平均每次增长的大小(相邻两个节点的距离)
//        path.setFillType(Path.FillType.EVEN_ODD);
        for (int i = 0; i < count; i++) {
            path.reset();
            float nextRadius = r * i;// 原点到当前节点的距离(当前层级的正多边形与原点的距离)
            for (int j = 0; j < count; j++) {
                if (j == 0) {
                    path.moveTo(centerX + nextRadius, centerY);// 设置绘制的起点
                } else {
                    //对于直角三角形sin(x)是对边比斜边,cos(x)是底边比斜边,tan(x)是对边比底边
                    //因此可以推导出:底边(x坐标)=斜边(半径)*cos(夹角角度)
                    //               对边(y坐标)=斜边(半径)*sin(夹角角度)
                    float x = (float) (centerX + nextRadius * Math.cos(angle * j));
                    float y = (float) (centerY + nextRadius * Math.sin(angle * j));
//                    Log.e("三角函数: ", "x==" + x + "  y==" + y + "  centerX==" + centerX + "  centerY==" + centerY);
                    path.lineTo(x, y);
                }
            }
            path.close();
            path.addPath(pathPoint);
            canvas.drawPath(path, paint);
            path.reset();
            pathPoint.reset();
        }
    }


在这里插入图片描述

  /**
 * 从原点连接从里道外多边形顶点 形成蜘蛛网
*/
    private void drawLines(Canvas canvas) {
        Path path = new Path();
        for (int i = 0; i < count; i++) {
            path.reset();
            path.moveTo(centerX, centerY);
            float x = (float) (centerX + radius * Math.cos(angle * i));
            float y = (float) (centerY + radius * Math.sin(angle * i));
            path.lineTo(x, y);
            canvas.drawPath(path, linePaint);
        }
        path.reset();
    }

在这里插入图片描述

 /**
     * 绘制蜘蛛网上面的图
     */
    private void drawRegion(Canvas canvas) {
        Path regionPath = new Path();
        for (int i = 0; i < count; i++) {
            Double aDouble = regionList.get(i);
            double percent = aDouble / maxValue;// 算出数据和最大半径的比值  //maxValue代表最大半径代表的值
            double percentValue = percent * radius;
            float x = (float) (centerX + percentValue * Math.cos(angle * i));
            float y = (float) (centerY + percentValue * Math.sin(angle * i));
            if (i == 0) { // 迁移绘制起点
                regionPath.moveTo(x, y);
            } else {
                regionPath.lineTo(x, y);
            }
            canvas.drawCircle(x, y, 10, drawRegionPaint);
        }
        regionPath.close();
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setStyle(Paint.Style.FILL);
        paint.setColor(Color.RED);
        paint.setAlpha(155);
        canvas.drawPath(regionPath, paint);
    }

在这里插入图片描述


/**
* 画文字
*/
    private void drawToText(Canvas canvas) {
        Paint textPaint = new TextPaint();
        textPaint.setColor(Color.BLUE);
        textPaint.setTextSize(30);

        Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
        float fontHeight = fontMetrics.descent - fontMetrics.ascent;
        //绘制文字时不让文字和雷达图形交叉,加大绘制半径
        float textRadius = radius + fontHeight;
        double pi = Math.PI;
        List<String> titleList = new ArrayList<>();
        titleList.add("A");
        titleList.add("B");
        titleList.add("C");
        titleList.add("D");
        titleList.add("E");
        titleList.add("F");
        titleList.add("G");
        titleList.add("GF");
        titleList.add("GGGG");
        for (int i = 0; i < count; i++) {
            float x = (float) (centerX + textRadius * Math.cos(angle * i));
            float y = (float) (centerY + textRadius * Math.sin(angle * i));
            canvas.drawText(titleList.get(i), x, y, textPaint);
        }
    }

在这里插入图片描述

自定义 基础绘制,笔记就做了这么多!各位看官如果又发现什么不对的,或者不完善的,请指出!谢谢

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值