上面的布局,我们自定义了HistogramView集成ViewGroup。
public class HistogramView extends ViewGroup {}
那我们应该在哪里获取HistogramView 中的控件呢?
答案是这个方法。
@Override
protected void onFinishInflate() {
super.onFinishInflate();
tvLeft = findViewById(R.id.tvLeft);
tvRight = findViewById(R.id.tvRight);
viewBg = findViewById(R.id.viewBg);
}
注意这些方法执行的先后顺序是
构造方法————>onFinishInflate——————>setHeightList————>onMeassure(若干次)————>onLayout————>onDraw()
setHeightList是在在Activity中调用的方法。 histogramView.setHeightList(histogramDataBeanList, 100.7f);

下面是柱状图全部代码。可以看看
package cn.xiaozhibo.com.kit.widgets;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import cn.xiaozhibo.com.R;
import cn.xiaozhibo.com.kit.bean.HistogramDataBean;
import cn.xiaozhibo.com.kit.third.utils.UIUtils;
public class HistogramView extends ViewGroup {
//switchButton是否在右侧
private boolean isRightNumberAndPeriod;
private Context mContext;
// 左侧文字的画笔
private Paint textPaintLeft;
// 底侧文字的画笔
private Paint textPaintDown;
private List heightList;
// 最高的柱子,需要取整数
private int heightest;
// 需要绘制的柱子的数量
private int columnCount;
//每个柱子连同它左右的空间的距离
private int spaceWithLeftRight;
// 无透明度的画笔
private Paint abovePaint;
// 有透明度的画笔
private Paint downPaint;
// 左侧文字的大小
private int leftTextSize;
// 上侧文字的大小
private int topTextSize;
// 底侧文字的大小
private int bottomTextSize;
// 一个刻度的像素高度
private int markSizePx;
// 一个刻度的高度
private int markSize;
// 单位场次的画笔
private Paint textPaintTop;
private Paint textPaintTopNote;
// 底部需要绘制的数据
private List bottomList = new ArrayList<>();
// 顶部要绘制的数据
private String topUnit;
private String topUnitNote;
private Rect topUnitRect;
private Rect leftTextRect;
// 顶部两个控件的y轴
private int topRegion;
private TextView tvLeft;
private TextView tvRight;
private View viewBg;
private final int animationTime = 2000;
private ValueAnimator valueAnimatorColorFromBlackToWhite;
private ValueAnimator valueAnimatorColorFromWhiteToBlack;
private AnimatorSet animatorSetLeftClicked;
private AnimatorSet animatorSetRightClicked;
private ValueAnimator valueAnimatorToLeft;
private ValueAnimator valueAnimatorToRight;
public HistogramView(Context context) {
this(context, null);
}
public HistogramView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public HistogramView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public HistogramView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initView(context, attrs, defStyleAttr, defStyleRes);
}
private void initView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
this.mContext = context;
setBackgroundResource(R.drawable.shape_white_5);
// 左侧文字的画笔
textPaintLeft = new Paint();
textPaintLeft.setAntiAlias(true);
textPaintLeft.setTextAlign(Paint.Align.RIGHT);
textPaintLeft.setColor(ContextCompat.getColor(mContext, R.color.gray56));
textPaintLeft.setTextSize(UIUtils.dip2px(mContext, 9));
// 底侧文字的画笔
textPaintDown = new Paint();
textPaintDown.setAntiAlias(true);
textPaintDown.setTextAlign(Paint.Align.CENTER);
textPaintDown.setColor(ContextCompat.getColor(mContext, R.color.gray56));
textPaintDown.setTextSize(UIUtils.dip2px(mContext, 9));
// 顶侧文字的画笔
textPaintTop = new Paint();
textPaintTop.setAntiAlias(true);
textPaintTop.setTextAlign(Paint.Align.LEFT);
textPaintTop.setColor(ContextCompat.getColor(mContext, R.color.load_more_text));
textPaintTop.setTextSize(UIUtils.dip2px(mContext, 10));
// 顶侧文字的注释画笔
textPaintTopNote = new Paint();
textPaintTopNote.setAntiAlias(true);
textPaintTopNote.setTextAlign(Paint.Align.LEFT);
textPaintTopNote.setColor(ContextCompat.getColor(mContext, R.color.gray56));
textPaintTopNote.setTextSize(UIUtils.dip2px(mContext, 10));
// 不带透明度的画笔
abovePaint = new Paint();
abovePaint.setAntiAlias(true);
// 设置笔帽
abovePaint.setStrokeCap(Paint.Cap.BUTT);
abovePaint.setStrokeWidth(UIUtils.dip2px(mContext, 20));
abovePaint.setColor(ContextCompat.getColor(mContext, R.color.orange5));
// 带透明度的画笔
downPaint = new Paint();
downPaint.setAntiAlias(true);
//设置笔帽
downPaint.setStrokeCap(Paint.Cap.BUTT);
downPaint.setStrokeWidth(UIUtils.dip2px(mContext, 20));
downPaint.setColor(ContextCompat.getColor(mContext, R.color.orangeWithAlpha));
leftTextSize = UIUtils.dip2px(mContext, 31);
topTextSize = UIUtils.dip2px(mContext, 28);
bottomTextSize = UIUtils.dip2px(mContext, 25);
topUnitRect = new Rect();
leftTextRect = new Rect();
}
private void initViews() {
tvLeft = findViewById(R.id.tvLeft);
tvRight = findViewById(R.id.tvRight);
viewBg = findViewById(R.id.viewBg);
tvLeft.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (isRightNumberAndPeriod) {
animatorSetLeftClicked.start();
}
}
});
tvRight.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!isRightNumberAndPeriod) {
animatorSetRightClicked.start();
}
}
});
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(UIUtils.dip2px(mContext, 355), UIUtils.dip2px(mContext, 255));
if (heightList == null && heightList.isEmpty()) {
return;
}
View viewChild = getChildAt(0);
measureChild(viewChild, widthMeasureSpec, heightMeasureSpec);
this.columnCount = heightList.size();
this.spaceWithLeftRight = (getMeasuredWidth() - leftTextSize) / columnCount;
this.markSizePx = (getMeasuredHeight() - topTextSize - bottomTextSize) / 10;
this.markSize = this.heightest / 10;
// 求文字的高度,文字随便写
textPaintLeft.getTextBounds("content", 0, "content".length(), leftTextRect);
topRegion = getMeasuredHeight() - UIUtils.dip2px(getContext(), 28) - 10 * markSizePx - leftTextRect.height();
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
View childAt = getChildAt(0);
int right = getMeasuredWidth() - UIUtils.dip2px(getContext(), 12);
int bottom = topRegion + leftTextRect.height();
childAt.layout(right - childAt.getMeasuredWidth(), bottom - childAt.getMeasuredHeight(), right, bottom);
initAnimator();
}
@Override
protected void onDraw(@NonNull Canvas canvas) {
super.onDraw(canvas);
if (heightList == null && heightList.isEmpty()) {
return;
}
for (int i = 0; i < heightList.size(); i++) {
boolean isWithAlpha = heightList.get(i).getHeight() < this.heightest / 2;
int startX = (int) (leftTextSize + spaceWithLeftRight * i + (spaceWithLeftRight - abovePaint.getStrokeWidth()) / 2);
int startY = getHeight() - bottomTextSize;
int endX = startX;
int endY = (int) (startY - heightList.get(i).getHeight() / markSize * markSizePx);
canvas.drawLine(startX, startY, endX, endY, isWithAlpha ? downPaint : abovePaint);
canvas.drawText(bottomList.get(i), startX, getMeasuredHeight() - UIUtils.dip2px(getContext(), 12), textPaintDown);
}
for (int i = 0; i < 11; i++) {
canvas.drawText(String.valueOf(i * markSize), UIUtils.dip2px(getContext(), 24), getMeasuredHeight() - UIUtils.dip2px(getContext(), 23) - i * markSizePx, textPaintLeft);
}
textPaintTop.getTextBounds(topUnit, 0, topUnit.length(), topUnitRect);
canvas.drawText(topUnit, UIUtils.dip2px(getContext(), 10),
topRegion, textPaintTop);
if (topUnitNote != null && !topUnitNote.isEmpty()) {
canvas.drawText(topUnitNote, UIUtils.dip2px(getContext(), 10) + topUnitRect.width(),
topRegion, textPaintTopNote);
}
}
public void setHeightList(List heightList, float heightest) {
this.heightList = heightList;
// 把float转换为大于该值得int
this.heightest = (int) heightest + 1;
String[] stringArray;
if (!isRightNumberAndPeriod) {
stringArray = getContext().getResources().getStringArray(R.array.number);
topUnitNote = null;
} else {
stringArray = getContext().getResources().getStringArray(R.array.period);
topUnitNote = getContext().getResources().getString(R.string.unit_session_note);
}
this.columnCount = heightList.size();
this.spaceWithLeftRight = (getMeasuredWidth() - leftTextSize) / columnCount;
topUnit = getContext().getResources().getString(R.string.unit_session);
bottomList = Arrays.asList(stringArray);
invalidate();
}
private void initAnimator() {
valueAnimatorColorFromBlackToWhite = ValueAnimator.ofArgb(ContextCompat.getColor(mContext, R.color.black_333), ContextCompat.getColor(mContext, R.color.white));
valueAnimatorColorFromBlackToWhite.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int animationValue = (int) animation.getAnimatedValue();
if (isRightNumberAndPeriod) {
tvLeft.setTextColor(animationValue);
} else {
tvRight.setTextColor(animationValue);
}
}
});
valueAnimatorColorFromWhiteToBlack = ValueAnimator.ofArgb(ContextCompat.getColor(mContext, R.color.white), ContextCompat.getColor(mContext, R.color.black_333));
valueAnimatorColorFromWhiteToBlack.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int animationValue = (int) animation.getAnimatedValue();
if (isRightNumberAndPeriod) {
tvRight.setTextColor(animationValue);
} else {
tvLeft.setTextColor(animationValue);
}
}
});
// 注意,此时tvUp还没有改变位置。
valueAnimatorToLeft = ValueAnimator.ofFloat(viewBg.getLeft() + viewBg.getWidth(), viewBg.getLeft());
valueAnimatorToLeft.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(@NonNull ValueAnimator animation) {
float animatedValue = (float) animation.getAnimatedValue();
viewBg.layout((int) animatedValue, viewBg.getTop(), (int) animatedValue + viewBg.getWidth(), viewBg.getBottom());
}
});
valueAnimatorToRight = ValueAnimator.ofFloat(viewBg.getLeft(), viewBg.getLeft() + viewBg.getWidth());
valueAnimatorToRight.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(@NonNull ValueAnimator animation) {
float animatedValue = (float) animation.getAnimatedValue();
viewBg.layout((int) animatedValue, viewBg.getTop(), (int) animatedValue + viewBg.getWidth(), viewBg.getBottom());
}
});
animatorSetLeftClicked = new AnimatorSet();
AnimatorSet.Builder builderLeftClicked = animatorSetLeftClicked.play(valueAnimatorToLeft);
builderLeftClicked.with(valueAnimatorColorFromBlackToWhite);
builderLeftClicked.with(valueAnimatorColorFromWhiteToBlack);
animatorSetLeftClicked.setDuration(animationTime);
animatorSetLeftClicked.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
isRightNumberAndPeriod = false;
// initData(true);
switchButtonListenner.isRightClicked(false);
tvLeft.setTextColor(Color.WHITE);
tvRight.setTextColor(ContextCompat.getColor(mContext, R.color.black_333));
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
animatorSetRightClicked = new AnimatorSet();
AnimatorSet.Builder builderRightClicked = animatorSetRightClicked.play(valueAnimatorToRight);
builderRightClicked.with(valueAnimatorColorFromBlackToWhite);
builderRightClicked.with(valueAnimatorColorFromWhiteToBlack);
animatorSetRightClicked.setDuration(animationTime);
animatorSetRightClicked.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
// initData2(false);
isRightNumberAndPeriod = true;
switchButtonListenner.isRightClicked(true);
tvRight.setTextColor(ContextCompat.getColor(mContext, R.color.white));
tvLeft.setTextColor(ContextCompat.getColor(mContext, R.color.black_333));
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
initViews();
}
public interface SwitchButtonListenner {
void isRightClicked(boolean isRight);
}
private SwitchButtonListenner switchButtonListenner;
public void setSwitchButtonListenner(SwitchButtonListenner switchButtonListenner) {
this.switchButtonListenner = switchButtonListenner;
}
}
Activity中调用
private void initZhuZhuangChart() {
histogramView.setSwitchButtonListenner(new HistogramView.SwitchButtonListenner() {
@Override
public void isRightClicked(boolean isRight) {
if (isRight) {
initData2();
} else {
initData();
}
}
});
initData();
}
private void initData() {
List histogramDataBeanList = new ArrayList<>();
histogramDataBeanList.add(new HistogramDataBean(60.7f, true));
histogramDataBeanList.add(new HistogramDataBean(100.7f, true));
histogramDataBeanList.add(new HistogramDataBean(70.7f, true));
histogramDataBeanList.add(new HistogramDataBean(10.7f, false));
histogramDataBeanList.add(new HistogramDataBean(90.7f, true));
histogramDataBeanList.add(new HistogramDataBean(40.7f, false));
histogramDataBeanList.add(new HistogramDataBean(30.7f, false));
histogramView.setHeightList(histogramDataBeanList, 100.7f);
}
private void initData2() {
List histogramDataBeanList = new ArrayList<>();
histogramDataBeanList.add(new HistogramDataBean(60.7f, true));
histogramDataBeanList.add(new HistogramDataBean(100.7f, true));
histogramDataBeanList.add(new HistogramDataBean(10.7f, false));
histogramDataBeanList.add(new HistogramDataBean(90.7f, true));
histogramDataBeanList.add(new HistogramDataBean(40.7f, false));
histogramDataBeanList.add(new HistogramDataBean(30.7f, false));
histogramView.setHeightList(histogramDataBeanList, 100.7f);
}