先上图
还是有些需要完善的,比如每个柱状图的点击事件,做的有点乱;画柱子画线都比较简单,复杂一点的就是左右滑动;还有画Y轴刻度,还有每个柱子Top的计算有点麻烦;下面贴上所有代码:
package com.ex.testview;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.widget.Scroller;
import androidx.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
public class BarChartView extends View {
private Paint axisPaint;//画L线
private int axisWidth=DensityUtil.dip2px(getContext(),2);//线的宽度
private int axisColor = Color.LTGRAY;//线的颜色
private int paddingLeft = DensityUtil.dip2px(getContext(),20);
private int paddingTop = DensityUtil.dip2px(getContext(),20);
private int paddingRight = DensityUtil.dip2px(getContext(),20);
private int paddingBottom = DensityUtil.dip2px(getContext(),20);
private int xTextHeight = DensityUtil.dip2px(getContext(),20);//X轴底部文字高度
private TextPaint axisTextPaint;//画坐标轴的文字
private int axisTextSize=DensityUtil.dip2px(getContext(),12);//文字大小
private int axisTextColor = Color.BLACK;
private TextPaint axisXTextPaint;//画坐标轴的文字
private int axisXTextSize=DensityUtil.dip2px(getContext(),10);//文字大小
private int axisXTextColor = Color.BLACK;
private TextPaint barTextPaint;//画坐标轴的文字
private int barTextSize=DensityUtil.dip2px(getContext(),10);//文字大小
private int barTextColor = Color.BLACK;
private int barTextHeight = DensityUtil.dip2px(getContext(),15);
public void setBarTextColor(int barTextColor) {
this.barTextColor = barTextColor;
}
private int barWidth = DensityUtil.dip2px(getContext(),40);//柱子宽度
private int barSpace = DensityUtil.dip2px(getContext(),15);//柱子间距
//设置柱子宽度
public void setBarWidth(int barWidth) {
this.barWidth = barWidth;
}
private Paint barPaint;//画柱子
private int barColor = Color.GREEN;//柱子颜色
//设置柱子颜色
public void setBarColor(int barColor) {
this.barColor = barColor;
}
private Paint LegendPaint;
private TextPaint legendTextPaint;
private int legendTextColor = Color.BLACK;
private int legendTextSize = DensityUtil.dip2px(getContext(),10);
private List<Float> yList;//y轴数据
private List<String> xList;
private int maxOffset;
private float lastX;
private VelocityTracker tracker;
private Scroller scroller;
private boolean isLegend;
private String legendText;
//设置数据
public void setChartData(List<String> xList,List<Float> yList,boolean isLegend,String legendText){
this.yList = yList;
this.xList = xList;
this.isLegend = isLegend;
this.legendText = legendText;
invalidate();
}
//得到Y轴最大值
private float maxYData(List<Float> lists){
HashSet<Float> hashSet = new HashSet<>(lists);
List<Float> list = new ArrayList<>(hashSet);
Collections.sort(list);//升序
return list.get(list.size()-1);
}
public BarChartView(Context context) {
this(context,null);
}
public BarChartView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public BarChartView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
axisPaint = new Paint();
axisPaint.setStrokeWidth(axisWidth);
axisPaint.setColor(axisColor);
axisPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
axisPaint.setAntiAlias(true);
axisTextPaint = new TextPaint();
axisTextPaint.setAntiAlias(true);
axisTextPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
axisTextPaint.setTextSize(axisTextSize);
axisTextPaint.setColor(axisTextColor);
axisXTextPaint = new TextPaint();
axisXTextPaint.setAntiAlias(true);
axisXTextPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
axisXTextPaint.setTextSize(axisXTextSize);
axisXTextPaint.setColor(axisXTextColor);
barTextPaint = new TextPaint();
barTextPaint.setAntiAlias(true);
barTextPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
barTextPaint.setTextSize(barTextSize);
barTextPaint.setColor(barTextColor);
barPaint = new Paint();
barPaint.setColor(barColor);
barPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
barPaint.setAntiAlias(true);
LegendPaint = new Paint();
LegendPaint.setColor(barColor);
LegendPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
LegendPaint.setAntiAlias(true);
legendTextPaint = new TextPaint();
legendTextPaint.setAntiAlias(true);
legendTextPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
legendTextPaint.setTextSize(legendTextSize);
legendTextPaint.setColor(legendTextColor);
scroller = new Scroller(getContext());
}
private float ageY;//平均没等分是多少
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onDraw(Canvas canvas) {
int width = getMeasuredWidth();
int height = getMeasuredHeight();
if (yList==null || yList.size()==0)return;
int maxTextWidth = (int) axisTextPaint.measureText(maxYData(yList)+"0");
int left = paddingLeft+axisWidth/2+maxTextWidth;//paddingLeft+线的宽度/2+Y轴值最大宽度
int right = width - paddingRight;
int top = paddingTop;
int bottom = height-paddingBottom-axisWidth/2;//预留X轴文字高度
canvas.save();
canvas.translate(getScrollX(),0);
//画Legend
if (isLegend){
Rect rect = new Rect();
rect.left = (width-left-paddingRight)/2;
rect.top= top;
rect.right=rect.left+20;
rect.bottom= top+20;
canvas.drawRect(rect,LegendPaint);
Paint.FontMetricsInt LegendmetricsInt = legendTextPaint.getFontMetricsInt();
int dyLegend = (LegendmetricsInt.bottom-LegendmetricsInt.top)/2-LegendmetricsInt.bottom;
float ydyLegend = dyLegend+rect.top+10;
canvas.drawText(legendText,rect.right+20,ydyLegend,legendTextPaint); //
}
//画Y轴值
float maxdata = maxYData(yList);//最大值
float agedata = maxdata/8;//平均每份
ageY = (height-paddingBottom-paddingTop)/8;//线的每等分
for (int i=0;i<8;i++){
if (i==0){
canvas.drawText("0",paddingLeft,height-paddingBottom-xTextHeight,axisTextPaint);//画Y轴刻度
}else {
Paint.FontMetricsInt metricsInt = axisTextPaint.getFontMetricsInt();
int dy = (metricsInt.bottom-metricsInt.top)/2-metricsInt.bottom;
float y = dy+(height-paddingBottom)-ageY*i-xTextHeight;
canvas.drawText(""+i*agedata*1.2,paddingLeft,y,axisTextPaint); //画Y轴刻度
}
}
canvas.drawLine(left,top,left,bottom-xTextHeight,axisPaint);//画Y轴
canvas.drawLine(left,bottom-xTextHeight,right,bottom-xTextHeight,axisPaint);//画X轴
for (int i=0;i<yList.size();i++){
int x0 = left+(barSpace+barWidth)*i+barSpace+getScrollX();
int x1 = x0+barWidth;
if (x1<=left || x0>=right){
continue;
}
float top0 =(float)( height-paddingBottom-(yList.get(i)*ageY/(agedata*1.2))-xTextHeight);
canvas.clipRect(left,top,right,bottom);//剪切柱状图区域
canvas.drawRect(x0,top0,x1,bottom-xTextHeight,barPaint);//画柱状图
//底部X轴文字
String xtext = xList.get(i);
float xtextwidth = axisXTextPaint.measureText(xtext);//X文字宽度
Paint.FontMetricsInt metricsInt = axisXTextPaint.getFontMetricsInt();
int dy = (metricsInt.bottom-metricsInt.top)/2-metricsInt.bottom;
float y = height-xTextHeight-dy;
canvas.drawText(xtext,x0+(barWidth-xtextwidth)/2f,y,axisXTextPaint);
//柱状图上加文字
String ytext = String.valueOf(yList.get(i));
float ytextwidth = barTextPaint.measureText(ytext);
canvas.drawText(ytext,x0+(barWidth-ytextwidth)/2f,top0-barTextHeight,barTextPaint);
}
maxOffset = (yList.size() * (barWidth+barSpace)-(getMeasuredWidth()-paddingRight-paddingLeft-maxTextWidth));//计算可滑动距离
if (maxOffset<0){
maxOffset=0;
}
canvas.restore();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
if (tracker!=null){
tracker.clear();
}
lastX = event.getX();
break;
case MotionEvent.ACTION_MOVE:
if (tracker==null){
tracker = VelocityTracker.obtain();
}
if (tracker!=null){
tracker.addMovement(event);
}
int sX= getScrollX();
sX += event.getX()-lastX;
sX = Math.max(Math.min(0,sX),-maxOffset);
scrollTo(sX,0);
lastX=event.getX();
break;
case MotionEvent.ACTION_UP:
setTracker();
break;
case MotionEvent.ACTION_CANCEL:
setTracker();
break;
}
invalidate();
return true;
}
private void setTracker(){
if (tracker!=null){
tracker.computeCurrentVelocity(1000);
scroller.forceFinished(true);
scroller.fling(getScrollX(),0,(int) (0.5*tracker.getXVelocity()),0,-maxOffset,0,0,0);
tracker.recycle();
}
tracker=null;
}
@Override
public void computeScroll() {
if (scroller.computeScrollOffset()){
scrollTo(scroller.getCurrX(),0);
postInvalidate();
}
}
}
这是柱状图代码;还有一部分工具类代码:
package com.ex.testview;
import android.content.Context;
public class DensityUtil {
/**
* 根据手机的分辨率从 dp 的单位 转成为 px(像素)
*/
public static int dip2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
/**
* 根据手机的分辨率从 px(像素) 的单位 转成为 dp
*/
public static int px2dip(Context context, float pxValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f);
}
}
然后就是使用了:
chartView = findViewById(R.id.chartView);
List<Float> yList = new ArrayList<>();
yList.add(10f);
yList.add(20f);
yList.add(30f);
yList.add(40f);
yList.add(50f);
yList.add(60f);
yList.add(70f);
yList.add(80f);
yList.add(70f);
yList.add(60f);
yList.add(50f);
yList.add(40f);
ArrayList<String> type = new ArrayList<String>();
type.add("支付宝");
type.add("微信");
type.add("QQ");
type.add("微博");
type.add("空间");
type.add("新浪");
type.add("网易");
type.add("360");
type.add("暴雪");
type.add("小米");
type.add("苹果");
type.add("华为");
chartView.setChartData(type,yList,true,"营业额");
整体都是这样,菜鸟一枚,欢迎交流讨论!
在此感谢潘帅大神的指导!