自定义变形饼图

运行效果

关键效果描述:半径不同的扇形、扇形的弧度根据占比决定、有展示动画、有阴影、有指示线、有点击事件。

           

直接上代码:

不规则扇形饼图

package demo.hanli.hldemo.modules.custompie.widget;

import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.BlurMaskFilter;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

/**
 * 不规则扇形饼图
 */
public class IrregularPieView extends View {
    private int ANIM_DURATION = 1000;//动画时长,默认1秒
    private float maxRadiusScale = 1.2f;//扇形最大缩放比例
    private float minRadiusScale = 0.5f;//扇形最小缩放比例
    private float unitRadiusScale = 0.1f;//扇形递增缩放比例

    private int mHeight, mWidth;//宽高
    private Paint mPaint;//扇形的画笔
    private Paint mShadowPaint;//阴影的画笔
    private Paint mTextPaint;//画文字的画笔
    private int centerX, centerY;//中心坐标

    private double total;//数据的总和
    private double[] datas;//数据集
    private int[] dataCells;//数据扇形缩放基数集
    private String[] texts;//每个数据对应的文字集
    private float currentPercent;//当前展示角度的百分比
    private List<Region> regions = new ArrayList<>();//扇形区域
    private Region centerRegion;//中心圆区域
    private IrregularPieListener listener;//饼图监听

    //颜色 默认的颜色
    private int[] mColors = {
            Color.parseColor("#FFC65B"), Color.parseColor("#FD5998"),
            Color.parseColor("#8971FB"), Color.parseColor("#676974")
    };

    private int mTextSize;//文字大小

    private int radius = 1000;//半径

    public IrregularPieView(Context context) {
        super(context);
    }

    public IrregularPieView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    //初始化
    private void init() {
        mTextSize = 25;

        mPaint = new Paint();
        //当画笔样式为STROKE或FILL_OR_STROKE时,设置笔刷的图形样式, 如圆形样Cap.ROUND,或方形样式Cap.SQUARE
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        //设置是否使用抗锯齿功能,会消耗较大资源,绘制图形速度会变慢。
        mPaint.setAntiAlias(true);

        //阴影
        mShadowPaint = new Paint();
        mShadowPaint.setAntiAlias(true);

        mTextPaint = new Paint();
        //设置绘制文字的字号大小
        mTextPaint.setTextSize(mTextSize);
        //当画笔样式为STROKE或FILL_OR_STROKE时,设置笔刷的粗细度
        mTextPaint.setStrokeWidth(2);
        //设置是否使用抗锯齿功能,会消耗较大资源,绘制图形速度会变慢。
        mTextPaint.setAntiAlias(true);
        //设置绘制的颜色,使用颜色值来表示,该颜色值包括透明度和RGB颜色。
        mTextPaint.setColor(Color.BLACK);
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //获取宽高 不要设置wrap_content
        mHeight = MeasureSpec.getSize(heightMeasureSpec);
        mWidth = MeasureSpec.getSize(widthMeasureSpec);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //无数据
        if (datas == null || datas.length == 0) return;

        centerX = (getRight() - getLeft()) / 2;
        centerY = (getBottom() - getTop()) / 2;
        int min = mHeight > mWidth ? mWidth : mHeight;
        if (radius > min / 2) {
            radius = (int) ((min - getPaddingTop() - getPaddingBottom()) / 3.5);
        }

        //画阴影
        canvas.save();
        drawShadow(canvas);
        canvas.restore();

        //画扇形
        canvas.save();
        drawCircle(canvas);
        canvas.restore();

        //线与文字
        canvas.save();
        drawLineAndText(canvas);
        canvas.restore();

        //画中心圆
        canvas.save();
        drawCenterCircle(canvas);
        canvas.restore();

    }


    //画中心圆
    private void drawCenterCircle(Canvas canvas) {

        //设置画笔属性
        mPaint.setColor(Color.WHITE);
        mPaint.setStyle(Paint.Style.FILL);//画笔属性是实心圆
        canvas.drawCircle(centerX, centerY, radius * 0.3f, mPaint);

        if(currentPercent == 1){
            centerRegion = new Region();
            Path path = new Path();
            path.addCircle(centerX, centerY, radius * 0.3f, Path.Direction.CW);
            RectF r = new RectF();
            path.computeBounds(r, true);
            centerRegion.setPath(path, new Region((int)r.left, (int) r.top, (int) r.right,(int)r.bottom));
        }
    }

    //画线与文字
    private void drawLineAndText(Canvas canvas) {
        int start = 0;
        canvas.translate(centerX, centerY);//平移画布到中心
        mPaint.setStrokeWidth(4);
        for (int i = 0; i < datas.length; i++) {
            String textLabel;
            if(texts == null || texts.length <= i){
                textLabel = "";
            }else{
                textLabel = texts[i];
            }
            if (i == datas.length - 1) {
                drawLine(canvas, (int) (start*currentPercent), 360 - start*currentPercent, textLabel, mColors[i % mColors.length], i);
            } else {
                float angles = (float) ((datas[i] * 1.0f / total) * 360);
                drawLine(canvas, (int) (start*currentPercent), angles*currentPercent, textLabel, mColors[i % mColors.length], i);
                start += angles;
            }
        }
    }

    private void drawLine(Canvas canvas, int start, float angles, String text, int color, int position) {
        mPaint.setColor(color);
        mPaint.setStrokeWidth(2);
        mTextPaint.setColor(color);
        float stopX, stopY;
        stopX = (float) ((radius + 20) * Math.cos((2 * start + angles) / 2 * Math.PI / 180));
        stopY = (float) ((radius + 20) * Math.sin((2 * start + angles) / 2 * Math.PI / 180));

        double scale = (0.5 + (dataCells[position] * unitRadiusScale)) > maxRadiusScale ?
                maxRadiusScale : (0.5 + (dataCells[position] * unitRadiusScale));
        canvas.drawLine((float) ((radius * scale) * Math.cos((2 * start + angles) / 2 * Math.PI / 180)),
                (float) ((radius * scale) * Math.sin((2 * start + angles) / 2 * Math.PI / 180)),
                stopX, stopY, mPaint);

        //测量百分比大小
        String percentage = new BigDecimal((datas[position] / total) * 100)
                .setScale(0, BigDecimal.ROUND_HALF_UP) + "";
        percentage = percentage.substring(0, percentage.length() > 4 ? 4 : percentage.length()) + "%";
        //测量文字大小
        Rect rect = new Rect();
        String data = new BigDecimal(datas[position])
                .setScale(0, BigDecimal.ROUND_HALF_UP) + "";
        String textInfo = percentage + "(" + data + ")";
        mTextPaint.getTextBounds(textInfo, 0, textInfo.length(), rect);
        int w = rect.width();
        int h = rect.height();
        int offset = 10;//文字在横线的偏移量

        //画横线
        int dx;//判断横线是画在左边还是右边
        int endX;
        int lineW = (w + offset) > 110 ? (w + offset) : 110;//横线宽度
        if (stopX > 0) {
            endX = (int) (stopX + lineW);
        } else {
            endX = (int) (stopX - lineW);
        }
        //画横线
        canvas.drawLine(stopX, stopY,
                endX, stopY, mPaint
        );
        dx = (int) (endX - stopX);

        //画横线顶端的小圈
        mPaint.setStyle(Paint.Style.STROKE);
        canvas.drawCircle(dx > 0 ? endX + 4 : (stopX - lineW) - 4, stopY, 4, mPaint);
        mPaint.setStyle(Paint.Style.FILL);


        //画文字
        canvas.drawText(textInfo, 0, textInfo.length(), dx > 0 ? stopX + offset : stopX - w - offset, stopY - 10, mTextPaint);

    }

    //画扇形
    private void drawCircle(Canvas canvas) {
        RectF rect = null;
        int start = 0;
        regions.clear();
        for (int i = 0; i < datas.length; i++) {
            double scale = (0.5 + (dataCells[i] * unitRadiusScale)) > maxRadiusScale ?
                    maxRadiusScale : (0.5 + (dataCells[i] * unitRadiusScale));
            rect = new RectF((float) (centerX - radius * scale), (float) (centerY - radius * scale),
                    (float) (centerX + radius * scale), (float) (centerY + radius * scale));

            float angles = (float) ((datas[i] * 1.0f / total *currentPercent) * 360);
            mPaint.setColor(mColors[i % mColors.length]);
            canvas.drawArc(rect, start, angles, true, mPaint);

            if(currentPercent == 1){
                Region mRegion = new Region();
                Path path = new Path();
                path.moveTo(centerX, centerY);
                double radian = Math.toRadians(start);
                float x = (float) (centerX + Math.cos(radian)*angles);
                float y = (float) (centerY + Math.sin(radian)*angles);
                path.lineTo(x, y);
                path.addArc(rect, start, angles);
                path.lineTo(centerX, centerY);
                RectF r = new RectF();
                path.computeBounds(r, true);
                mRegion.setPath(path, new Region((int)r.left, (int) r.top, (int) r.right,(int)r.bottom));
                regions.add(mRegion);
            }

            start += angles;
        }
    }

    //画阴影
    private void drawShadow(Canvas canvas) {
        RectF rect = null;
        int start = 0;
        for (int i = 0; i < datas.length; i++) {
            double scale = (0.5 + (dataCells[i] * unitRadiusScale)) > maxRadiusScale ?
                    maxRadiusScale : (0.5 + (dataCells[i] * unitRadiusScale));
            rect = new RectF((float) (centerX - radius * scale), (float) (centerY - radius * scale),
                    (float) (centerX + radius * scale), (float) (centerY + radius * scale));

            RectF shadowRect = new RectF(rect.left + 5, rect.top + 5, rect.right + 5, rect.bottom + 5);

            float angles = (float) ((datas[i] * 1.0f / total * currentPercent) * 360);

            // 绘制阴影,首先将画笔设置颜色色
            mPaint.setColor(Color.LTGRAY);
            // 就是添加内外发光效果
            mPaint.setMaskFilter(new BlurMaskFilter(10, BlurMaskFilter.Blur.SOLID));
            // 绘制阴影在矩形框里面 左上角坐标 10,10,右下角坐标width,height
            canvas.drawArc(shadowRect, start + 2, angles, true, mPaint);

            start += angles;
        }
    }

    //冒泡排序
    public void bubbleSort(double[] arr) {
        if (arr == null || arr.length <= 1) return;

        for (int i = 0; i < arr.length; ++i) {
            boolean flag = false;
            for (int j = 0; j < arr.length - i - 1; ++j) {
                if (arr[j] > arr[j + 1]) {
                    double temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                    flag = true;
                }
            }
            if (!flag) break;//没有数据交换,数组已经有序,退出排序
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                float x = event.getX();
                float y = event.getY();
                boolean isCenter = centerRegion.contains((int) x, (int) y);
                int index = -1;
                if(!isCenter && regions != null && regions.size() != 0){
                    for(int i=0; i<regions.size(); i++){
                        Region item = regions.get(i);
                        boolean isContains = item.contains((int) x, (int) y);
                        if(isContains){
                            index = i;
                            break;
                        }
                    }
                }
                if(index >= 0 && index < datas.length){
                    if(listener != null){
                        listener.irregularClick(index);
                    }
                }
                return true;
        }
        return super.onTouchEvent(event);
    }

    //setter
    public void setColors(int[] mColors) {
        this.mColors = mColors;
        invalidate();
    }

    public void setTextSize(int mTextSize) {
        this.mTextSize = mTextSize;
        mTextPaint.setTextSize(mTextSize);
        invalidate();
    }

    public void setRadius(int radius) {
        this.radius = radius;
        invalidate();
    }

    public void setDatas(double[] datas) {
        this.datas = datas;
        double[] temps = null;
        if (datas != null) {
            temps = new double[datas.length];
            dataCells = new int[datas.length];
            for (int i = 0; i < datas.length; i++) {
                temps[i] = datas[i];
            }
            bubbleSort(temps);
            for (int i = 0; i < datas.length; i++) {
                for (int j = 0; j < temps.length; j++) {
                    if (datas[i] == temps[j]) {
                        dataCells[i] = j;
                        break;
                    }
                }
            }
        }
        total = 0;
        for (int i = 0; i < datas.length; i++) {
            total += datas[i];
        }
        if(total == 0) total = 1;
        unitRadiusScale = (maxRadiusScale - minRadiusScale) / datas.length;

        //属性动画
        ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
        animator.setDuration(ANIM_DURATION);       //设置动画时间
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                currentPercent = (float) animation.getAnimatedValue(); //获取变动后的值
                if(currentPercent == 1){//动画结束后,允许点击
                    setClickable(true);
                }
                invalidate();      //刷新布局
            }
        });
        setClickable(false);
        animator.start();  //启动动画
    }

    public void setTexts(String[] texts) {
        this.texts = texts;
    }

    public void setIrregularPieListener(IrregularPieListener listener){
        this.listener = listener;
    }

    public interface IrregularPieListener{
        void irregularClick(int index);
    }

}

使用方式

第一步,构造布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/wide_divider">
    <include
        android:id="@+id/title"
        layout="@layout/layout_main_title" />
    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="none">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">
            <demo.hanli.hldemo.modules.custompie.widget.IrregularPieView
                android:id="@+id/irregularPieView"
                android:layout_width="match_parent"
                android:layout_height="300dp"
                android:background="#ffffff"/>
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="10dp"
                android:layout_marginLeft="10dp"
                android:layout_marginRight="10dp"
                android:orientation="vertical">
                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="数值(多个数据则“,”隔开):"
                    android:textSize="@dimen/text_size_small"
                    android:textColor="@color/text_black"/>
                <EditText
                    android:id="@+id/etData"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="10dp"
                    android:hint="请输入数据"
                    android:text="16,18,33,33"
                    android:textColor="@color/text_black"
                    android:textSize="@dimen/text_size_small"/>
                <TextView
                    android:id="@+id/tvWarning"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="数据格式不正确"
                    android:textSize="@dimen/text_size_small"
                    android:textColor="@color/red"/>
            </LinearLayout>
            <TextView
                android:id="@+id/update"
                android:layout_width="130dp"
                android:layout_height="wrap_content"
                android:paddingTop="10dp"
                android:paddingBottom="10dp"
                android:layout_marginTop="10dp"
                android:gravity="center"
                android:layout_gravity="center_horizontal"
                android:background="@drawable/bg_round_purple"
                android:text="刷新"
                android:textSize="@dimen/text_size_small"
                android:textColor="@color/white"/>
        </LinearLayout>
    </ScrollView>
</LinearLayout>

第二步,初始化控件:



//初始化图表
    private void iniChart() {
       int[] colors = {
            Color.parseColor("#fbb132"), Color.parseColor("#b1d0ff"),
            Color.parseColor("#fddd71"), Color.parseColor("#54a3ff"),
            Color.parseColor("#32c5e9"), Color.parseColor("#9fe6b8"),
            Color.parseColor("#b4d236"), Color.parseColor("#ff9f7f"),
            Color.parseColor("#e7bcf3"), Color.parseColor("#fb7293")};
       irregularPieView.setIrregularPieListener(new IrregularPieView.IrregularPieListener() {
            @Override
            public void irregularClick(int index) {
                if (index < 0 || index >= datasList.size()) {
            return;
        }
        Toast.makeText(this, "点击了第" + (index + 1) + "个扇形,扇形值为:" + datasList.get(index),
                Toast.LENGTH_SHORT).show();
            }
        });
        irregularPieView.setColors(colors);
        irregularPieView.setTextSize(getResources().getDimensionPixelOffset(R.dimen.text_size_more_small));
    }

第三步,获取网络数据后,添加进控件:

        //此处获取EditText的输入内容,实际情况请改为网络数据
        datasList.clear();
    
        String dataStr = etData.getText().toString();
        String[] dataArr = null;
        if (dataStr != null && dataStr.trim().length() != 0) {
            dataArr = dataStr.trim().split(",");
            if (dataArr != null && dataArr.length != 0) {
                for (int i = 0; i < dataArr.length; i++) {
                    double dataItem = -1;
                    try {
                        dataItem = Double.parseDouble(dataArr[i]);
                        datasList.add(dataItem);
                    } catch (Exception e) {

                    }
                }
            }
        }
        double[] datas = new double[datasList.size()];
        for (int i = 0; i < datasList.size(); i++) {
            datas[i] = datasList.get(i);
        }

        //数据设置到控件
        irregularPieView.setDatas(datas);

 

注意:使用时请不要设置warp_content

 

圆柱图

package demo.hanli.hldemo.modules.custompie.widget;

import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.BlurMaskFilter;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;

import java.math.BigDecimal;

/**
 * 圆柱形柱状图
 */
public class CylinderBarView extends View {
    private int ANIM_DURATION = 1000;//动画时长,默认1秒
    private int maxNum = 4;//扇形图的最大块数,超出部分自动合并到最后一块上去
    private int maxBarWidth;//柱体最大宽度
    private int maxBarSpace;//柱体最大、最小间距
    private int ovalH;//椭圆高度
    private int topOffset;//柱体头部预留的文本高度
    private int axisMarginH;//数轴的左、右边距
    private int axisMarginV;//数轴的上、下边距

    private int mHeight, mWidth;//宽高
    private Paint mPaint;//扇形的画笔
    private Paint mTextPaint;//画文字的画笔
    private Paint mLinePaint;//画数轴的画笔

    private Context context;
    private double maxData;//最大数据
    private double[] datas;//数据集
    private int barWidth = maxBarWidth;//默认柱体宽度
    private int barSpace;//柱体间隔
    private float currentPercent;//当前展示数据的百分比


    //颜色 默认柱体颜色
    private int[] mColors = {
            Color.parseColor("#FFC65B"), Color.parseColor("#FD5998"),
            Color.parseColor("#8971FB"), Color.parseColor("#676974")
    };
    //颜色 默认的头部颜色
    private int[] mTopColors = {
            Color.parseColor("#f3ad2b"), Color.parseColor("#f5317c"),
            Color.parseColor("#765afe"), Color.parseColor("#5c5d64")
    };
    //数轴颜色
    private int mLineColor = Color.parseColor("#34374A");

    private int mTextSize;//文字大小

    public CylinderBarView(Context context) {
        super(context);
        this.context = context;
    }

    public CylinderBarView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.context = context;
        init();
    }

    //初始化
    private void init() {
        mTextSize = 25;
        maxBarWidth = dip2px(24);
        ovalH = dip2px(9);
        axisMarginV = dip2px(15);
        axisMarginH = dip2px(15);
        barSpace = dip2px(7);
        maxBarSpace = dip2px(34);
        topOffset = dip2px(30);

        mPaint = new Paint();
        //当画笔样式为STROKE或FILL_OR_STROKE时,设置笔刷的图形样式, 如圆形样Cap.ROUND,或方形样式Cap.SQUARE
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        //设置是否使用抗锯齿功能,会消耗较大资源,绘制图形速度会变慢。
        mPaint.setAntiAlias(true);

        //数轴
        mLinePaint = new Paint();
        //当画笔样式为STROKE或FILL_OR_STROKE时,设置笔刷的图形样式, 如圆形样Cap.ROUND,或方形样式Cap.SQUARE
        mLinePaint.setStrokeCap(Paint.Cap.ROUND);
        mLinePaint.setStyle(Paint.Style.STROKE);
        mLinePaint.setStrokeWidth(2);
        mLinePaint.setColor(mLineColor);
        //设置是否使用抗锯齿功能,会消耗较大资源,绘制图形速度会变慢。
        mLinePaint.setAntiAlias(true);

        //文字
        mTextPaint = new Paint();
        //设置绘制文字的字号大小
        mTextPaint.setTextSize(mTextSize);
        //当画笔样式为STROKE或FILL_OR_STROKE时,设置笔刷的粗细度
        mTextPaint.setStrokeWidth(2);
        //设置是否使用抗锯齿功能,会消耗较大资源,绘制图形速度会变慢。
        mTextPaint.setAntiAlias(true);
        //设置绘制的颜色,使用颜色值来表示,该颜色值包括透明度和RGB颜色。
        mTextPaint.setColor(Color.BLACK);
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //获取宽高 不要设置wrap_content
        mHeight = MeasureSpec.getSize(heightMeasureSpec);
        mWidth = MeasureSpec.getSize(widthMeasureSpec);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //无数据
        if (datas == null || datas.length == 0) return;

        barWidth = mWidth / (datas.length + 1) > maxBarWidth ? maxBarWidth : mWidth / (datas.length + 1);
        if (datas.length > 1) {
            barSpace = (mWidth - barWidth * (datas.length + 1)) / (datas.length) < 0 ? 0 : (mWidth - barWidth * (datas.length + 1)) / (datas.length);
            barSpace = maxBarSpace < barSpace ? maxBarSpace : barSpace;
        }
        axisMarginH = (mWidth - barSpace * datas.length - barWidth * (datas.length + 1)) / 2;


        //画数轴
        canvas.save();
        drawLine(canvas);
        canvas.restore();

        //画圆柱
        canvas.save();
        drawBar(canvas);
        canvas.restore();

        //文字
        canvas.save();
        drawText(canvas);
        canvas.restore();

    }

    //画文字
    private void drawText(Canvas canvas) {

        for (int i = 0; i < (maxNum < datas.length ? maxNum : datas.length); i++) {
            mTextPaint.setColor(mColors[i % mColors.length]);
            //测量文字大小
            Rect rect = new Rect();
            String textInfo = new BigDecimal(datas[i])
                    .setScale(0, BigDecimal.ROUND_HALF_UP) + "";
            mTextPaint.getTextBounds(textInfo, 0, textInfo.length(), rect);
            int w = rect.width();
            int h = rect.height();
            int offset = dip2px(5);//文字在柱体头部的偏移量
            //画文字
            float top = (float) ((getBottom() - axisMarginV) - (datas[i] * 1.0f * currentPercent / maxData) * (mHeight - axisMarginV - topOffset) + ovalH / 2);
            float bottom = getBottom() - axisMarginV - ovalH / 2;
            float topT = top - ovalH / 2;
            float bottomT = top + ovalH / 2;
            float topB = bottom - ovalH / 2;
            if (bottomT > (getBottom() - axisMarginV)) {
                topT = topB;
            }

            float x = (float) (barWidth * (i + 1) + barSpace * (i)) + barWidth / 2 - w / 2 + axisMarginH;
            float y = topT - offset;
            canvas.drawText(textInfo, 0, textInfo.length(),
                    x, y,
                    mTextPaint);

        }
    }

    //画圆柱
    private void drawBar(Canvas canvas) {
        RectF rect = null, rectOvalBottom, rectOvalTop;
        for (int i = 0; i < (maxNum < datas.length ? maxNum : datas.length); i++) {
            float left = (float) (barWidth * (i + 1) + barSpace * (i) + axisMarginH);
            float top = (float) ((getBottom() - axisMarginV) - (datas[i] * 1.0f * currentPercent / maxData) * (mHeight - axisMarginV - topOffset) + ovalH / 2);
            float right = (float) (barWidth * (i + 1) + barSpace * (i) + barWidth + axisMarginH);
            float bottom = getBottom() - axisMarginV - ovalH / 2;
            if(top > bottom) top = bottom;
            //画矩形
            rect = new RectF(left, top, right, bottom);
            mPaint.setColor(mColors[i % mColors.length]);
            canvas.drawRect(rect, mPaint);

            //画底部椭圆
            float topB = bottom - ovalH / 2;
            float bottomB = getBottom() - axisMarginV;
            rectOvalBottom = new RectF(left, topB, right, bottomB);
            canvas.drawOval(rectOvalBottom, mPaint);

            //画头部椭圆
            mPaint.setColor(mTopColors[i % mTopColors.length]);
            float topT = top - ovalH / 2;
            float bottomT = top + ovalH / 2;
            if (bottomT > (getBottom() - axisMarginV)) {
                topT = topB;
                bottomT = bottomB;
            }
            rectOvalTop = new RectF(left, topT, right, bottomT);
            canvas.drawOval(rectOvalTop, mPaint);
        }
    }

    //画数轴
    private void drawLine(Canvas canvas) {
        mLinePaint.setColor(mLineColor);
        //横轴
        canvas.drawLine(getLeft() + 10 + axisMarginH, getBottom() - axisMarginV, getRight() - axisMarginH, getBottom() - axisMarginV + 2, mLinePaint);

        //竖轴
        canvas.drawLine(getLeft() + barWidth / 2 + axisMarginH, getTop() + axisMarginV, getLeft() + barWidth / 2 + axisMarginH - 2, getBottom() - 10, mLinePaint);
    }

    /**
     * dp转px
     */
    public int dip2px(float dp) {
        float density = context.getResources().getDisplayMetrics().density;
        return (int) (density * dp + 0.5);
    }

    //柱体颜色
    public void setColors(int[] mColors) {
        this.mColors = mColors;
        invalidate();
    }

    //柱头颜色
    public void setTopColors(int[] mColors) {
        this.mTopColors = mColors;
        invalidate();
    }

    //数轴颜色
    public void setLineColor(int mLineColor) {
        this.mLineColor = mLineColor;
        invalidate();
    }

    public void setTextSize(int mTextSize) {
        this.mTextSize = mTextSize;
        mTextPaint.setTextSize(mTextSize);
        invalidate();
    }

    public void setMaxNum(int maxNum) {
        this.maxNum = maxNum;
        invalidate();
    }

    public void setDatas(double[] datas) {
        this.datas = datas;
        maxData = 0;
        for (int i = 0; i < datas.length; i++) {
            if (maxData < datas[i]) {
                maxData = datas[i];
            }
        }

        //属性动画
        ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
        animator.setDuration(ANIM_DURATION);       //设置动画时间
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                currentPercent = (float) animation.getAnimatedValue(); //获取变动后的值
                invalidate();      //刷新布局
            }
        });
        animator.start();  //启动动画
    }

}

圆柱图使用:

 //初始化图表
    private void iniChart(){
       int[] colors = {
            Color.parseColor("#fbb132"), Color.parseColor("#b1d0ff"),
            Color.parseColor("#fddd71"), Color.parseColor("#54a3ff"),
            Color.parseColor("#32c5e9"), Color.parseColor("#9fe6b8"),
            Color.parseColor("#b4d236"), Color.parseColor("#ff9f7f"),
            Color.parseColor("#e7bcf3"), Color.parseColor("#fb7293")};
       int[] topColors = {
            Color.parseColor("#f7a00e"), Color.parseColor("#75aafb"),
            Color.parseColor("#fdce2f"), Color.parseColor("#3490fb"),
            Color.parseColor("#14b9e1"), Color.parseColor("#6ae495"),
            Color.parseColor("#98bc03"), Color.parseColor("#fe7b4f"),
            Color.parseColor("#dc84f5"), Color.parseColor("#f83867")};


        cylinderBarView.setColors(colors);
        cylinderBarView.setTopColors(topColors);
        cylinderBarView.setLineColor(Color.parseColor("#68c8c7"));
                
 cylinderBarView.setTextSize(getResources().getDimensionPixelOffset(R.dimen.text_size_more_small));
    }

 //刷新数据
    private void updateChart(){
        datasList.clear();
        tvWarning.setVisibility(View.GONE);
        String dataStr = etData.getText().toString();
        String[] dataArr = null;
        if(dataStr != null && dataStr.trim().length() != 0){
            dataArr = dataStr.trim().split(",");
            if(dataArr != null && dataArr.length != 0){
                for(int i=0; i<dataArr.length; i++){
                    double dataItem = -1;
                    try {
                        dataItem = Double.parseDouble(dataArr[i]);
                        datasList.add(dataItem);
                    }catch (Exception e){
                        tvWarning.setVisibility(View.VISIBLE);
                        return;
                    }
                }
            }
        }
        double[] datas = new double[datasList.size()];
        for(int i=0; i<datasList.size(); i++){
            datas[i] = datasList.get(i);
        }
        cylinderBarView.setMaxNum(datas.length);
        cylinderBarView.setDatas(datas);

    }

 

 

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值