Android 自定义优美的柱形图控件
2018-07-01 本文已影响20人
SwitchLife
开篇
由于进来工作比较忙,很久没有写博客了。在近期的项目中用到柱形图,虽然第三方开源的图饼库也挺多的,但是,项目中不需要那么复杂的图饼,所以就花了点时间自己撸了一个。啥也不说,先看效果图。
- 可动态配置X、Y轴坐标刻度
- 可控制柱形条数以及柱形宽度
- 可监听每条柱形的点击事件和长按事件
- 点击柱形,在柱形上方显示展示信息,点击柱形之外的地方则不显示
效果截屏
![](https://img.haomeiwen.com/i10068842/a64c9a100c167c44.png)
![](https://img.haomeiwen.com/i10068842/b343dcb365f6e85d.png)
立即体验
扫描以下二维码下载体验App(从0.2.3
版本开始,体验App内嵌版本更新检测功能):
![](https://img.haomeiwen.com/i10068842/32167337beb31f27.png)
JSCKit库传送门:https://github.com/JustinRoom/JSCKit
简析源码
VerticalColumnarGraphViewk.java:
- protected void onDraw(Canvas canvas)
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
clipRect.set(getPaddingLeft() + lOffset, getPaddingTop() + tOffset, getWidth() - getPaddingRight() - rOffset, getHeight() - getPaddingBottom() - bOffset);
drawAxis(canvas);
float[] xAxisScales = calculateXScales(clipRect.left);
float[] yAxisScales = calculateYScales(clipRect.bottom);
drawXAxisScales(canvas, xAxisScales, 10);
drawYAxisScales(canvas, yAxisScales, 10);
clipRect.set(getPaddingLeft(), getPaddingTop(), getWidth() - getPaddingRight(), getHeight() - getPaddingBottom());
textPaint.setColor(axisLabelTextColor);
textPaint.setTextSize(axisLabelTextSize);
textPaint.setTypeface(Typeface.defaultFromStyle(Typeface.NORMAL));
textPaint.setAlpha(0xFF);
drawXAxisLabels(canvas, xAxisScales);
drawYAxisLabels(canvas, yAxisScales);
clipRect.set(getPaddingLeft() + lOffset, getPaddingTop() + tOffset, getWidth() - getPaddingRight() - rOffset, getHeight() - getPaddingBottom() - bOffset);
drawItems(canvas);
if (isPressed) {
} else {
drawSelectedItemDetailInfo(canvas);
}
}
- public boolean onTouchEvent(MotionEvent event)
⚠️要想onTouchEvent方法触发MotionEvent.ACTION_UP事件,必须返回true。
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (!isPressed) {
isPressed = true;
selectedIndex = getSelectedIndex(event.getX(), event.getY(), space / 3);
pressedTimStamp = System.nanoTime();
if (selectedIndex >= 0){
//Send a long click event message after DEFAULT_LONG_CLICK_TIME million seconds.
getHandler().postDelayed(longClickRunnable, DEFAULT_LONG_CLICK_TIME);
}
}
break;
case MotionEvent.ACTION_MOVE:
int moveIndex = getSelectedIndex(event.getX(), event.getY(), space / 3);
if (moveIndex < 0) {
pressedTimStamp = 0;
//Remove long click event.
getHandler().removeCallbacks(longClickRunnable);
}
break;
case MotionEvent.ACTION_UP:
isPressed = false;
//Remove long click event.
getHandler().removeCallbacks(longClickRunnable);
if (System.nanoTime() - pressedTimStamp <= DEFAULT_CLICK_TIME) {
pressedTimStamp = 0;
performColumnarItemClick(selectedIndex);
}
break;
}
return true;
}
- 在监听到视图大小发生变化时,重新绘制
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
invalidate();
}
- 提供一个个性化显示UI的入口:
/**
* 个性化定制UI。
*
* @param builder builder
*/
public void initCustomUI(Builder builder) {
if (builder == null)
return;
xAxisLabels = builder.xAxisLabels;
yAxisLabels = builder.yAxisLabels;
axisColor = builder.axisColor;
axisLabelTextColor = builder.axisLabelTextColor;
axisLabelTextSize = builder.axisLabelTextSize;
column = builder.column;
lOffset = builder.leftOffset;
tOffset = builder.topOffset;
rOffset = builder.rightOffset;
bOffset = builder.bottomOffset;
if (axisLabelTextSize <= 0)
axisLabelTextSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, getResources().getDisplayMetrics());
invalidate();
}
Builder.java
⚠️自带一些默认设置,避免在没有设置某些参数时引起view绘制混乱。
public class Builder {
String[] yAxisLabels;
String[] xAxisLabels;
int axisColor;//坐标系以及刻度线颜色
int axisLabelTextColor;//刻度字体颜色
float axisLabelTextSize;//刻度字体大小
int column;//柱形数目
int leftOffset;
int topOffset;
int rightOffset;
int bottomOffset;
...
}
- 设置柱形点击和长按事件监听:
public void setOnColumnarItemClickListener(OnColumnarItemClickListener onColumnarItemClickListener) {
this.onColumnarItemClickListener = onColumnarItemClickListener;
}
public void setOnColumnarItemLongClickListener(OnColumnarItemLongClickListener onColumnarItemLongClickListener) {
this.onColumnarItemLongClickListener = onColumnarItemLongClickListener;
}
用法
verticalColumnarGraphView.setOnColumnarItemClickListener(new VerticalColumnarGraphView.OnColumnarItemClickListener() {
@Override
public void onColumnarItemClick(VerticalColumnarGraphView view, int selectedIndex, @Nullable ColumnarItem selectedItem) {
showCustomToast("click " + selectedIndex);
}
});
verticalColumnarGraphView.setOnColumnarItemLongClickListener(new VerticalColumnarGraphView.OnColumnarItemLongClickListener() {
@Override
public void onColumnarItemLongClick(VerticalColumnarGraphView view, int selectedIndex, @Nullable ColumnarItem selectedItem) {
showCustomToast("long click " + selectedIndex);
}
});
verticalColumnarGraphView.initCustomUI(
new VerticalColumnarGraphView.Builder()
.setYAxisLabels(new String[]{"\u20000", "25", "50", "75", "100"})
.setOffset(60, 0, 20, 20)
);
verticalColumnarGraphView.setItems(createTestData());
/**
* 创建测试数据
*
* @return test data source
*/
private List<ColumnarItem> createTestData() {
List<ColumnarItem> data = new ArrayList<>();
float[] ratios = {.76f, .36f, .54f, .36f, .05f, .36f, .6f};
int[] colors = {0xFFFFCF5E, 0xFFB4EE4D, 0xFF27E67B, 0xFF36C771, 0xFF1CA291, 0xFF24DDD0, 0xFf32CEF7};
String[] labels = {"返情配种", "多次输精", "人工授精", "本交", "同精液配种", "已配种母猪", "其他配种"};
String[] values = {"76头", "36头", "54头", "36头", "5头", "36头", "60头"};
for (int i = 0; i < 7; i++) {
ColumnarItem item = new ColumnarItem();
item.setColor(colors[i]);
item.setRatio(ratios[i]);
item.setLabel(labels[i]);
item.setValue(values[i]);
data.add(item);
}
return data;
}
百忙之中抽点时间分享此控件,文字写得不多,请参照代码里的注释予以理解。写得不好请见谅。
篇尾
QQ:1006368252。
土地是以它的肥沃和收获而被估价的;才能也是土地,不过它生产的不是粮食,而是真理。如果只能滋生瞑想和幻想的话,即使再大的才能也只是砂地或盐池,那上面连小草也长不出来的。 —— 别林斯基