一.效果图
因项目需要自定义一个柱状图View及饼状图View,本文先主要介绍下自定义柱状图View,饼状图View下一篇介绍,主要做个学习积累。
实现也很简单,大家可以先看下动态效果图:
二、实现步骤
代码也比较简单,就是遵循自定义View步骤顺序完成相应绘制,不清楚可以参考鸿祥大神的博客。
1.定义HistogramView继承View,给HistogramView添加自定义属性:
以在value目录下找到attrs.xml文件,在文件中可以定义HistogramView用到的属性,比如颜色、字体大小等属性。文件内容如下:
<declare-styleable name="HistogramView">
<attr name="histogramItemWidth" format="dimension"/><!-- 柱状图项宽度 -->
<attr name="topTextColor" format="color"/><!-- 顶部文字颜色 -->
<attr name="topTextSize" format="dimension"/><!-- 顶部文字大小 -->
<attr name="axesColor" format="color"/><!-- 坐标轴颜色 -->
<attr name="axesTextColor" format="color"/><!-- 坐标轴文字颜色 -->
<attr name="axesTextSize" format="dimension"/><!-- 坐标轴文字大小 -->
</declare-styleable>
2.在.xml布局文件中对自定义的属性进行设置:
<com.cyj.histogramview.HistogramView
android:id="@+id/histogramView"
android:layout_width="match_parent"
android:layout_height="280dp"
android:layout_centerInParent="true"
android:paddingTop="40dp"
android:paddingLeft="10dp"
android:layout_centerHorizontal="true"
app:histogramItemWidth="20dp"
app:axesColor="@color/black"
app:axesTextColor="@color/mainColor"
app:axesTextSize="12sp"
app:topTextColor="@color/accentColor"
app:topTextSize="13sp"
android:background="@color/white"/>
3.在HistogramView的构造方法中获得自定义的属性,主要代码如下:
public HistogramView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.HistogramView);
histogramItemWidth = typedArray.getDimension(R.styleable.HistogramView_axesTextSize, dp2px(20));
axesColor = typedArray.getColor(R.styleable.HistogramView_axesColor, getResources().getColor(R.color.accentColor));
axesTextColor = typedArray.getColor(R.styleable.HistogramView_axesTextColor, getResources().getColor(R.color.accentColor));
axesTextSize = typedArray.getDimension(R.styleable.HistogramView_axesTextSize, sp2px(12));
topTextColor = typedArray.getColor(R.styleable.HistogramView_topTextColor, getResources().getColor(R.color.accentColor));
topTextSize = typedArray.getDimension(R.styleable.HistogramView_topTextSize, sp2px(12));
typedArray.recycle();
init();
}
4.初始化画笔及一些必要变量,代码如下:
private void init() {
ani = new HistogramAnimation();
ani.setDuration(2000);
mMargin = dp2px(20);
//初始化坐标轴画笔
mAexsPaint = new Paint();
mAexsPaint.setColor(axesColor);
mAexsPaint.setAntiAlias(true);
//坐标轴文字画笔
mAexsTextPaint = new TextPaint();
mAexsTextPaint.setStyle(Paint.Style.FILL);
mAexsTextPaint.setColor(axesTextColor);
mAexsTextPaint.setTextSize(axesTextSize);
mAexsTextPaint.setAntiAlias(true);
mAexsTextPaint.setTextAlign(Paint.Align.LEFT);
//顶部文字画笔
mTopTextPaint = new TextPaint();
mTopTextPaint.setStyle(Paint.Style.FILL);
mTopTextPaint.setColor(topTextColor);
mTopTextPaint.setTextSize(topTextSize);
mTopTextPaint.setAntiAlias(true);
mTopTextPaint.setTextAlign(Paint.Align.LEFT);
//柱状图画笔
mHistogramPaint = new Paint();
mHistogramPaint.setAntiAlias(true);// 抗锯齿效果
mHistogramPaint.setStyle(Paint.Style.FILL);
mHistogramPaint.setColor(Color.parseColor("#9e95e9f3"));
}
5.重写onDraw方法。在onDraw方法中完成柱状图的绘制。代码如下:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mWidth = getWidth() - getPaddingLeft() - getPaddingRight();
mHeight = getHeight();
histogramHeight = mHeight - bottomAxesTextHeight - getPaddingTop();
mMarginTop = getPaddingTop();
columWidth = (int) ((mWidth - mMargin) / columCount);//项的宽度
drawAxesLineAndAxes(canvas);//绘制坐标线及y轴刻度
drawAxesText(canvas);//绘制x轴值
drawHistogramItem(canvas);//绘制柱状图及柱状图上文字
}
主要完全以下几个部分的绘制:
a.绘制坐标线及y坐标轴上的刻度,代码如下:
private void drawAxesLineAndAxes(Canvas canvas) {
// 绘制底部的线条
canvas.drawLine(0, mHeight - bottomAxesTextHeight, mWidth, mHeight - bottomAxesTextHeight, mAexsPaint);
// 绘制底部的线条
canvas.drawLine(mMargin, mMarginTop, mMargin, mHeight - bottomAxesTextHeight + mMargin, mAexsPaint);
canvas.drawText("0", mMargin / 2, mHeight - bottomAxesTextHeight, mAexsTextPaint);
canvas.drawText(maxValue + "", mMargin / 2, mHeight - bottomAxesTextHeight - histogramHeight, mAexsTextPaint);
}
b.绘制x轴值
private void drawAxesText(Canvas canvas) {
for (int i = 0; i < columCount; i++) {
// 设置底部的文字
float textWidth = mAexsTextPaint.measureText(nameLists.get(i));
if (textWidth > columWidth) {
textWidth = columWidth / 2;
} else {
textWidth = (columWidth - textWidth) / 2;
}
TextPaint textPaint = new TextPaint();
textPaint.setColor(axesTextColor);
textPaint.setAntiAlias(true);
textPaint.setTextSize(axesTextSize);
StaticLayout layout = new StaticLayout(nameLists.get(i), textPaint, columWidth,
Layout.Alignment.ALIGN_NORMAL, 1.0F, 0.0F, false);
canvas.save();
canvas.translate((i) * columWidth + textWidth + mMargin, mHeight - bottomAxesTextHeight + mMargin / 2);
layout.draw(canvas);
canvas.restore();//别忘了restore
}
}
c.绘制柱状图及柱状图上的该项的值,代码如下:
private void drawHistogramItem(Canvas canvas) {
// 绘制矩形
if (aniProgress != null && aniProgress.size() > 0) {
for (int i = 0; i < aniProgress.size(); i++) {// 循环遍历将柱状图形画出来
int value = aniProgress.get(i);
float left = (columWidth - histogramItemWidth) / 2 + i * columWidth + mMargin;
float right = left + histogramItemWidth;
float t = (maxValue) * 1000;
float top = histogramHeight - histogramHeight * (value / t) + mMarginTop;
float bottom = histogramHeight + mMarginTop;
//进行paint设颜色
if (i % 4 == 0) {
mHistogramPaint.setColor(getResources().getColor(R.color.subColor));
mTopTextPaint.setColor(getResources().getColor(R.color.subColor));
} else if (i % 4 == 1) {
mHistogramPaint.setColor(getResources().getColor(R.color.subColor1));
mTopTextPaint.setColor(getResources().getColor(R.color.subColor1));
} else if (i % 4 == 2) {
mHistogramPaint.setColor(getResources().getColor(R.color.subColor2));
mTopTextPaint.setColor(getResources().getColor(R.color.subColor2));
} else if (i % 4 == 3) {
mHistogramPaint.setColor(getResources().getColor(R.color.subColor3));
mTopTextPaint.setColor(getResources().getColor(R.color.subColor3));
}
RectF rect1 = new RectF(left, top, right, bottom);// 柱状图的形状
canvas.drawRoundRect(rect1, (histogramItemWidth) / 2, (histogramItemWidth) / 2, mHistogramPaint);
//设置柱形上面的文字
float textWidth = mTopTextPaint.measureText(countLists.get(i));
if (textWidth > columWidth) {
textWidth = 0;
} else {
textWidth = (columWidth - textWidth) / 2;
}
canvas.drawText(countLists.get(i) + "", (i) * columWidth + textWidth + mMargin, top - dp2px(10), mTopTextPaint);
}
}
}
在这一步不断改变柱状图的高度,使柱状图有个动画效果,使高度不断改变可以通过如下动画类实现:
/**
* 集成animation的一个动画类
*/
private class HistogramAnimation extends Animation {
protected void applyTransformation(float interpolatedTime,
Transformation t) {
super.applyTransformation(interpolatedTime, t);
for (int i = 0; i < aniProgress.size(); i++) {
aniProgress.set(i, (int) (Integer.parseInt(countLists.get(i)) * 1000 * interpolatedTime));
}
invalidate();
}
}
三、使用
在Activity中初始化柱状图数据,并开启动画效果:
ArrayList<String> nameLists = new ArrayList<>();
ArrayList<String> countLists = new ArrayList<>();
for (int i=0;i<8;i++){
nameLists.add("项"+i);
countLists.add(""+new Random().nextInt(20));
}
histogramView.start(nameLists,countLists);
}
}
上面已经贴出了实现的关键代码,完整demo可以参考:https://github.com/yjchen920927/HistogramView