Android高阶自定义ChartView,让你几分钟掌握绘制,折线图,曲线图,柱状图,柱线图,分分钟写图表框架

喜欢订阅我的公众号--Java2Android技术


Android自定义图表:

效果图:


Demo下载地址:http://download.csdn.net/download/chen364567628/10249231

需求:折线图,曲线图,柱状图,柱线图,让你几分钟搞明白那些框架

思路:柱状图为例

1、自定义ChartView继承View

 初始化需要的画笔(Paint)和图(canvas),配置画笔的颜色、大小、指定所需要画的图形。等等

2、测量ChartView高度,并设置边距和内容区域大小

 直接调用系统方法即可;

3、根据数据计算每个Chart的间隔

 ChartView的高度是x轴文字的高度加上下边距以及y轴的长度。

同理,ChartView的宽度是y轴的文字加上下边距以及x轴的长度。如图所示:


4、直接画出对应的图

 调用系统方法

步骤:

1、

public class CombineChart extends View 
2、测量view高度,并设置边距和内容区域大小

//    当这个视图的大小改变时,这在布局过程中被调用。 如果
//    你刚刚被添加到视图层次结构,你被称为旧的
//    值为0。
//    @param w此视图的当前宽度。
//    @参数h此视图的当前高度。
//    @param oldw这个视图的旧宽度。
//    @param oldh这个视图的旧高度。
@Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { screenWidth = getMeasuredWidth(); screenHeight = getMeasuredHeight(); //设置矩形的顶部 底部 右边Y轴的3部分每部分的高度 getStatusHeight(); super.onSizeChanged(w, h, oldw, oldh); }

 

3、初始化画笔

 public CombineChart(Context context) {
        super(context);
        init(context);
    }
    private void init(Context context) {
//         如果这个视图没有自己做任何绘图,那么设置这个标志
//         允许进一步的优化。 默认情况下,该标志未设置
//         查看,但可以在某些View子类(如ViewGroup)上设置。
//
//         通常,如果您覆盖{@link #onDraw(android.graphics.Canvas)}
//         你应该清除这个标志。
//         @param willNotDraw是否这个视图自己绘制
        setWillNotDraw(false);//-------------------------------------这个必须有
        barPaint = new Paint();
        barPaint.setColor(Color.parseColor("#6FC5F4"));//淡蓝色
} 
4、测量每一个线的点与点之间的距离,以及矩形的宽度

  X轴的长度除以总的item数+空白空间数乘以空白区域宽度(也可以不指定)

5、重写onDraw方法

详情看代码实现
代码:

public class CombineChart extends View {
    private int screenWidth, screenHeight;
    /**
     * item中的最大值
     */
    private float maxValueInItems;
    /**
     * bar的最高值
     */
    private float maxHeight;
    /**
     * 各种画笔 柱形图的 轴的 文本的 线形图的 画点的
     */
    private Paint barPaint, textPaint, linePaint, pointPaint;
    /**
     * 原点的半径
     */
    private RectF barRect;
    /**
     * 左边和上边的边距
     */
    private int botMargin, topMargin;
    /**
     * 每一个bar的宽度
     */
    private int barWidth;
    /**
     * 每个bar之间的距离
     */
    private int barSpace;
    /**
     * x轴 y轴 起始坐标
     */
    private float xStartIndex=0;

    /**
     * 线的路径
     */
    Path linePathT;

    /**
     * 保存bar的左边和右边的x轴坐标点
     */
    private List<Integer> leftPoints = new ArrayList<>();
    private List<Integer> rightPoints = new ArrayList<>();
    //1.数据集合
    List<ArrayList<String>> list;


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

    public CombineChart(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }
    public CombineChart(Context context) {
        super(context);
        init(context);
    }
    private void init(Context context) {
//         如果这个视图没有自己做任何绘图,那么设置这个标志
//         允许进一步的优化。 默认情况下,该标志未设置
//         查看,但可以在某些View子类(如ViewGroup)上设置。
//
//         通常,如果您覆盖{@link #onDraw(android.graphics.Canvas)}
//         你应该清除这个标志。
//         @param willNotDraw是否这个视图自己绘制
        setWillNotDraw(false);//-------------------------------------这个必须有
        barPaint = new Paint();
        barPaint.setColor(Color.parseColor("#6FC5F4"));//淡蓝色

        textPaint = new Paint();
        textPaint.setAntiAlias(true);

        linePaint = new Paint();
        linePaint.setAntiAlias(true);
        linePaint.setStrokeWidth(1);
        linePaint.setColor(Color.parseColor("#6FC5F4"));
        linePaint.setStyle(Paint.Style.STROKE);

        pointPaint = new Paint();
        pointPaint.setAntiAlias(true);
        pointPaint.setColor(Color.parseColor("#6FC5F4"));
        pointPaint.setStyle(Paint.Style.FILL);

        barRect = new RectF();//这是柱子

        linePathT = new Path();//这是线的路径

        botMargin=ScreenUtils.dp2px(context, 20);
        topMargin=ScreenUtils.dp2px(context, 30);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        screenWidth = getMeasuredWidth();
        screenHeight = getMeasuredHeight();
        //设置矩形的顶部 底部 右边Y轴的3部分每部分的高度
        getStatusHeight();
        super.onSizeChanged(w, h, oldw, oldh);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //得到每个bar的宽度
        if (list != null) {
            getItemsWidth(screenWidth, list.size());
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        leftPoints.clear();
        rightPoints.clear();
        canvas.drawColor(Color.parseColor("#FFFFFF"));
        if (list == null||list.size()==0) return;
        //重置路径
        linePathT.reset();
        linePathT.incReserve(list.size());
        //画矩形,划线,花点
        drawBars(canvas);
        canvas.save();
        //画线型图
        canvas.drawPath(linePathT, linePaint);
    }


    private void drawBars(Canvas canvas) {

        for (int i = 0; i < list.size(); i++) {
            barRect.left = (int) (xStartIndex + barWidth * i + barSpace * (i + 1));
            barRect.top = (int) maxHeight - (int) (maxHeight * (Float.parseFloat(list.get(i).get(0)) / maxValueInItems))+topMargin;
            barRect.right = barRect.left + barWidth;
            leftPoints.add((int) barRect.left);
            rightPoints.add((int) barRect.right);
            barPaint.setColor(Color.parseColor("#6FC5F4"));
            canvas.drawRoundRect(barRect, 30,30,barPaint);

            //确定线形图的路径 和 画圆点
            canvas.drawCircle(leftPoints.get(i) + barWidth / 2, barRect.top, 5, pointPaint);
            //划线,由两个点
            if (i == 0) {
                linePathT.moveTo(barRect.left + barWidth / 2, barRect.top);
            }else {
                linePathT.lineTo(barRect.left + barWidth / 2, barRect.top);
            }
            //计算文本宽度
            float tsize = textPaint.measureText(list.get(i).get(1));
            textPaint.setTextSize(ScreenUtils.dp2px(getContext(), 14));

            Float tx = barRect.left+barWidth / 2-tsize/2;
            //点上方
            canvas.drawText(list.get(i).get(0),tx, barRect.top-topMargin/3,textPaint);
            //X轴
            canvas.drawText(list.get(i).get(1),tx,barRect.bottom+topMargin/2,textPaint);
        }
    }

    /**
     * 设置矩形的顶部 底部 右边Y轴的3部分每部分的高度
     */
    private void getStatusHeight() {
        barRect.top =topMargin;
        barRect.bottom = screenHeight-botMargin;
        maxHeight = barRect.bottom - barRect.top;
    }

    public void setChartData(List<ArrayList<String>> list) {
        if (list==null||list.size()==0){
            throw new RuntimeException("数据不能为null");
        }
        this.list = list;
        for (ArrayList<String> item :list){
            float val = Float.parseFloat(item.get(0));
            if (val>maxValueInItems){
                    maxValueInItems=val;
                }
        }
        //得到每个bar的宽度
        getItemsWidth(screenWidth, list.size());
        invalidate();
    }

    /**
     * 设定每个bar的宽度
     *
     * @param screenWidth
     * @param size
     */
    private void getItemsWidth(int screenWidth, int size) {
        barWidth = screenWidth / (size + 8);//每个柱子的宽度
        barSpace = (screenWidth - barWidth * size) / (size + 1);//空白区域的宽度
    }
}
有些手机测量每个数据之间的距离需要在 onSizeChanged这个方法中再调用一次,比如华为手机,不然测不到View的高度,所以绘制不出来。如有兼容问题, 有必要下面这个方法需要多处调用。
getItemsWidth(screenWidth, list.size());


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值