自定义控件_柱状图


在这里插入图片描述

使用
ColumnChart lineChart = findViewById(R.id.lineChart);

        //设定20组数据
        int[][] data = new int[20][5];
        data[0][1] = R.color.blue_rgba_24_261_255;
        data[0][2] = R.color.orange;
        data[0][3] = R.color.red_2;
        data[0][4] = R.color.main_green;

        //设置随机数
        Random random = new Random();
        for (int i = 0; i < 20; i++) {
            data[i][0]  =random.nextInt(19)+1;
        }

        lineChart.setData(data);//添加数据
        lineChart.start();//开启动画
布局文件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
    <com.example.ocean.charts.column.ColumnChart
        android:id="@+id/lineChart"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="400dp"
        app:graphTitle = "骑行记录"
        app:xAxisNmae = "第几天"
        app:yAxisNmae = "骑行公里数"
        app:axisTextSize = "4sp"
        app:axisDevidedSizeX = "20"
        app:axisDevidedSizeY = "20"
        android:layout_marginBottom="30dp"
        />
<!--        app:axisTextColor = "000000"-->
</RelativeLayout>

实现步骤

        drawTitle(canvas,basePaint);//绘制标题
        drawAxisArrowX(canvas,basePaint);//绘制X轴箭头
        drawAxisArrowY(canvas,basePaint);//绘制Y轴箭头
        drawAxisX(canvas,basePaint);//绘制X轴
        drawAxisY(canvas,basePaint);//绘制Y轴
        drawAxisScaleX(canvas,basePaint);//绘制X轴刻度
        drawAxisScaleY(canvas,basePaint);//绘制Y轴刻度
        drawAxisScaleValuveX(canvas,basePaint);//绘制X轴刻度值
        drawAxisScaleXValuveY(canvas,basePaint);//绘制Y轴刻度值
        drawColumn(canvas,basePaint);//绘制柱体
图表类 ColumnChart.java
package com.example.ocean.charts.column;

import android.animation.Animator;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.os.Build;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;

import com.example.ocean.R;

import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;

public class ColumnChart extends BaseColumnChart {

    private float moveX;//当前手指在屏幕上X轴的坐标
    private float moveY;//当前手指在屏幕上Y轴的坐标
    private Paint touchPaint;//触摸时候的画笔
    int[][] data;//传入的数据
    public void setData(int[][] data) {
        this.data = data;
    }

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

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

    public ColumnChart(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr,0);
    }

    public ColumnChart(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        initPaint();
    }


//    初始化画笔
    private void initPaint() {
        touchPaint = new Paint(basePaint);//触摸时候的画笔,触摸效果
        touchPaint.setStyle(Paint.Style.FILL);
    }

    /**        Explain : 返回当前需要设定的Y轴的长度
    * @author LiXiang */
    @Override
    protected int getSpaceY() {
        return getDefaultSpaceY();
    }

    /**        Explain : 返回当前需要设定的X轴的长度
     * @author LiXiang */
    @Override
    protected int getSpaceX() {
        return getDefaultSpaceX();
    }


    /**        Explain : 绘制柱体
    * @author LiXiang */
    @Override
    protected void drawColumn(Canvas canvas, Paint basePaint) {

        Paint rectPaint = new Paint(basePaint);
        if (data != null) {

        for (int i = 0; i < data.length; i++) {
            getColumnColor(rectPaint, data[i]);
            canvas.drawRect(originalX+segmentX*i,originalY-segmentY*data[i][0]*mAnimatedValue,originalX+segmentX*(i+1)-dip2px(2),originalY,rectPaint);
        }
        }

        if ( onTouch && mAnimatedValue == mAnimatedValueMax) {//曲线绘制完成之后才能进行触摸绘制
            drawOnTouch(canvas);
        }

    }

    /**        Explain : 设置柱体颜色,依据当前不同高度设定不同颜色
    * @author LiXiang */
    private int getColumnColor(Paint rectPaint, int[] datum) {
        int color = 0;
        if (datum[0]<5){//高度<5的颜色
            color = ContextCompat.getColor(mContext, data[0][1]);
        }else if (datum[0]<10 && datum[0]>4){//4<高度<10的颜色
            color = ContextCompat.getColor(mContext, data[0][2]);
        }else if (datum[0]<15 && datum[0]>9){//9<高度<15的颜色
            color = ContextCompat.getColor(mContext, data[0][3]);
        }else {//14<高度 的颜色
            color = ContextCompat.getColor(mContext, data[0][4]);
        }
            rectPaint.setColor(color);
        return color;
    }

    /**        Explain : 绘制X轴
     * @author LiXiang */
    @Override
    protected void drawAxisX(Canvas canvas, Paint basePaint) {
        canvas.drawLine(originalX,originalY,endX,originalY,basePaint);
    }

    /**        Explain : 绘制Y轴
    * @author LiXiang */
    @Override
    protected void drawAxisY(Canvas canvas, Paint basePaint) {
        canvas.drawLine(originalX,endY,originalX,originalY,basePaint);
    }

    /**        Explain : 绘制X轴刻度线
    * @author LiXiang */
    @Override
    protected void drawAxisScaleX(Canvas canvas, Paint basePaint) {
        for (int i = 0; i < axisDevidedSizeX-1; i++) {
            canvas.drawLine(originalX+i*segmentX,originalY,originalX+i*segmentX,originalY-dip2px(2),basePaint);
        }
    }

    /**        Explain : 绘制Y轴刻度线
     * @author LiXiang */
    @Override
    protected void drawAxisScaleY(Canvas canvas, Paint basePaint) {
        for (int i = 0; i < axisDevidedSizeY-1; i++) {
            canvas.drawLine(originalX,originalY-i*segmentY,originalX+dip2px(2),originalY-i*segmentY,basePaint);
        }
    }

    /**        Explain : 绘制X轴刻度值
    * @author LiXiang */
    @Override
    protected void drawAxisScaleValuveX(Canvas canvas, Paint basePaint) {
        for (int i = 1; i < axisDevidedSizeX+1; i++) {
            canvas.drawText(i+"",originalX+i*segmentX-segmentX/2,originalY+dip2px(15),basePaint);
        }

    }

    /**        Explain : 绘制Y轴刻度值
     * @author LiXiang */
    @Override
    protected void drawAxisScaleXValuveY(Canvas canvas, Paint basePaint) {
        for (int i = 1; i < axisDevidedSizeY+1; i++) {
            canvas.drawText(i+"",originalX-dip2px(10),originalY-i*segmentY+segmentX/2,basePaint);
        }
    }





    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //当前动画没有加载完成则不进行触摸监听
        if (mAnimatedValue != mAnimatedValueMax) {
            return false;
        }
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                if (mAnimatedValue == mAnimatedValueMax) {//当动画效果展示完成以后才可以进行触摸效果
                    onTouch = true;
                    moveX = event.getX();
                    moveY = event.getY();

                }
                break;
            case MotionEvent.ACTION_MOVE:
                    moveX = event.getX();
                    moveY = event.getY();

                    //触摸效果只能在当前自定义视图之内
                    if (moveX >= getLeft() && moveX <= getRight() && moveY >= getTop() && moveY <= getBottom()) {//设定范围边界
                        getParent().requestDisallowInterceptTouchEvent(true);//绘制区域内 允许子view响应触摸事件
                        invalidate();
                    } else {//当滑出范围边界就对视图延迟一秒后刷新
                        postDelayedInvalidate();
                    }
                break;
            case MotionEvent.ACTION_UP:
                moveX = event.getX();
                moveY = event.getY();
                postDelayedInvalidate();//手指抬起后就对视图延迟一秒后刷新
                onTouch = false;
                break;
        }
//        返回值由当前是否正在滑动决定
        return onTouch == true ? true : super.onTouchEvent(event);
    }

    private void drawOnTouch(Canvas canvas) {

        //这里获取int整型数值 ,刚好与数据源的索引吻合 ,如果数据长度过短,可能会索引越界,可以对index进行判断
        int index = (int) ((moveX - originalX) / segmentX);
        if (index >= data.length) index = data.length - 1;


        float x0 = originalX + index * segmentX;


        float y = data[index][0];
        float y0 = originalY - y * segmentY;
        //每次绘制之前都需重新设置渐变属性
        touchPaint.setColor(ContextCompat.getColor(getContext(), R.color.alpha));
        //画矩形
        canvas.drawRect(x0, y0, x0 + segmentX -dip2px(2), originalY, touchPaint);

        Paint p = new Paint(touchPaint);
        p.setTextSize(dip2px(15));
        p.setColor(getColumnColor(touchPaint,data[index]));

        float x1 = x0 + 0.5f * segmentX;
        canvas.drawText(y + "公里", x1, y0 - defaultPadding / 2, p);

    }



}

基础类 BaseColumnChart.java

package com.example.ocean.charts.column;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Typeface;
import android.text.TextUtils;
import android.util.AttributeSet;

import com.example.ocean.R;
import com.example.ocean.charts.BaseChart;

import androidx.annotation.Nullable;

public abstract class BaseColumnChart extends BaseChart {

    protected int axisDevidedSizeX;//轴间距数量
    protected int axisDevidedSizeY;//轴间距数量
    protected int axisTextColor;//刻度值颜色
    protected int axisTextSize;//刻度值字体大小
    protected String xAxisNmae;//X轴名字
    protected String yAxisNmae;//Y轴名字
    protected String graphTitle;//图表名字
    protected int segmentX;//轴间距
    protected int segmentY;//轴间距

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

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

        public BaseColumnChart(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            this(context, attrs, defStyleAttr,0);
        }

        public BaseColumnChart(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
            super(context, attrs, defStyleAttr, defStyleRes);
            /**        Explain : 获取样式
             * @author LiXiang */
            TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.GraphStyle);
            graphTitle = typedArray.getString(R.styleable.GraphStyle_graphTitle);
            xAxisNmae = typedArray.getString(R.styleable.GraphStyle_xAxisNmae);
            yAxisNmae = typedArray.getString(R.styleable.GraphStyle_yAxisNmae);
            axisDevidedSizeX = typedArray.getInteger(R.styleable.GraphStyle_axisDevidedSizeX,10);
            axisDevidedSizeY = typedArray.getInteger(R.styleable.GraphStyle_axisDevidedSizeY,20);
            axisTextColor = typedArray.getColor(R.styleable.GraphStyle_axisTextColor,Color.GRAY);
            axisTextSize= (int) typedArray.getDimension(R.styleable.GraphStyle_axisTextSize,4);
            axisTextSize = dip2px(axisTextSize);
            if (typedArray != null) {
                typedArray.recycle();
            }

            //初始化基础画笔
            basePaint.setTextSize(dip2px(12));//设置标题大小
            basePaint.setTextAlign(Paint.Align.CENTER);//标题对其格式
            basePaint.setStrokeWidth(dip2px(0.5f));//画笔粗细
            basePaint.setStrokeCap(Paint.Cap.ROUND);//设置圆角
            basePaint.setColor(Color.GRAY);//设置画笔颜色
            Typeface font0 = Typeface.create(Typeface.SANS_SERIF, Typeface.DEFAULT_BOLD.getStyle());
            basePaint.setTypeface(font0);

        }

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

        //获取刻度的宽度
        segmentX = (endX -  originalX)/axisDevidedSizeX ;
        segmentY = (originalY -  endY )/axisDevidedSizeY;

        drawTitle(canvas,basePaint);//绘制标题
        drawAxisArrowX(canvas,basePaint);//绘制X轴箭头
        drawAxisArrowY(canvas,basePaint);//绘制Y轴箭头

        //以下类交给子类处理
        drawAxisX(canvas,basePaint);//绘制X轴
        drawAxisY(canvas,basePaint);//绘制Y轴
        drawAxisScaleX(canvas,basePaint);//绘制X轴刻度
        drawAxisScaleY(canvas,basePaint);//绘制Y轴刻度
        drawAxisScaleValuveX(canvas,basePaint);//绘制X轴刻度值
        drawAxisScaleXValuveY(canvas,basePaint);//绘制Y轴刻度值
        drawColumn(canvas,basePaint);
    }


    /**        Explain : 绘制X轴箭头
    * @author LiXiang */
    private void drawAxisArrowX(Canvas canvas, Paint basePaint) {
        Path path = new Path();
        path.moveTo(endX+dip2px(2),originalY);
        path.lineTo(endX,originalY + dip2px(2));
        path.lineTo(endX,originalY - dip2px(2));
        path.close();
        canvas.drawPath(path,basePaint);
        canvas.drawText(xAxisNmae,endX+dip2px(3),originalY+dip2px(15),basePaint);
    }

    /**        Explain : 绘制Y轴箭头
     * @author LiXiang */
    private void drawAxisArrowY(Canvas canvas, Paint basePaint) {
        Path path = new Path();
        path.moveTo(originalX,endY - dip2px(2));
        path.lineTo(originalX+ dip2px(2),endY );
        path.lineTo(originalX- dip2px(2),endY );
        path.close();
        canvas.drawPath(path,basePaint);
        canvas.drawText(yAxisNmae,originalX+defaultPadding/3,endY - dip2px(3),basePaint);
    }

    /**        Explain : 设置标题
     * @author LiXiang
     * @param canvas
     * @param basePaint  */
    private void drawTitle(Canvas canvas, Paint basePaint) {
        if (!TextUtils.isEmpty(graphTitle)) {
            Paint titlePaint = new Paint(basePaint);
            titlePaint.setFakeBoldText(true);//设置字体加粗
            titlePaint.setTextSize(axisTextSize);
            titlePaint.setColor(axisTextColor);
            canvas.drawText(graphTitle,getWidth() / 2,originalY+dip2px(35),titlePaint);
        }
    }

    protected abstract void drawColumn(Canvas canvas, Paint basePaint);

    protected abstract void drawAxisScaleXValuveY(Canvas canvas, Paint basePaint);

    protected abstract void drawAxisScaleValuveX(Canvas canvas, Paint basePaint);

    protected abstract void drawAxisScaleY(Canvas canvas, Paint basePaint);

    protected abstract void drawAxisScaleX(Canvas canvas, Paint basePaint);

    protected abstract void drawAxisY(Canvas canvas, Paint basePaint);

    protected abstract void drawAxisX(Canvas canvas, Paint basePaint);

}


基础类 BaseChart.java
package com.example.ocean.charts;

import android.animation.Animator;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Paint;
import android.os.Build;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.View;

import com.example.ocean.R;

import androidx.annotation.Nullable;

public abstract class BaseChart extends View {

    protected final Context mContext;

    protected Paint basePaint;//基础画笔
    protected int originalX ;//X轴起点
    protected int originalY ;//Y轴起点
    protected int endX ;//X轴终点
    protected int endY ;//Y轴终点
    protected int defaultPadding = dip2px(30);//默认内边距
    protected ValueAnimator valueAnimator;//动画
    protected boolean starting = false;//是否正在执行动画
    protected float mAnimatedValueMax = 1;//动画最大值
    protected float mAnimatedValue = 0;//动画当前值
    protected boolean onTouch = false;//是否正在触摸

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

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

    public BaseChart(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr,0);
    }

    public BaseChart(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        mContext = context;
        initBasePaint();
    }

    /**        Explain : 初始化基础画笔
    * @author LiXiang */
    private void initBasePaint() {
        if (basePaint == null) {
        basePaint = new Paint();
        basePaint.setAntiAlias(true);//抗锯齿
        basePaint.setDither(true);//防抖动
        }
    }


    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        originalX = getPaddingLeft() + defaultPadding;
        originalY = getMeasuredHeight() - getPaddingBottom() - 2*defaultPadding;
        endX = getSpaceX()>getDefaultSpaceX()?getSpaceX():getDefaultSpaceX();
        endY = getSpaceY()>getDefaultSpaceY()?getSpaceY():getDefaultSpaceY();

    }

    protected int getDefaultSpaceY() { return getPaddingTop() + defaultPadding; }

    protected int getDefaultSpaceX() { return getMeasuredWidth() - getPaddingRight() - defaultPadding; }

    protected abstract int getSpaceY();

    abstract protected int getSpaceX();


    protected int dip2px(float dipValue) {
        final float scale = Resources.getSystem().getDisplayMetrics().density;
        return (int) (dipValue * scale + 0.5f);
    }



    public void start() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            startAnimator();
        } else {
            this.post(new Runnable() {//可以避免页面未初始化完成造成的 空白
                @Override
                public void run() {
                    startAnimator();
                }
            });
        }
    }

    private void startAnimator() {
        if ( starting) {//只能绘制一次 或者正在绘制过程中的话不能再次绘制
            return;
        }
        starting = true;
        valueAnimator = ValueAnimator.ofFloat(0, mAnimatedValueMax).setDuration(5000);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                mAnimatedValue = (float) valueAnimator.getAnimatedValue();
                if (starting) {
                    System.out.println("mAnimatedValue:" + mAnimatedValue);
                    invalidateOtherData();
                    invalidate();
                }
            }
        });
        valueAnimator.addListener(new Animator.AnimatorListener() {

            @Override
            public void onAnimationStart(Animator animation) {

            }

            @Override
            public void onAnimationEnd(Animator animation) {
                starting = false;
            }

            @Override
            public void onAnimationCancel(Animator animation) {

            }

            @Override
            public void onAnimationRepeat(Animator animation) {

            }
        });
        valueAnimator.start();
    }

    protected  void invalidateOtherData(){};

    public void postDelayedInvalidate() {
        onTouch = false;//置为响应触摸操作的绘制
        getParent().requestDisallowInterceptTouchEvent(false);//离开绘制区域,拦截触摸事件
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                invalidate();
            }
        }, 1000);
    }

    @SuppressLint("HandlerLeak")
    private Handler handler = new Handler();

}

Color
    <color name="blue_rgba_24_261_255">#17A1FF</color>
    <color name="orange">#FFBB4D</color>
    <color name="red_2">#F76567</color>
    <color name="main_green">#7EC501</color>
style样式属性
 <!--    柱状图图表样式-->
    <declare-styleable name="GraphStyle">
        <attr name="graphTitle" format="string"/>
        <attr name="xAxisNmae" format="string"/>
        <attr name="yAxisNmae" format="string"/>
        <attr name="axisTextSize" format="dimension|integer"/>
        <attr name="axisTextColor" format="color|integer"/>
        <attr name="axisDevidedSizeX" format="integer"/>
        <attr name="axisDevidedSizeY" format="integer"/>
    </declare-styleable>

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值