虽然开源了,但是转载本文需要经过本人的同意,谢谢!
直接上图
想达到下面的效果,怎么去处理?下面一步步去分析。
整体是5个类似的圆柱进行的一个动画效果。第一时间想的比较复制,由于给到的设计图每个圆柱下面还有一个TextView,我是在画布里面把5个圆柱都画出来,包括下面的文字处理,比较复杂,走了不少弯路,老大后来说直接画一个,结合RecycleView就能实现,试了一下,效果不错。
现在就抛开动画效果,对单一的一个进行分析。我们可以看出,静态的图片当中是有几个部分。1.圆柱2.圆环,包括虚线部分,还有实线部分,3.圆。其中圆柱部分可以看成一个矩形,上边的一个椭圆,下面部分一个半圆。 , (画的比较丑,将就将就)。下面就是基础图形的绘制,如果有不清楚的,可以参考下面这个地址,我觉得写的非常好。 https://github.com/GcsSloop/AndroidNote/blob/master/CustomView/Advance/%5B02%5DCanvas_BasicGraphics.md
下面直接上代码:
public class BarGraphView extends View {
private float totalWeight;
private Paint dotLinePaint;
private Paint circleLinePaint;
private Paint circleFillPaint;
private Paint columnPaint;
private Paint columnBottomPaint;
private Paint columnTopPaint;
private Paint numberPaint;
private int barGraphViewWidth = 1080;
private int barGraphViewHeight = 1920;
private int ovalWidth = 120;
private int ovalHeight = 60;
private int outerInnerWidth = 10;
private int outerInnerHeight = 10;
private int numberTextSize = 30;
private int maxHeight;
private float maxNumber = 5000;
private int startColor = 0xD900D061;
private int endColor = 0xFF2DE526;
private int colorTransEnd = 0xE92DE526;
private int numberTextColor = Color.WHITE;
private int marginBottom = 30;
private int lineStrokeWidth = 3;
private float progress;
private boolean hasAnimation;
public BarGraphView(Context context) {
this(context, null);
}
public BarGraphView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public BarGraphView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initPaint();
}
private void initPaint() {
dotLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
dotLinePaint.setStyle(Paint.Style.STROKE);
dotLinePaint.setStrokeWidth(2f);
//先画长度为3的实线,再间隔长度为2的空白
dotLinePaint.setPathEffect(new DashPathEffect(new float[]{3, 2}, 0));
columnPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
columnPaint.setStrokeWidth(20f);
columnPaint.setStyle(Paint.Style.FILL);
columnBottomPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
columnBottomPaint.setStyle(Paint.Style.FILL);
columnTopPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
columnTopPaint.setStrokeWidth(20f);
columnTopPaint.setStyle(Paint.Style.FILL);
circleLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
circleLinePaint.setStrokeWidth(lineStrokeWidth);
circleLinePaint.setStyle(Paint.Style.STROKE);
circleFillPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
circleFillPaint.setStrokeWidth(lineStrokeWidth);
circleFillPaint.setStyle(Paint.Style.FILL_AND_STROKE);
numberPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
numberPaint.setStyle(Paint.Style.FILL);
numberPaint.setTextAlign(Paint.Align.CENTER);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
canvas.translate(lineStrokeWidth, barGraphViewHeight - ovalHeight - marginBottom);
float number = totalWeight;
float currentHeight = number / maxNumber * maxHeight;
drawColumn(canvas, currentHeight * progress, currentHeight);
drawNumberText(canvas, totalWeight, currentHeight * progress + ovalHeight / 2);
canvas.restore();
}
private void drawColumn(Canvas canvas, float currentHeight, float finalHeight) {
columnTopPaint.setColor(endColor);
columnBottomPaint.setColor(startColor);
dotLinePaint.setColor(endColor);
circleLinePaint.setColor(endColor);
circleFillPaint.setColor(endColor);
columnPaint.setShader(new LinearGradient(ovalWidth / 2,
ovalHeight / 2, ovalWidth / 2,
-currentHeight + ovalHeight / 2, new int[]{startColor,
colorTransEnd}, new float[]{0.2f, 0.8f}, LinearGradient.TileMode.CLAMP));
RectF dotRectF = new RectF(outerInnerWidth, outerInnerHeight, ovalWidth - outerInnerWidth, ovalHeight - outerInnerHeight);
canvas.drawOval(dotRectF, dotLinePaint);
//从某个角度开始,扫过多少角度
RectF ArcRectF = new RectF(0, 0, ovalWidth, ovalHeight);
canvas.drawArc(ArcRectF, 60, -310, false, circleLinePaint);
/*椭圆上的特殊点 坐标
13*pow(x,2)=4*pow(b,2)
以0,0为原点计算值*/
float x = (float) Math.sqrt(Math.pow(ovalHeight, 2) / 13);
float y = (float) (Math.sqrt(Math.pow(ovalHeight, 2) / 13) * Math.sqrt(3));
x = ovalWidth / 2 - x;
y = ovalHeight / 2 + y;
canvas.drawCircle(x, y, 5, circleFillPaint);
canvas.drawRect(outerInnerWidth * 2, -currentHeight + ovalHeight / 2, ovalWidth - outerInnerWidth * 2, ovalHeight / 2, columnPaint);
RectF rectFBottom = new RectF(outerInnerWidth * 2, outerInnerHeight * 2, ovalWidth - outerInnerWidth * 2, ovalHeight - outerInnerHeight * 2);
canvas.drawArc(rectFBottom, 0, 180, false, columnBottomPaint);
RectF rectFTop = new RectF(outerInnerWidth * 2, outerInnerHeight * 2 - currentHeight, ovalWidth - outerInnerWidth * 2, ovalHeight - outerInnerHeight * 2 - currentHeight);
canvas.drawOval(rectFTop, columnTopPaint);
}
/**
* 画文字数量
*
* @param numberText
*/
private void drawNumberText(Canvas canvas, float numberText, float columnHeightWithoutBottom) {
numberPaint.setColor(numberTextColor);
RectF numberRectF = new RectF(0, -columnHeightWithoutBottom + ovalHeight / 2, ovalWidth, -columnHeightWithoutBottom + ovalHeight / 2);
Paint.FontMetrics fontMetrics = numberPaint.getFontMetrics();
float distance = (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom;
float baseline = numberRectF.centerY() + distance;
float currentData = numberText / 1000 * progress;
DecimalFormat decimalFormat = new DecimalFormat("0.000");
String data = decimalFormat.format(currentData);
if (progress == 1) {
canvas.drawText(trimDotEndZero(data), numberRectF.centerX(), baseline, numberPaint);
} else {
canvas.drawText(data, numberRectF.centerX(), baseline, numberPaint);
}
}
@Override
protected void onMeasure(int widthSpec, int heightSpec) {
setMeasuredDimension(barGraphViewWidth, barGraphViewHeight);
}
/**
* 设置虚线样式
*
* @param dashPathEffect
*/
public BarGraphView setDotLineStyle(DashPathEffect dashPathEffect) {
dotLinePaint.setPathEffect(dashPathEffect);
return this;
}
public BarGraphView setMaxNumber(float maxNumber) {
this.maxNumber = maxNumber;
return this;
}
public void setLineStrokeWidth(int lineStrokeWidth) {
this.lineStrokeWidth = lineStrokeWidth;
}
/**
* 必须设置
*
* @param barGraphViewHeight
* @return
*/
public BarGraphView setBarGraphViewHeight(int barGraphViewHeight) {
this.barGraphViewHeight = barGraphViewHeight;
return this;
}
/**
* 长宽比2:1方便计算
* ovalHeight必须为2的整数,不然会有1个像素误差
* 那么计算值的时候ovalWidth为4的整数倍
*
* @param ovalWidth
* @return
*/
public BarGraphView setOvalWidthAndHeight(int ovalWidth) {
this.ovalWidth = ovalWidth / 4 * 4;
this.ovalHeight = this.ovalWidth / 2;
return this;
}
public BarGraphView setNumberTextSize(int numberTextSize) {
this.numberTextSize = numberTextSize;
numberPaint.setTextSize(numberTextSize);
return this;
}
public BarGraphView hasAnimation(boolean b) {
hasAnimation = b;
return this;
}
public BarGraphView setStartColor(int startColor) {
this.startColor = startColor;
return this;
}
public BarGraphView setEndColor(int endColor) {
this.endColor = endColor;
return this;
}
public BarGraphView setColorTransEnd(int colorTransEnd) {
this.colorTransEnd = colorTransEnd;
return this;
}
public BarGraphView setNumberTextColor(int numberTextColor) {
this.numberTextColor = numberTextColor;
numberPaint.setColor(this.numberTextColor);
return this;
}
/**
* 最终调用方法
*/
public void setTotalWeight(float totalWeight) {
this.totalWeight = totalWeight;
progress = 0;
if (barGraphViewHeight == 0) {
throw new RuntimeException("barGraphViewHeight can not be zero");
}
barGraphViewWidth = ovalWidth + lineStrokeWidth * 2;
maxHeight = barGraphViewHeight - ovalHeight - numberTextSize - marginBottom;
if (hasAnimation) {
ValueAnimator valueAnimator = ValueAnimator.ofFloat(progress, 1);
valueAnimator.setDuration(2000);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator1) {
float value = (float) valueAnimator1.getAnimatedValue();
progress = value;
invalidate();
}
});
valueAnimator.setInterpolator(new DecelerateInterpolator());
valueAnimator.start();
}
}
private String trimDotEndZero(String oriStr) {
if (oriStr.indexOf(".") > 0) {
//去掉多余的0
oriStr = oriStr.replaceAll("0+?$", "");
//如最后一位是.则去掉
oriStr = oriStr.replaceAll("[.]$", "");
}
return oriStr;
}
其中加了颜色渐变,startColor,endColor加以设置。具体的XML,还有RecycleView的Adapter关键代码如下
holder.barGraphView
.setNumberTextSize(UnitFormatUtil.dp2px(12))
.setOvalWidthAndHeight(UnitFormatUtil.dp2px(50))
.setBarGraphViewHeight(UnitFormatUtil.dp2px(165))
.setStartColor(colorStartArray[position])
.setEndColor(colorEndArray[position])
.setColorTransEnd(colorTransEndArray[position])
.setMaxNumber(maxNumber)
.hasAnimation(true)
.setTotalWeight(supplierBean.getTotalWeight());
接下来看下增加渐变和结合textView的最终样子
写的一般,仅供参考。