使用
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>