安卓自定义View进阶-Canvas之绘制图形

参考文章:
GcsSloop

一.Canvas简介

Canvas我们可以称之为画布,能够在上面绘制各种东西,是安卓平台2D图形绘制的基础,非常强大。

一般来说,比较基础的东西有两大特点:

1.可操作性强:由于这些是构成上层的基础,所以可操作性必然十分强大。
2.比较难用:各种方法太过基础,想要完美的将这些操作组合起来有一定难度。

一.Canvas常用操作

这里写图片描述

一.Canvas详解

1、绘制颜色

这里写图片描述

这里写图片描述

2、绘制点

初始化画笔:

这里写图片描述

可以绘制一个点,也可以绘制一组点,如下:

这里写图片描述

这里写图片描述

3、绘制直线

绘制直线需要两个点,初始点和结束点,同样绘制直线也可以绘制一条或者绘制一组:

这里写图片描述

这里写图片描述

4、绘制矩形

确定确定一个矩形最少需要四个数据,就是对角线的两个点的坐标值,这里一般采用左上角和右下角的两个点的坐标。

关于绘制矩形,Canvas提供了三种重载方法,第一种就是提供四个数值(矩形左上角和右下角两个点的坐标)来确定一个矩形进行绘制。 其余两种是先将矩形封装为Rect或RectF(实际上仍然是用两个坐标点来确定的矩形),然后传递给Canvas绘制,如下:
这里写图片描述

这里写图片描述

看到这里,相信很多观众会产生一个疑问,为什么会有Rect和RectF两种?两者有什么区别吗?

答案当然是存在区别的,两者最大的区别就是精度不同,Rect是int(整形)的,而RectF是float(单精度浮点型)的。除了精度不同,两种提供的方法也稍微存在差别,在这里我们暂时无需关注,想了解更多参见官方文档 Rect 和 RectF

5、绘制圆角矩形

绘制圆角矩形也提供了两种重载方式,如下:

这里写图片描述
上面两种方法绘制效果也是一样的,但鉴于第二种方法在API21的时候才添加上,所以我们一般使用的都是第一种。

这里写图片描述

下面简单解析一下圆角矩形的几个必要的参数的意思。
很明显可以看出,第二种方法前四个参数和第一种方法的RectF作用是一样的,都是为了确定一个矩形,最后一个参数Paint是画笔,无需多说,与矩形相比,圆角矩形多出来了两个参数rx 和 ry,这两个参数是干什么的呢?

稍微分析一下,既然是圆角矩形,他的角肯定是圆弧(圆形的一部分),我们一般用什么确定一个圆形呢?

答案是圆心 和 半径,其中圆心用于确定位置,而半径用于确定大小。

由于矩形位置已经确定,所以其边角位置也是确定的,那么确定位置的参数就可以省略,只需要用半径就能描述一个圆弧了。

但是,半径只需要一个参数,但这里怎么会有两个呢?

好吧,让你发现了,这里圆角矩形的角实际上不是一个正圆的圆弧,而是椭圆的圆弧,这里的两个参数实际上是椭圆的两个半径,他们看起来个如下图:

5、绘制椭圆

这里写图片描述

这里写图片描述

绘制椭圆实际上就是绘制一个矩形的内切图形,原理如下:( 如果你传递进来的是一个长宽相等的矩形(即正方形),那么绘制出来的实际上就是一个圆。)
这里写图片描述

6、绘制圆:

绘制圆形有四个参数,前两个是圆心坐标,第三个是半径,最后一个是画笔。

这里写图片描述

这里写图片描述

6、绘制圆弧:

先看一下它需要的几个参数:

// 第一种
drawArc(@NonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter, @NonNull Paint paint){}

// 第二种
drawArc(float left, float top, float right, float bottom, float startAngle,
            float sweepAngle, boolean useCenter, @NonNull Paint paint) {}

从上面可以看出,相比于绘制椭圆,绘制圆弧还多了三个参数:

startAngle // 开始角度
sweepAngle // 扫过角度
useCenter // 是否使用中心

通过字面意思我们基本能猜测出来前两个参数(startAngle, sweepAngel)的作用,就是确定角度的起始位置和扫过角度, 不过第三个参数是干嘛的?试一下就知道了,上代码:

这里写图片描述

上述代码实际上是绘制了一个起始角度为0度,扫过90度的圆弧,两者的区别就是是否使用了中心点,结果如下:

这里写图片描述

改变画笔mPaint.setStyle(Paint.Style.STROKE);

这里写图片描述
可以发现使用了中心点之后绘制出来类似于一个扇形,而不使用中心点则是圆弧起始点和结束点之间的连线加上圆弧围成的图形。这样中心点这个参数的作用就很明显了

二.小示例

简要介绍画布的操作:

这里写图片描述

制作一个饼状图

这里写图片描述

简单分析

其实根据我们上面的知识已经能自己制作一个饼状图了。不过制作东西最重要的不是制作结果,而是制作思路。 相信我贴上代码大家一看就立刻明白了,非常简单的东西。不过嘛,咱们还是想了解一下制作思路:

先分析饼状图的构成,非常明显,饼状图就是一个又一个的扇形构成的,每个扇形都有不同的颜色,对应的有名字,数据和百分比。

经以上信息可以得出饼状图的最基本数据应包括:名字 数据值 百分比 对应的角度 颜色。

用户关心的数据 : 名字 数据值 百分比

需要程序计算的数据: 百分比 对应的角度

其中颜色这一项可以用户指定也可以用程序指定(我们这里采用程序指定)。

封装数据:

这里写图片描述

以上省略了get set方法

自定义View:

先按照自定义View流程梳理一遍(确定各个步骤应该做的事情):

这里写图片描述

public class MyCustemView extends View {

// 颜色表 (注意: 此处定义颜色使用的是ARGB,带Alpha通道的)
private int[] mColors = {0xFFCCFF00, 0xFF6495ED, 0xFFE32636, 0xFF800000, 0xFF808000, 0xFFFF8C69, 0xFF808080,
        0xFFE6B800, 0xFF7CFC00};
//画笔
public Paint mPaint;
//数据集合
public List<PieBean> dataList;
// 饼状图初始绘制角度
private float mStartAngle = 0;
// 宽高
private int mWidth, mHeight;

public MyCustemView(Context context) {
    this(context, null);
}

public MyCustemView(Context context, @Nullable AttributeSet attrs) {
    this(context, attrs, 0);
}

public MyCustemView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init();
}

private void init() {
    mPaint = new Paint();
    mPaint.setAntiAlias(true);//设置抗锯齿
    mPaint.setStyle(Paint.Style.FILL);  //设置画笔模式为填充
}

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    mWidth = w;
    mHeight = h;
}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    if (dataList != null && dataList.size() != 0) {
        //当前起始角度
        float currentStartAngle = mStartAngle;
        // 将画布坐标原点移动到中心位置
        //canvas.translate(mWidth / 2, mHeight / 2);
        // 饼状图半径
        float r = (float) (Math.min(mWidth, mHeight) / 2 * 0.8);
        //饼状图绘制区域
        RectF rect = new RectF(-r, -r, r, r);
        for (int i = 0; i < dataList.size(); i++) {
            PieBean pie = dataList.get(i);
            mPaint.setColor(pie.getColor());
            canvas.drawArc(rect, currentStartAngle, pie.getAngle(), true, mPaint);
            currentStartAngle += pie.getAngle();
        }
    }
}

//设置起始角度
public void setStartAngle(int startAngle) {
    this.mStartAngle = startAngle;
    //刷新
    invalidate();
}

public void setData(List<PieBean> dataList) {
    this.dataList = dataList;
    initData(dataList);
    //刷新
    invalidate();
}

private void initData(List<PieBean> data) {
    if (data != null && data.size() != 0) {

        float sumValue = 0;
        for (int i = 0; i < data.size(); i++) {
            PieBean pie = data.get(i);
            //计算数值和
            sumValue += pie.getValue();
            //设置颜色
            int j = i % mColors.length;
            pie.setColor(mColors[j]);
        }

        float sumAngle = 0;
        for (int i = 0; i < data.size(); i++) {
            PieBean pie = data.get(i);
            // 百分比
            float percentage = pie.getValue() / sumValue;
            // 对应的角度
            float angle = percentage * 360;
            // 记录百分比
            pie.setPercentage(percentage);
            // 记录角度大小
            pie.setAngle(angle);
            sumAngle += angle;
        }
    }
}

}

这里写图片描述

如果不进行画布的平移操作的话,会出现:

这里写图片描述

在更改了数据需要重绘界面时要调用invalidate()这个函数重新绘制。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值