Android 自定义环形图和柱状图总结(支持点击事件)

一、前言:

最近一个项目中涉及到了自定义环形图和柱状图的功能,闲暇之余,记录一下实现过程,文末会分享项目Demo源码,话不多说,直接进入正题吧!老规矩,看下效果图:

截了一张UI效果图
如上图所示:主要涉及到的功能就是环形图和柱状图了,环形图主要分4块区域 ,点击能知道触发的那块区域,柱状图也同理。

二、自定义环形图

1.实现原理:
首先外层一个是一个灰色的圆(默认圆),这个直接用一个画笔0-360度绘制就ok了,然后再用四个画笔画4个四分之一圆,怎么画尼,我这边是利用旋转角度,分别旋转0、90、180、270度即可,接下来就是如何确定点击区域呢,其实这个也简单,通过象限原理去实现,完整代码如下

package com.*.customringchart_master;

import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;

/**
 * @author xiaoyi
 * @description
 * 自定义环形图
 * @date 2021/2/4
 */
public class CircularGraphView extends View {
    
    private ValueAnimator valueAnimator;
    private int mViewCenterX;   //view宽的中心点
    private int mViewCenterY;   //view高的中心点

    private int mRadius; //最里面白色圆的半径
    private float mRingWidth; //圆环的宽度
    private int mMinCircleColor;    //最里面圆的颜色
    private int mRingNormalColor;    //默认圆环的颜色
    private Paint mPaint;//默认圆环画笔
    //四个圆环画笔
    private Paint mPaint1;
    private Paint mPaint2;
    private Paint mPaint3;
    private Paint mPaint4;
    //4个圆环角度值
    private float sweepAngle1 = 0;
    private float sweepAngle2 = 0;
    private float sweepAngle3 = 0;
    private float sweepAngle4 = 0;
    
    private RectF mRectF; //圆环的矩形区域
    
    //处理点击区域
    //圆周率
    private static final float PI = 3.1415f;
    private static final int PART_ZERO = 0;
    private static final int PART_ONE = 1;
    private static final int PART_TWO = 2;
    private static final int PART_THREE = 3;
    private static final int PART_FOUR = 4;
    // 控件点击监听
    private OnClickListener onClickListener;
    private int mTotalRadio; //总点击区域
    // 圆环文字画笔
    private Paint dialWordsPaint;
    
    public CircularGraphView(Context context) {
        this(context, null);
    }

    public CircularGraphView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CircularGraphView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircularGraphView);
        //最里面白色圆的半径
        mRadius = a.getInteger(R.styleable.CircularGraphView_min_circle_radio, 100);
        //圆环宽度
        mRingWidth = a.getFloat(R.styleable.CircularGraphView_ring_width, 60);
        //最里面的圆的颜色(白色)
        mMinCircleColor = a.getColor(R.styleable.CircularGraphView_circle_color, context.getResources().getColor(R.color.white));
        //圆环的默认颜色(圆环占据的是里面的圆的空间)
        mRingNormalColor = a.getColor(R.styleable.CircularGraphView_ring_normal_color, context.getResources().getColor(R.color.color_EFF1F4));
        a.recycle();
        //默认圆环画笔
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setAntiAlias(true);
        //抗锯齿画笔1
        mPaint1 = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint1.setAntiAlias(true);
        //抗锯齿画笔2
        mPaint2 = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint2.setAntiAlias(true);
        //抗锯齿画笔3
        mPaint3 = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint3.setAntiAlias(true);
        //抗锯齿画笔4
        mPaint4 = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint4.setAntiAlias(true);
        //需要重写onDraw就得调用此
        this.setWillNotDraw(false);
        //圆环文字画笔
        dialWordsPaint = new Paint();
        dialWordsPaint.setAntiAlias(true);
        dialWordsPaint.setStyle(Paint.Style.FILL);
        dialWordsPaint.setTextSize(30.0f);
        dialWordsPaint.setColor(Color.parseColor("#9E9FAF"));

    }

    @SuppressLint("DrawAllocation")
    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);

        //view的宽和高,相对于父布局(用于确定圆心)
        int viewWidth = getMeasuredWidth();
        int viewHeight = getMeasuredHeight();
        mViewCenterX = viewWidth / 2;
        mViewCenterY = viewHeight / 2;
        //画矩形
        mRectF = new RectF(mViewCenterX - mRadius - mRingWidth / 2, mViewCenterY - mRadius - mRingWidth / 2,
                mViewCenterX + mRadius + mRingWidth / 2, mViewCenterY + mRadius + mRingWidth / 2);
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mPaint.setColor(mMinCircleColor);
        canvas.drawCircle(mViewCenterX, mViewCenterY, mRadius, mPaint);

        //画文字
        canvas.drawText("18点", mViewCenterX, mViewCenterY + 200, dialWordsPaint);
        canvas.drawText("12点", mViewCenterX + 160, mViewCenterY, dialWordsPaint);
        canvas.drawText("6点", mViewCenterX, mViewCenterY - 180, dialWordsPaint);
        canvas.drawText("0点", mViewCenterX - 220, mViewCenterY, dialWordsPaint);
        
        //画默认圆环
        drawNormalRing(canvas);
        //画彩色圆环
        drawColorRing(canvas, mPaint1, 0f,
                sweepAngle1, Color.parseColor("#EAB537"));
        drawColorRing(canvas, mPaint2, -90f,
                sweepAngle2, Color.parseColor("#2CCAA9"));
        drawColorRing(canvas, mPaint3, -180f,
                sweepAngle3, Color.parseColor("#2B6DE6"));
        drawColorRing(canvas, mPaint4, -270f,
                sweepAngle4, Color.parseColor("#103A87"));

    }

    /**
     * 画默认圆环
     *
     * @param canvas
     */
    private void drawNormalRing(Canvas canvas) {
        Paint ringNormalPaint = new Paint(mPaint);
        ringNormalPaint.setStyle(Paint.Style.STROKE);
        ringNormalPaint.setStrokeWidth(mRingWidth);
        ringNormalPaint.setColor(mRingNormalColor);//圆环默认颜色为灰色
        canvas.drawArc(mRectF, 0, 360, false, ringNormalPaint);
    }

    /**
     * 画彩色圆环
     * degrees 旋转角度
     * startAngle 开始角度
     * sweepAngle 可以理解成结束角度
     * color 圆环颜色值
     *
     * @param canvas
     */
    private void drawColorRing(Canvas canvas, Paint paint, float degrees,
                               float sweepAngle, int color) {
        paint.setColor(color);
        Paint ringColorPaint = new Paint(paint);
        ringColorPaint.setStyle(Paint.Style.STROKE);
        ringColorPaint.setStrokeWidth(mRingWidth);
        //   ringColorPaint.setShader(new SweepGradient(mViewCenterX, mViewCenterX, color, null));
        //逆时针旋转角度
        canvas.rotate(degrees, mViewCenterX, mViewCenterY);
        canvas.drawArc(mRectF, (float) 0.0, sweepAngle, false, ringColorPaint);
        ringColorPaint.setShader(null);
    }

    /**
     * 设置当前圆环值
     *
     * @param
     */
    public void setAnnularData(float sweepAngle1, float sweepAngle2,
                               float sweepAngle3, float sweepAngle4) {
        this.sweepAngle1 = sweepAngle1;
        this.sweepAngle2 = sweepAngle2;
        this.sweepAngle3 = sweepAngle3;
        this.sweepAngle4 = sweepAngle4;
        invalidate();
    }

    /**
     * 设置当前圆环宽度比例
     *
     * @param minRadio  内圆半径
     * @param ringWidth 圆环宽度
     */
    public void setProportionData(int minRadio, float ringWidth) {
        this.mRadius = minRadio;
        this.mRingWidth = ringWidth;
        invalidate();
    }

    /**
     * 饼图touch事件
     * 获取触摸位置计算属于哪个区域
     *
     * @param
     * @return
     */
    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            float x = event.getX() - mViewCenterX;
            float y = event.getY() - mViewCenterY;
            whichZone(x,y);
        }
        return super.onTouchEvent(event);
    }

    /**
     * 判断点击了圆的哪个区域
     * @param x
     * @param y
     */
    private void whichZone(float x, float y) {
        // 简单的象限点处理
        // 第一象限在右下角,第二象限在左下角,代数里面的是逆时针,这里是顺时针
        if(x > 0 && y > 0) {
            // 点击事件
            if (onClickListener != null) {
                onClickListener.onClick(PART_ZERO);
            }
        } else if(x > 0 && y < 0) {
            // 点击事件
            if (onClickListener != null) {
                onClickListener.onClick(PART_THREE);
            }
        } else if(x < 0 && y < 0) {
            // 点击事件
            if (onClickListener != null) {
                onClickListener.onClick(PART_TWO);
            }
        } else if(x < 0 && y > 0) {
            // 点击事件
            if (onClickListener != null) {
                onClickListener.onClick(PART_ONE);
            }
        }
    }
    
    /**
     * 设置点击监听
     *
     * @param onClickListener 点击回调接口
     */
    public void setOnClickListener(OnClickListener onClickListener) {
        this.onClickListener = onClickListener;
    }
    
    /**
     * 点击回调接口
     */
    public interface OnClickListener {
        /**
         * 点击回调方法
         *
         * @param region 区域类型
         */
        void onClick(int region);
    }
}

三、自定义柱状图

package com.*.customringchart_master;

import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Build;
import android.util.AttributeSet;
import android.widget.FrameLayout;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

/**
 * @author xiaoyi
 * @description
 * 自定义柱状图
 * @date 2021/2/4
 */
public class HistogramView extends FrameLayout {
    protected int defaultBorderColor = Color.argb(255, 217, 217, 217);
    protected int titleTextColor = Color.argb(255, 217, 217, 217);
    protected int labelTextColor;
    protected int mTitleTextSize = 42;
    protected int mLabelTextSize = 20;
    protected String mTitle;
    private int mWidth;
    private int mHeight;
    private int mLeftTextSpace;
    private int mBottomTextSpace;
    private int mTopTextSpace;
    protected Paint mBorderLinePaint;
    private Double maxData=0.0;

    private List<Double> mDatas;

    /**
     * 备注文本画笔
     */
    private Paint mTextPaint;
    /**
     * 标题文本画笔
     */
    private Paint mTitleTextPaint;

    private BaseChart baseChartView;

    public HistogramView(@NonNull Context context) {
        super(context);
        init(context, null);
    }

    public HistogramView(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

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

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public HistogramView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context, attrs);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mWidth = getMeasuredWidth();
        mHeight = getMeasuredHeight();
    }
    
    @SuppressLint("CustomViewStyleable")
    private void init(Context context, AttributeSet attrs) {
        mDatas = new ArrayList<>();
        
        TypedArray t = context.obtainStyledAttributes(attrs, R.styleable.barCharts);
        defaultBorderColor = t.getColor(R.styleable.barCharts_borderColor, defaultBorderColor);
        titleTextColor = t.getColor(R.styleable.barCharts_titleTextColor, Color.GRAY);
        mTitleTextSize = (int) t.getDimension(R.styleable.barCharts_titleTextSize, mTitleTextSize);
        mLabelTextSize = (int) t.getDimension(R.styleable.barCharts_labelTextSize, mLabelTextSize);
        labelTextColor = t.getColor(R.styleable.barCharts_labelTextColor, Color.GRAY);

        mLeftTextSpace = (int) t.getDimension(R.styleable.barCharts_leftTextSpace, 30);
        mBottomTextSpace = (int) t.getDimension(R.styleable.barCharts_bottomTextSpace, 20);
        mTopTextSpace = (int) t.getDimension(R.styleable.barCharts_topTextSpace, 70); //间距
        mTitle = t.getString(R.styleable.barCharts_title);
        t.recycle();
        
        mBorderLinePaint = generatePaint();
        mBorderLinePaint.setColor(defaultBorderColor);
        mBorderLinePaint.setStrokeWidth(dp2px(context, 1));

        mTextPaint = generatePaint();
        mTextPaint.setColor(labelTextColor);
        mTextPaint.setTextSize(mLabelTextSize);

        mTitleTextPaint = generatePaint();
        mTitleTextPaint.setColor(titleTextColor);
        mTitleTextPaint.setTextSize(mTitleTextSize);

        baseChartView = new BaseChart(context, attrs);
        LayoutParams parames = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
        parames.setMargins(mLeftTextSpace, mTopTextSpace, mLeftTextSpace, 0);
        baseChartView.setLayoutParams(parames);
        baseChartView.setBottomDrawPadding(mBottomTextSpace);
        baseChartView.setLeftDrawPadding(mLeftTextSpace);
        baseChartView.setTopDrawPadding(mTopTextSpace);
        addView(baseChartView);
    }

    private Paint generatePaint() {
        Paint m = new Paint();
        m.setAntiAlias(true);
        m.setStyle(Paint.Style.STROKE);
        return m;
    }

    private void setMaxData() {
        if (mDatas.size()>0){
            this.maxData = Collections.max(mDatas);
        }
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);

        if (mTitle != null) {
            canvas.drawText(mTitle, mWidth / 2 - mTitleTextPaint.measureText(mTitle) / 2,
                    mTopTextSpace - mTitleTextSize, mTitleTextPaint);
        }

        canvas.translate(mLeftTextSpace, mHeight - mBottomTextSpace);
    }
    
    public void setDatas(List<Double> mDatas, List<String> mDescribe,boolean isAnimation) {
        this.mDatas = mDatas;
        setMaxData();
        baseChartView.setDatas(mDatas, mDescribe,isAnimation);
    }
    
    //柱状图item点击事件
    public void setOnItemClick(BaseChart.setOnRangeBarItemClickListener clickListener) {
        baseChartView.setOnRangeBarItemClickListener(clickListener);
    }

    
    public void addEndMoreData(List<Double> mDatas, List<String> mDesciption) {
        baseChartView.addEndMoreData(mDatas, mDesciption);
    }

    
    public void addStartMoreData(List<Double> mDatas, List<String> mDesciption) {
        baseChartView.addStartMoreData(mDatas,mDesciption);
    }
    
    public static int dp2px(Context context, int dpValue) {
        return (int) context.getResources().getDisplayMetrics().density * dpValue;
    }
}
package com.*.customringchart_master;

import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;


/**
 * @author xiaoyi
 * @description
 * 自定义柱状图
 * @date 2021/2/4
 */
public class BaseChart extends View {
  
    protected List<Double> mDatas;
    protected List<String> mDescription;
    //柱状图画笔
    protected Paint mDataLinePaint;
    protected int defaultLineColor = Color.parseColor("#2B6DE6");
    protected int descriptionColor;
    protected int dataColor;
    private int mWidth;
    private int mHeight;
    private int mShowNumber;

    private float perBarW;
    private Double maxData = 0.0;

    private int mMaxScrollx;
    protected int defaultBorderColor = Color.argb(255, 217, 217, 217);
    protected Paint mBorderLinePaint;
    //x轴描述文字画笔
    protected Paint mTextPaint;
    protected int descriptionTextSize;
    protected int dataTextSize;

    private int mBottomPadding;
    private int mLeftPadding;
    private int mTopPadding;


    protected float scale = 0.5f;

    protected boolean canClickAnimation = false;

    protected ValueAnimator animator;

    // 柱状图控件点击监听  0.0
    private setOnRangeBarItemClickListener onRangeBarItemClickListener;
    /* 用户点击到了无效位置 */
    public static final int INVALID_POSITION = -1;
    /* 辅助计算柱宽,表示一个条目的宽度,包括柱子和空余部分 */
    private float mItemBarWidth = 0;
    private Context mContext;

    public BaseChart(Context context) {
        super(context);
        init(context, null);
    }


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

    @SuppressLint("CustomViewStyleable")
    private void init(Context context, AttributeSet attrs) {
        mContext = context;
        initAnimation();
        TypedArray t = context.obtainStyledAttributes(attrs, R.styleable.barCharts);
        defaultBorderColor = t.getColor(R.styleable.barCharts_borderColor, defaultBorderColor);
        descriptionTextSize = (int) t.getDimension(R.styleable.barCharts_labelTextSize, 30);
        dataTextSize = (int) t.getDimension(R.styleable.barCharts_dataTextSize, 20);
        descriptionColor = t.getColor(R.styleable.barCharts_descriptionTextColor, Color.GRAY);
        dataColor = t.getColor(R.styleable.barCharts_dataTextColor, Color.GRAY);
        mShowNumber = t.getInteger(R.styleable.barCharts_barShowNumber, 4);
        canClickAnimation = t.getBoolean(R.styleable.barCharts_isClickAnimation, false);
        t.recycle();
        
        mDatas = new ArrayList<>();
        mDescription = new ArrayList<>();

        mDataLinePaint = new Paint();
        mDataLinePaint.setAntiAlias(true);
        mDataLinePaint.setColor(defaultLineColor);
        mDataLinePaint.setStyle(Paint.Style.STROKE);

        mBorderLinePaint = new Paint();
        mBorderLinePaint.setColor(defaultBorderColor);
        mBorderLinePaint.setStyle(Paint.Style.STROKE);
        mBorderLinePaint.setStrokeWidth(dp2px(5));
        mBorderLinePaint.setAntiAlias(true);

        mTextPaint = new Paint();
        mTextPaint.setAntiAlias(true);
        mTextPaint.setColor(Color.GRAY);
        mTextPaint.setStyle(Paint.Style.FILL);
        mTextPaint.setTextSize(dp2pxTo(context,14.0f));
    }

    public BaseChart(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mWidth = getMeasuredWidth();
        mHeight = getMeasuredHeight();
        setDataLineWidth();
    }

    private void setDataLineWidth() {
        if (mDatas.size() > 0) {
            //设置柱状图宽度
            mDataLinePaint.setStrokeWidth(15);
            mMaxScrollx = (mWidth / mShowNumber) * mDatas.size() - mWidth;

            //计算ITEM宽度
            mItemBarWidth = mWidth / mDatas.size();
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        perBarW = mWidth / mShowNumber;
        canvas.translate(0, mHeight - mBottomPadding);
        setMaxData();

        canvas.drawLine(0, 0, mMaxScrollx + mWidth, 0, mBorderLinePaint);
        for (int i = 0; i < mDatas.size(); i++) {
            String perData = String.valueOf(Math.round(scale < 1 ? Math.round(mDatas.get(i) * scale) : mDatas.get(i)));

            float x = (i + 0.5f) * perBarW;
            float y = (float) ((mHeight - mTopPadding - mBottomPadding) / maxData * mDatas.get(i));
            canvas.drawLine(x, 0, x, -y * scale, mDataLinePaint);

            mTextPaint.setTextSize(dataTextSize);
            mTextPaint.setColor(dataColor);
            if (Integer.parseInt(perData) != 0) {
                canvas.drawText(perData + "次",
                        x - mTextPaint.measureText(perData) / 2,
                        -y * scale - dataTextSize,
                        mTextPaint);
            }
            mTextPaint.setTextSize(descriptionTextSize);
            mTextPaint.setColor(descriptionColor);

        }
        //绘制描文字
        for (int i = 0; i < mDescription.size(); i++) {
            float x = (i + 0.5f) * perBarW;
            canvas.drawText(mDescription.get(i),
                    x - mTextPaint.measureText(mDescription.get(i)) / 2,
                    descriptionTextSize * 2,
                    mTextPaint);
        }
    }

    public void startCliclkAnimation() {
     //   if (canClickAnimation) {
            animator.start();
      //  }
    }

    private void initAnimation() {
        animator = ValueAnimator.ofFloat(0.2f, 1);
        animator.setInterpolator(new AccelerateDecelerateInterpolator());
        animator.setDuration(600);
        animator.setRepeatCount(0);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                scale = (float) animation.getAnimatedValue();
                postInvalidate();
            }
        });
    }
    
    public void setBottomDrawPadding(int bottomy) {
        mBottomPadding = bottomy;
    }

    public void setLeftDrawPadding(int left) {
        mLeftPadding = left;
    }

    public void setTopDrawPadding(int left) {
        mTopPadding = left;
    }


    private void setMaxData() {
        if (mDatas.size() > 0) {
            this.maxData = Collections.max(mDatas);
        }
    }
    
    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            int position = identifyWhickItemClick(event.getX(), event.getY());
            if (position != INVALID_POSITION && onRangeBarItemClickListener != null) {
                startCliclkAnimation();
                onRangeBarItemClickListener.onItemClick(position);
            }
        }
        return super.onTouchEvent(event);
    }
    
    /**
     * 根据点击的手势位置识别是第几个柱图被点击
     *
     * @param x
     * @param y
     * @return -1时表示点击的是无效位置
     */
    private int identifyWhickItemClick(float x, float y) {
        float leftx = 0;
        float rightx = 0;
        for (int i = 0; i < mDatas.size(); i++) {
            leftx = i * mItemBarWidth;
            rightx = (i + 1) * mItemBarWidth;
            if (x < leftx) {
                break;
            }
            if (leftx <= x && x <= rightx && mDatas.get(i) != 0.0) {
                return i;
            }
        }
        return INVALID_POSITION;
    }
    /**
     * 设置点击监听
     *
     * @param onClickListener 点击回调接口
     */
    public void setOnRangeBarItemClickListener(setOnRangeBarItemClickListener onClickListener) {
        this.onRangeBarItemClickListener = onClickListener;
    }

    /**
     * 点击回调接口
     */
    public interface setOnRangeBarItemClickListener {
        /**
         * 点击回调方法
         *
         * @param position 点击的下标
         */
        void onItemClick(int position);
    }


    public void setDatas(List<Double> mDatas, List<String> mDescribe, boolean isAnimation) {
        this.mDatas.clear();
        this.mDatas.addAll(mDatas);
        this.mDescription = mDescribe;
        setDataLineWidth();
        if (isAnimation) {
            animator.start();
        } else {
            scale = 1;
            postInvalidate();
        }
    }

    public void addEndMoreData(List<Double> mDatas, List<String> mDesciption) {
        this.mDatas.addAll(mDatas);
        this.mDescription.addAll(mDesciption);
        setDataLineWidth();

        scale = 1;
        postInvalidate();
    }

    private int startX = 0;

    public void addStartMoreData(List<Double> mDatas, List<String> mDesciption) {
        mDatas.addAll(this.mDatas);
        mDesciption.addAll(this.mDescription);
        this.mDatas.clear();
        this.mDatas.addAll(mDatas);
        this.mDescription.clear();
        this.mDescription.addAll(mDesciption);
        startX = (mWidth / mShowNumber) * mDatas.size();
        setDataLineWidth();
        postInvalidate();
    }

    protected int dp2px(int dpValue) {
        return (int) getContext().getResources().getDisplayMetrics().density * dpValue;
    }
    
    public static float dp2pxTo(Context context,float dp) {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,
                context.getResources().getDisplayMetrics());
    }
}

四、布局文件代码

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#FFFFFF"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <com.xxxxx.customringchart_master.CircularGraphView
        android:id="@+id/circularView"
        android:layout_width="230dp"
        android:layout_height="220dp"
        android:layout_gravity="center"
        android:layout_marginTop="20dp"
        app:min_circle_radio="100"
        app:ring_width="60"
        tools:ignore="MissingConstraints" />

    <com.xxxxx.customringchart_master.HistogramView
        android:id="@+id/barChart"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:layout_gravity="center"
        android:layout_marginLeft="20dp"
        android:layout_marginTop="10dp"
        android:layout_marginRight="20dp"
        app:barShowNumber="4"
        app:borderColor="@color/color_F1F3F6"
        app:bottomTextSpace="40dp"
        app:dataTextColor="@color/color_0C102C"
        app:dataTextSize="12sp"
        app:isClickAnimation="true"
        app:leftTextSpace="10dp" />


</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<resources>

    <declare-styleable name="CircularGraphView">

        <!-- 圆的半径 -->
        <attr name="min_circle_radio" format="integer"/>

        <!-- 圆环的宽度 -->
        <attr name="ring_width" format="float"/>

        <!-- 内圆的颜色 -->
        <attr name="circle_color" format="color"/>

        <!-- 外圆的颜色 -->
        <attr name="max_circle_color" format="color"/>

        <!-- 圆环的默认颜色 -->
        <attr name="ring_normal_color" format="color"/>

        <!-- 圆环要显示的彩色的区域(随着数值的改变,显示不同大小的彩色区域)-->
        <attr name="ring_color_select" format="integer"/>

        <!-- 绘制内容的数值 -->
        <attr name="maxValue" format="integer" />
        
        <attr name="value" format="integer" />

    </declare-styleable>

    <declare-styleable name="barCharts">
        <attr name="borderColor" format="color" />
        <attr name="lineColor" format="color" />
        <attr name="titleTextColor" format="color" />
        <attr name="title" format="string" />
        <attr name="titleTextSize" format="dimension" />

        <attr name="labelTextSize" format="dimension" />
        <attr name="labelTextColor" format="color" />

        <attr name="descriptionTextSize" format="dimension" />
        <attr name="descriptionTextColor" format="color" />

        <attr name="dataTextSize" format="dimension" />
        <attr name="dataTextColor" format="color" />

        <attr name="leftTextSpace" format="dimension" />
        <attr name="topTextSpace" format="dimension" />
        <attr name="bottomTextSpace" format="dimension" />
        <attr name="barShowNumber" format="integer" />
        <attr name="isClickAnimation" format="boolean" />

    </declare-styleable>
    
</resources>

总结:

1.以上就是环形图和柱状图所以实现代码了,柱状图我这边直接贴代码了,代码不难应该很好理解,
这里我要强调几点,环形图上面四个文字,坐标我是写死的,正常来说 需要动态计算的,还有一个自定义环形图手机适配的问题,打个比方,我在720手机上 圆的半径100和圆环宽度给的60刚刚好,但是分辨高的手机上显示的比例却小了,博主的解决办法:首先获取手机分辨率宽高,公式:60 / 720.0 * width+40 然后重新调用setProportionData方法设置一下半径和圆环宽度即可。
2.毕竟代码都需要慢慢完善,环形图动画暂未添加 ,需要自行实现。
3.最后喜欢该文章的朋友,欢迎点赞收藏哈,若有笔误的地方,请多多指教,勿喷!

自定义环形图和柱状图源码

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值