美文网首页第三方扩展
自定义View_柱形图-玩一把牛逼的~

自定义View_柱形图-玩一把牛逼的~

作者: BraveJoy | 来源:发表于2017-10-13 15:33 被阅读0次

    两个月没有写过文章了,最近比较忙,今天抽个时间写一篇,最近做的一个项目中有一个关于柱形图的,而且不是传统的单列柱形图,而是双列柱形图,类似于下面这种效果~

    双列柱状图.png

    然后我就本能的去GitHub上搜一些图表的框架,后来总结以下特点:
    1.很难吻合自己的需求,总是很多地方不一样;
    2.框架要么很全[复杂],会存在很多不需要的东西;要么很单一,不符合自己的需要;
    3.使用起来复杂。等等
    而且我这个双列柱状图好像还没有类似的框架,百度上搜也是寥寥无几,怎么办呢,自己造一个吧!于是这篇博客诞生了~照例先放个GitHub传送门:https://github.com/SuperKotlin/DoubleLineChatView
    然后看一下今天要实现的效果图:

    DoubleLineChatView.gif

    其实这样的柱状图绘制起来特别简单,难处理的是里面的计算和一些细节等等。

    1.自定义我们需要的属性:


    首先为了一定的扩展性,我们要先思考哪些属性需要自定义,这里我想到了这些

    <declare-styleable name="DoubleLineChatView">
            <!--左边的柱状图的颜色-->
            <attr name="chatview_left_background_color" format="color" />
            <!--右边的柱状图的颜色-->
            <attr name="chatview_right_background_color" format="color" />
    
            <!--字体(左数据)的颜色-->
            <attr name="chatview_left_text_data_color" format="color" />
            <!--字体(右数据)的颜色-->
            <attr name="chatview_right_text_data_color" format="color" />
            <!--字体(数据)的大小-->
            <attr name="chatview_text_data_size" format="dimension" />
    
            <!--(xy轴)的颜色-->
            <attr name="chatview_text_xy_color" format="color" />
            <!--(xy轴)的字体大小-->
            <attr name="chatview_text_xy_size" format="dimension" />
    
            <!--左右柱状图之间的小距离-->
            <attr name="chatview_line_small_distance" format="dimension" />
            <!--柱状图的宽度-->
            <attr name="chatview_line_width" format="dimension" />
            <!--柱状图之间的大距离-->
            <attr name="chatview_line_big_distance" format="dimension" />
            <!--Y轴数据的宽度,当数据字符串过大的时候需要调整这个宽度-->
            <attr name="chatview_y_distance" format="dimension" />
            <!--X轴数据的宽度-->
            <attr name="chatview_x_distance" format="dimension" />
    
            <!--动画时长-->
            <attr name="chatview_animation_duration" format="integer" />
            <!--是否显示坐标轴的箭头-->
            <attr name="chatview_show_arrow" format="boolean" />
            <!--是否显示Y坐标轴的间断标记-->
            <attr name="chatview_show_y_interval" format="boolean" />
    
        </declare-styleable>
    

    画个草图解释一下:

    属性点示意图.png

    OK,这就是大致上我能想到的需求了,还有就是这里解释一下X轴和Y轴的数值显示,X轴上一般显示某某文字,比如项目名称等等,Y轴上面的数据是自动计算的,根据最大值计算显示,计算方式下面会讲。

    声明相关变量和获取自定义属性值我就不详细讲了,之前博客讲过很多,这里重要讲一下绘制过程中需要考虑的东西。
    假设现在有两组数据,这里默认我都写成了0,还有一组字符串数组是X轴的值;

        /**
         * 左边柱状图数据
         */
        private int[] mDataLeft = {0, 0, 0, 0};
        /**
         * 右边柱状图数据
         */
        private int[] mDataRight = {0, 0, 0, 0};
        /**
         * X轴底部文字
         */
        private String[] mDataTextX = {"", "", "", ""};
    

    计算:

    想要绘制出Y轴的数字,那么必须计算,这里我是从0开始显示,后面分成5等分,那么Y轴肯定有个最大值,这个最大值的定义规则如下:先找出两个数据数组中的最大值,然后判断这个最大值是不是5的整倍数,如果不是则自加加到是5的整倍数为止;

          /**
         * 数据
         *
         * @param dataLeft  左边柱状图数据
         * @param dataRight 右边柱状图数据
         * @param dataTextX X轴文字数组
         */
        public void setData(int[] dataLeft, int[] dataRight, String[] dataTextX) {
            this.mDataLeft = dataLeft;
            this.mDataRight = dataRight;
            this.mDataTextX = dataTextX;
            //找出两个数组中的最大值
            int maxLeft = getMax(mDataLeft);
            int maxRight = getMax(mDataRight);
            mMaxData = maxLeft > maxRight ? maxLeft : maxRight;
            Log.i(TAG, "mMaxCount=" + mMaxData);
            //计算Y轴坐标最大值,根据当前最大数据并且是5的整倍数计算
            while (mMaxData % 5 != 0) {
                mMaxData++;
            }
            Log.i(TAG, "mMaxCount=" + mMaxData);
        }
    

    绘制:

    第一步:绘制柱状图drawLineData(canvas);,为什么不先绘制XY轴,因为这里有个覆盖的问题,XY轴和柱形图的覆盖问题,虽然差别不大,但是影响美观。

        /**
         * 绘制柱状图
         */
        private void drawLineData(Canvas canvas) {
            for (int i = 0; i < mDataLeft.length; i++) {
                float startX = mYDistance + mBigDistance + mLineWidth / 2 + (i * (mBigDistance + mSmallDistance + 2 * mLineWidth));
                float endY = ((mViewHeight - mXDistance)) - (mDataLeft[i] * (mViewHeight - 2 * mXDistance)) / mMaxData;
                canvas.drawLine(startX, mViewHeight - mXDistance, startX, endY, mPaintLeft);
                String text = mDataLeft[i] + "";
                float textWidth = mPaintTextLeft.measureText(text, 0, text.length());
                canvas.drawText(text, startX - textWidth / 2, endY - 15, mPaintTextLeft);
            }
    
            for (int i = 0; i < mDataRight.length; i++) {
                float startX = mYDistance + mBigDistance + mSmallDistance + mLineWidth + mLineWidth / 2 + (i * (mBigDistance + mSmallDistance + 2 * mLineWidth));
                float endY = ((mViewHeight - mXDistance)) - (mDataRight[i] * (mViewHeight - 2 * mXDistance)) / mMaxData;
                canvas.drawLine(startX, mViewHeight - mXDistance, startX, endY, mPaintRight);
                String text = mDataRight[i] + "";
                float textWidth = mPaintTextRight.measureText(text, 0, text.length());
                canvas.drawText(text, startX - textWidth / 2, endY - 15, mPaintTextRight);
            }
        }
    

    这里绘制是使用了drawLine方法,那么就需要起始点和终点的坐标值,这里的坐标都是有规律的,讲一大堆不如来个草图(这是左柱状图的计算方式,右柱状图同理)~

    drawLine的坐标计算草图.png

    第二步:绘制坐标轴drawLineXY(canvas);

        /**
         * 绘制坐标轴
         */
        private void drawLineXY(Canvas canvas) {
            canvas.drawLine(mYDistance, mViewHeight - mXDistance, mYDistance, 15, mPaintTextXY);
            canvas.drawLine(mYDistance, mViewHeight - mXDistance, mViewWidth - 15, mViewHeight - mXDistance, mPaintTextXY);
    
            if (mIsShowArrow) {
                //Y轴箭头
                canvas.drawLine(mYDistance, 15, mYDistance - 10, 25, mPaintTextXY);
                canvas.drawLine(mYDistance, 15, mYDistance + 10, 25, mPaintTextXY);
                //X轴箭头
                canvas.drawLine(mViewWidth - 15, mViewHeight - mXDistance, mViewWidth - 25, mViewHeight - mXDistance - 10, mPaintTextXY);
                canvas.drawLine(mViewWidth - 15, mViewHeight - mXDistance, mViewWidth - 25, mViewHeight - mXDistance + 10, mPaintTextXY);
    
            }
        }
    

    第三步:绘制X坐标值drawLineX(canvas);

        /**
         * 绘制X坐标值
         */
        private void drawLineX(Canvas canvas) {
            for (int i = 0; i < mDataTextX.length; i++) {
                //绘制进度数字
                String text = mDataTextX[i];
                //获取文字宽度
                float textWidth = mPaintTextXY.measureText(text, 0, text.length());
                float dx = (mYDistance + mBigDistance + mSmallDistance / 2 + mLineWidth + (i * (mBigDistance + mSmallDistance + 2 * mLineWidth))) - textWidth / 2;
                Paint.FontMetricsInt fontMetricsInt = mPaintTextXY.getFontMetricsInt();
                float dy = (fontMetricsInt.bottom - fontMetricsInt.top) / 2 - fontMetricsInt.bottom;
                float baseLine = mViewHeight - mXDistance / 2 + dy;
                canvas.drawText(text, dx, baseLine, mPaintTextXY);
            }
        }
    

    第四步:绘制Y坐标值drawLineY(canvas);

        /**
         * 绘制Y坐标值
         * 这里的坐标值是根据最大值计算出来对应的间隔,然后从0显示出6个数据
         */
        private void drawLineY(Canvas canvas) {
            for (int i = 0; i < 6; i++) {
                //绘制进度数字
                String text = (mMaxData / 5 * i) + "";
                //获取文字宽度
                float textWidth = mPaintTextXY.measureText(text, 0, text.length());
                float dx = mYDistance / 2 - textWidth / 2;
                Paint.FontMetricsInt fontMetricsInt = mPaintTextXY.getFontMetricsInt();
                float dy = (fontMetricsInt.bottom - fontMetricsInt.top) / 2 - fontMetricsInt.bottom;
                float baseLine = (mViewHeight - mXDistance) - (i * (mViewHeight - 2 * mXDistance) / 5) + dy;
                canvas.drawText(text, dx, baseLine, mPaintTextXY);
                if (mIsShowArrowYInterval) canvas.drawLine(mYDistance, (mViewHeight - mXDistance)
                                - (i * (mViewHeight - 2 * mXDistance) / 5), mYDistance + 10,
                        (mViewHeight - mXDistance) - (i * (mViewHeight - 2 * mXDistance) / 5), mPaintTextXY);
            }
        }
    

    最后再处理一下动画这块就差不多了:

        /**
         * 开启动画,并且绘制图表
         */
        public void start() {
            AnimatorSet set = new AnimatorSet();
            for (int i = 0; i < mDataLeft.length; i++) {
                ValueAnimator animator = ValueAnimator.ofInt(0, mDataLeft[i]);
                final int finalI = i;
                animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator valueAnimator) {
                        mDataLeft[finalI] = (int) valueAnimator.getAnimatedValue();
                        invalidate();
                    }
                });
                set.playTogether(animator);
            }
    
            for (int i = 0; i < mDataRight.length; i++) {
                ValueAnimator animator = ValueAnimator.ofInt(0, mDataRight[i]);
                final int finalI = i;
                animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator valueAnimator) {
                        mDataRight[finalI] = (int) valueAnimator.getAnimatedValue();
                        invalidate();
                    }
                });
                set.playTogether(animator);
            }
            set.setInterpolator(new DecelerateInterpolator());
            set.setDuration(mAnimationDuration);
            set.start();
        }
    
    

    好的,写到这里就可以在Activity中使用了,下面上全家福咯,注释很详细。

    DoubleLineChatView.java

    import android.animation.AnimatorSet;
    import android.animation.ValueAnimator;
    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.Log;
    import android.view.View;
    import android.view.animation.DecelerateInterpolator;
    
    /**
     * Created by zhuyong on 2017/10/12.
     */
    
    public class DoubleLineChatView extends View {
    
        private static final String TAG = "DoubleLineChatView";
        /**
         * 上下文
         */
        private Context mContext;
        /**
         * 左边柱状图画笔
         */
        private Paint mPaintLeft;
        /**
         * 右边柱状图画笔
         */
        private Paint mPaintRight;
        /**
         * 左边柱状图文字画笔
         */
        private Paint mPaintTextLeft;
        /**
         * 右边柱状图文字画笔
         */
        private Paint mPaintTextRight;
        /**
         * XY轴画笔
         */
        private Paint mPaintTextXY;
    
        /**
         * 左边柱状图数据
         */
        private int[] mDataLeft = {0, 0, 0, 0};
        /**
         * 右边柱状图数据
         */
        private int[] mDataRight = {0, 0, 0, 0};
        /**
         * X轴底部文字
         */
        private String[] mDataTextX = {"", "", "", ""};
    
        /**
         * Y轴文字宽度
         */
        private float mYDistance;
        /**
         * X轴底部高度
         */
        private float mXDistance;
        /**
         * 柱状图宽度
         */
        private float mLineWidth;
        /**
         * 颜色字体等变量
         */
        private int mLeftLineBackGroundColor = Color.RED;
        private int mRightLineBackGroundColor = Color.BLUE;
        private int mLeftLineTextColor = Color.RED;
        private int mRightLineTextColor = Color.BLUE;
        private float mLineTextSize;
        private int mLineXYColor = Color.BLACK;
        private float mLineXYSize;
        /**
         * 是否显示XY轴箭头
         */
        private boolean mIsShowArrow = true;
        /**
         * 是否显示Y轴间隔标记
         */
        private boolean mIsShowArrowYInterval = true;
        /**
         * 动画时长,默认1秒
         */
        private int mAnimationDuration = 1000;
        /**
         * View宽度
         */
        private int mViewWidth;
        /**
         * View高度
         */
        private int mViewHeight;
        /**
         * 柱状图的Y轴最大数据,等于或者大于两个数组中的最大值
         */
        private int mMaxData;
        /**
         * 两组柱状图之间的距离(偏大距离)
         */
        private float mBigDistance;
        /**
         * 两个柱状图之间的距离(偏小距离)
         */
        private float mSmallDistance;
    
        public DoubleLineChatView(Context context) {
            this(context, null);
        }
    
        public DoubleLineChatView(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public DoubleLineChatView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            mContext = context;
            //获取自定义属性
            TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.DoubleLineChatView);
            //柱状图的宽度
            mLineWidth = array.getDimension(R.styleable.DoubleLineChatView_chatview_line_width, dip2px(mContext, 30));
            //左边柱状图的背景色
            mLeftLineBackGroundColor = array.getColor(R.styleable.DoubleLineChatView_chatview_left_background_color, mLeftLineBackGroundColor);
            //右边柱状图的背景色
            mRightLineBackGroundColor = array.getColor(R.styleable.DoubleLineChatView_chatview_right_background_color, mRightLineBackGroundColor);
            //左边柱状图的数据字体颜色
            mLeftLineTextColor = array.getColor(R.styleable.DoubleLineChatView_chatview_left_text_data_color, mLeftLineTextColor);
            //右边柱状图的数据字体颜色
            mRightLineTextColor = array.getColor(R.styleable.DoubleLineChatView_chatview_right_text_data_color, mRightLineTextColor);
            //柱状图的数据字体大小
            mLineTextSize = array.getDimension(R.styleable.DoubleLineChatView_chatview_text_data_size, sp2px(mContext, 14));
            //XY轴的颜色以及字体颜色
            mLineXYColor = array.getColor(R.styleable.DoubleLineChatView_chatview_text_xy_color, mLineXYColor);
            //XY轴的字体大小
            mLineXYSize = array.getDimension(R.styleable.DoubleLineChatView_chatview_text_xy_size, sp2px(mContext, 14));
            //两组柱状图之间的距离(偏大距离)
            mBigDistance = array.getDimension(R.styleable.DoubleLineChatView_chatview_line_big_distance, dip2px(mContext, 20));
            //两个柱状图之间的距离(偏小距离)
            mSmallDistance = array.getDimension(R.styleable.DoubleLineChatView_chatview_line_small_distance, dip2px(mContext, 5));
            //是否显示XY轴的箭头
            mIsShowArrow = array.getBoolean(R.styleable.DoubleLineChatView_chatview_show_arrow, true);
            //是否显示Y轴的数据间隔标志
            mIsShowArrowYInterval = array.getBoolean(R.styleable.DoubleLineChatView_chatview_show_y_interval, true);
            //柱状图生长动画时间,默认1秒
            mAnimationDuration = array.getInteger(R.styleable.DoubleLineChatView_chatview_animation_duration, 1000);
            //Y轴数据的宽度,也就是Y轴距离左边的宽度,这个要根据数据字符串的长度进行调整
            mYDistance = array.getDimension(R.styleable.DoubleLineChatView_chatview_y_distance, dip2px(mContext, 40));
            //X轴数据的高度,也就是X轴距离底部的距离
            mXDistance = array.getDimension(R.styleable.DoubleLineChatView_chatview_x_distance, dip2px(mContext, 40));
            array.recycle();
            initView();
        }
    
        /**
         * 初始化画笔
         */
        private void initView() {
            //左边柱状图
            mPaintLeft = new Paint();
            mPaintLeft.setColor(mLeftLineBackGroundColor);
            mPaintLeft.setStrokeWidth(mLineWidth);
            mPaintLeft.setAntiAlias(true);
    
            //右边柱状图
            mPaintRight = new Paint();
            mPaintRight.setColor(mRightLineBackGroundColor);
            mPaintRight.setStrokeWidth(mLineWidth);
            mPaintRight.setAntiAlias(true);
    
            //左边柱状图字体数据
            mPaintTextLeft = new Paint();
            mPaintTextLeft.setColor(mLeftLineTextColor);
            mPaintTextLeft.setTextSize(mLineTextSize);
            mPaintTextLeft.setAntiAlias(true);
    
            //右边柱状图字体数据
            mPaintTextRight = new Paint();
            mPaintTextRight.setColor(mRightLineTextColor);
            mPaintTextRight.setTextSize(mLineTextSize);
            mPaintTextRight.setAntiAlias(true);
    
            //XY轴
            mPaintTextXY = new Paint();
            mPaintTextXY.setStrokeWidth(3);
            mPaintTextXY.setColor(mLineXYColor);
            mPaintTextXY.setTextSize(mLineXYSize);
            mPaintTextXY.setAntiAlias(true);
        }
    
        /**
         * 数据
         *
         * @param dataLeft  左边柱状图数据
         * @param dataRight 右边柱状图数据
         * @param dataTextX X轴文字数组
         */
        public void setData(int[] dataLeft, int[] dataRight, String[] dataTextX) {
            this.mDataLeft = dataLeft;
            this.mDataRight = dataRight;
            this.mDataTextX = dataTextX;
            //找出两个数组中的最大值
            int maxLeft = getMax(mDataLeft);
            int maxRight = getMax(mDataRight);
            mMaxData = maxLeft > maxRight ? maxLeft : maxRight;
            Log.i(TAG, "mMaxCount=" + mMaxData);
            //计算Y轴坐标最大值,根据当前最大数据并且是5的整倍数计算
            while (mMaxData % 5 != 0) {
                mMaxData++;
            }
            Log.i(TAG, "mMaxCount=" + mMaxData);
        }
    
        /**
         * 找出数组中的最大值
         *
         * @param arr 目标数组
         * @return 最大值
         */
        private static int getMax(int[] arr) {
            int max = arr[0];
            for (int i = 1; i < arr.length; i++) {
                if (arr[i] > max) {
                    max = arr[i];
                }
            }
            return max;
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            setDimension(heightMeasureSpec);
        }
    
        /**
         * 重新赋予宽高
         */
        private void setDimension(int heightMeasureSpec) {
            //
            mViewWidth = (int) (mYDistance + mBigDistance + (mDataLeft.length * (mLineWidth * 2 + mBigDistance + mSmallDistance)));
            mViewHeight = MeasureSpec.getSize(heightMeasureSpec);
            Log.i(TAG, "mViewWidth=" + mViewWidth + "px");
            Log.i(TAG, "mViewHeight=" + mViewHeight + "px");
            setMeasuredDimension(mViewWidth, mViewHeight);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            /**
             * 绘制柱状图
             */
            drawLineData(canvas);
            /**
             * 绘制坐标轴
             */
            drawLineXY(canvas);
            /**
             * 绘制X坐标值
             */
            drawLineX(canvas);
            /**
             * 绘制Y坐标值
             */
            drawLineY(canvas);
        }
    
        /**
         * 绘制X坐标值
         */
        private void drawLineX(Canvas canvas) {
            for (int i = 0; i < mDataTextX.length; i++) {
                //绘制进度数字
                String text = mDataTextX[i];
                //获取文字宽度
                float textWidth = mPaintTextXY.measureText(text, 0, text.length());
                float dx = (mYDistance + mBigDistance + mSmallDistance / 2 + mLineWidth + (i * (mBigDistance + mSmallDistance + 2 * mLineWidth))) - textWidth / 2;
                Paint.FontMetricsInt fontMetricsInt = mPaintTextXY.getFontMetricsInt();
                float dy = (fontMetricsInt.bottom - fontMetricsInt.top) / 2 - fontMetricsInt.bottom;
                float baseLine = mViewHeight - mXDistance / 2 + dy;
                canvas.drawText(text, dx, baseLine, mPaintTextXY);
            }
        }
    
        /**
         * 绘制Y坐标值
         * 这里的坐标值是根据最大值计算出来对应的间隔,然后从0显示出6个数据
         */
        private void drawLineY(Canvas canvas) {
            for (int i = 0; i < 6; i++) {
                //绘制进度数字
                String text = (mMaxData / 5 * i) + "";
                //获取文字宽度
                float textWidth = mPaintTextXY.measureText(text, 0, text.length());
                float dx = mYDistance / 2 - textWidth / 2;
                Paint.FontMetricsInt fontMetricsInt = mPaintTextXY.getFontMetricsInt();
                float dy = (fontMetricsInt.bottom - fontMetricsInt.top) / 2 - fontMetricsInt.bottom;
                float baseLine = (mViewHeight - mXDistance) - (i * (mViewHeight - 2 * mXDistance) / 5) + dy;
                canvas.drawText(text, dx, baseLine, mPaintTextXY);
                if (mIsShowArrowYInterval) canvas.drawLine(mYDistance, (mViewHeight - mXDistance)
                                - (i * (mViewHeight - 2 * mXDistance) / 5), mYDistance + 10,
                        (mViewHeight - mXDistance) - (i * (mViewHeight - 2 * mXDistance) / 5), mPaintTextXY);
            }
        }
    
        /**
         * 绘制坐标轴
         */
        private void drawLineXY(Canvas canvas) {
            canvas.drawLine(mYDistance, mViewHeight - mXDistance, mYDistance, 15, mPaintTextXY);
            canvas.drawLine(mYDistance, mViewHeight - mXDistance, mViewWidth - 15, mViewHeight - mXDistance, mPaintTextXY);
    
            if (mIsShowArrow) {
                //Y轴箭头
                canvas.drawLine(mYDistance, 15, mYDistance - 10, 25, mPaintTextXY);
                canvas.drawLine(mYDistance, 15, mYDistance + 10, 25, mPaintTextXY);
                //X轴箭头
                canvas.drawLine(mViewWidth - 15, mViewHeight - mXDistance, mViewWidth - 25, mViewHeight - mXDistance - 10, mPaintTextXY);
                canvas.drawLine(mViewWidth - 15, mViewHeight - mXDistance, mViewWidth - 25, mViewHeight - mXDistance + 10, mPaintTextXY);
    
            }
        }
    
        /**
         * 绘制柱状图
         */
        private void drawLineData(Canvas canvas) {
            for (int i = 0; i < mDataLeft.length; i++) {
                float startX = mYDistance + mBigDistance + mLineWidth / 2 + (i * (mBigDistance + mSmallDistance + 2 * mLineWidth));
                float endY = (mViewHeight - mXDistance) - (mDataLeft[i] * (mViewHeight - 2 * mXDistance)) / mMaxData;
                canvas.drawLine(startX, mViewHeight - mXDistance, startX, endY, mPaintLeft);
                String text = mDataLeft[i] + "";
                float textWidth = mPaintTextLeft.measureText(text, 0, text.length());
                canvas.drawText(text, startX - textWidth / 2, endY - 15, mPaintTextLeft);
            }
    
            for (int i = 0; i < mDataRight.length; i++) {
                float startX = mYDistance + mBigDistance + mSmallDistance + mLineWidth + mLineWidth / 2 + (i * (mBigDistance + mSmallDistance + 2 * mLineWidth));
                float endY = ((mViewHeight - mXDistance)) - (mDataRight[i] * (mViewHeight - 2 * mXDistance)) / mMaxData;
                canvas.drawLine(startX, mViewHeight - mXDistance, startX, endY, mPaintRight);
                String text = mDataRight[i] + "";
                float textWidth = mPaintTextRight.measureText(text, 0, text.length());
                canvas.drawText(text, startX - textWidth / 2, endY - 15, mPaintTextRight);
            }
        }
    
        /**
         * 开启动画,并且绘制图表
         */
        public void start() {
            AnimatorSet set = new AnimatorSet();
            for (int i = 0; i < mDataLeft.length; i++) {
                ValueAnimator animator = ValueAnimator.ofInt(0, mDataLeft[i]);
                final int finalI = i;
                animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator valueAnimator) {
                        mDataLeft[finalI] = (int) valueAnimator.getAnimatedValue();
                        invalidate();
                    }
                });
                set.playTogether(animator);
            }
    
            for (int i = 0; i < mDataRight.length; i++) {
                ValueAnimator animator = ValueAnimator.ofInt(0, mDataRight[i]);
                final int finalI = i;
                animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator valueAnimator) {
                        mDataRight[finalI] = (int) valueAnimator.getAnimatedValue();
                        invalidate();
                    }
                });
                set.playTogether(animator);
            }
            set.setInterpolator(new DecelerateInterpolator());
            set.setDuration(mAnimationDuration);
            set.start();
        }
    
        /**
         * sp转px
         */
        public static int sp2px(Context context, float spValue) {
            final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
            return (int) (spValue * fontScale + 0.5f);
        }
    
        /**
         * dp转px
         */
        public static int dip2px(Context context, float dpValue) {
            final float scale = context.getResources().getDisplayMetrics().density;
            return (int) (dpValue * scale + 0.5f);
        }
    
    }
    

    activity_main.xml

    <?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:gravity="center"
        android:orientation="vertical"
        tools:context="com.zhuyong.doublelinechatview.MainActivity">
    
        <HorizontalScrollView
            android:layout_width="match_parent"
            android:layout_height="200dp"
            android:layout_marginTop="15dp"
            android:background="#DEDEDE"
            android:scrollbars="none">
    
            <com.zhuyong.doublelinechatview.DoubleLineChatView
                android:id="@+id/line_chat_one"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                app:chatview_animation_duration="0"
                app:chatview_left_background_color="@color/colorAccent"
                app:chatview_left_text_data_color="@color/colorAccent"
                app:chatview_line_big_distance="20dp"
                app:chatview_line_small_distance="3dp"
                app:chatview_line_width="25dp"
                app:chatview_right_background_color="@color/colorPrimaryDark"
                app:chatview_right_text_data_color="@color/colorPrimaryDark"
                app:chatview_show_arrow="true"
                app:chatview_show_y_interval="true"
                app:chatview_text_data_size="14sp"
                app:chatview_text_xy_color="@color/black"
                app:chatview_text_xy_size="14sp"
                app:chatview_x_distance="40dp"
                app:chatview_y_distance="45dp" />
    
        </HorizontalScrollView>
    
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:padding="15dp">
    
            <Button
                android:id="@+id/btn_start"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="开始动画" />
    
            <LinearLayout
                android:id="@+id/llayout_uncheck"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentRight="true"
                android:gravity="center"
                android:orientation="horizontal">
    
                <View
                    android:layout_width="15dp"
                    android:layout_height="15dp"
                    android:background="@color/yellow" />
    
                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginLeft="5dp"
                    android:text="未审核数" />
    
            </LinearLayout>
    
            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentRight="true"
                android:layout_below="@+id/llayout_uncheck"
                android:layout_marginTop="5dp"
                android:gravity="center"
                android:orientation="horizontal">
    
                <View
                    android:layout_width="15dp"
                    android:layout_height="15dp"
                    android:background="@color/green" />
    
                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginLeft="5dp"
                    android:text="已审核数" />
    
            </LinearLayout>
    
        </RelativeLayout>
    
        <com.zhuyong.doublelinechatview.DoubleLineChatView
            android:id="@+id/line_chat_two"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_marginTop="15dp"
            app:chatview_animation_duration="1000"
            app:chatview_left_background_color="@color/yellow"
            app:chatview_left_text_data_color="@color/yellow"
            app:chatview_line_big_distance="20dp"
            app:chatview_line_small_distance="3dp"
            app:chatview_line_width="25dp"
            app:chatview_right_background_color="@color/green"
            app:chatview_right_text_data_color="@color/green"
            app:chatview_show_arrow="true"
            app:chatview_show_y_interval="true"
            app:chatview_text_data_size="14sp"
            app:chatview_text_xy_color="@color/black"
            app:chatview_text_xy_size="14sp"
            app:chatview_x_distance="40dp"
            app:chatview_y_distance="45dp" />
    
    </LinearLayout>
    
    

    MainActivity.java

    public class MainActivity extends AppCompatActivity {
        /**
         * 第一组数据
         */
        private int[] mDataLeft = {60, 180, 130, 10, 299, 45, 199, 20, 250};
        private int[] mDataRight = {151, 65, 280, 66, 105, 88, 198, 299, 45};
        private String[] mDataTextX = {"项目1", "项目2", "项目3", "项目4", "项目5", "项目6", "项目7", "项目8", "项目9"};
    
        /**
         * 第二组数据
         */
        private int[] mDataLeftTwo = {60, 181, 130, 100};
        private int[] mDataRightTwo = {151, 65, 40, 20};
        private String[] mDataTextXTwo = {"测试1", "测试2", "测试3", "测试4"};
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            final DoubleLineChatView doubleLineChatViewOne = (DoubleLineChatView) findViewById(R.id.line_chat_one);
            doubleLineChatViewOne.setData(mDataLeft, mDataRight, mDataTextX);
            doubleLineChatViewOne.start();
    
            final DoubleLineChatView doubleLineChatViewTwo = (DoubleLineChatView) findViewById(R.id.line_chat_two);
            doubleLineChatViewTwo.setData(mDataLeftTwo, mDataRightTwo, mDataTextXTwo);
    
            findViewById(R.id.btn_start).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    doubleLineChatViewTwo.start();
                }
            });
        }
    }
    

    GitHub传送门:源码

    相关文章

      网友评论

        本文标题:自定义View_柱形图-玩一把牛逼的~

        本文链接:https://www.haomeiwen.com/subject/ukrzyxtx.html