最近自己改进了一下柱状图的绘制,支持多跟柱子,自己设置颜色,并且添加了手动滑动效果(在屏幕绘制宽度不够时启动滑动),下图是整体效果,下面我分析一下代码:
1、设置内容:
tagging:标注
xRawData:x轴坐标
yRawData:为柱形图的内容,通过List<Float>... yRawData传入,可以有多个
/** * 设置值 tagging:标注 xRawData:x轴坐标 yRawData:为柱形图的内容 */ public void setData(List<String> tagging, List<String> xRawData, List<Float>... yRawData) { occupyingText = null; this.xRawDatas.clear(); this.yRawData.clear(); this.xRawDatas.addAll(xRawData); this.tagging = tagging; Collections.addAll(this.yRawData, yRawData); updateTagging(); updateCutoffwidth(); scroll(); invalidate(); }2、调用方法:
BarChartView kanner = (BarChartView) findViewById(R.id.kanner); List<String> taggings = new ArrayList<>(); taggings.add("北京"); taggings.add("上海"); taggings.add("深圳"); List<String> xRawDatas = new ArrayList<>(); //横坐标值 xRawDatas.add("2016-01"); xRawDatas.add("2016-02"); xRawDatas.add("2016-03"); xRawDatas.add("2016-04"); xRawDatas.add("2016-05"); xRawDatas.add("2016-06"); xRawDatas.add("2016-07"); xRawDatas.add("2016-08"); xRawDatas.add("2016-09"); List<Float> yRawDatas1 = new ArrayList<>(); List<Float> yRawDatas2 = new ArrayList<>(); List<Float> yRawDatas3 = new ArrayList<>(); yRawDatas1.add(12.0f); yRawDatas1.add(7.0f); yRawDatas1.add(-12.0f); yRawDatas1.add(6.0f); yRawDatas1.add(12.0f); yRawDatas1.add(17.0f); yRawDatas1.add(-5.0f); yRawDatas1.add(9.0f); yRawDatas1.add(2.0f); yRawDatas2.add(-12.0f); yRawDatas2.add(7.0f); yRawDatas2.add(6.0f); yRawDatas2.add(7.0f); yRawDatas2.add(8.0f); yRawDatas2.add(-13.0f); yRawDatas2.add(5.0f); yRawDatas2.add(9.0f); yRawDatas2.add(2.8f); yRawDatas3.add(3.0f); yRawDatas3.add(6.0f); yRawDatas3.add(9.0f); yRawDatas3.add(6.0f); yRawDatas3.add(11.0f); yRawDatas3.add(-7.0f); yRawDatas3.add(6.66f); yRawDatas3.add(6.0f); yRawDatas3.add(-6.0f); kanner.setData(taggings, xRawDatas, yRawDatas1,yRawDatas2,yRawDatas3); 这里我做了一些假数据,实际开发中根据接口请求到的值赋值就可以了。3、这里我对一些关键技术说明一下:
private float mDownPosX = 0; private float mDownPosY = 0; @Override public boolean dispatchTouchEvent(MotionEvent event) { if (offsetWidthMax != 0) { final float x = event.getX(); final float y = event.getY(); final int action = event.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: mDownPosX = x; mDownPosY = y; break; case MotionEvent.ACTION_MOVE: final float deltaX = Math.abs(x - mDownPosX); final float deltaY = Math.abs(y - mDownPosY); if (deltaX > deltaY) { getParent().requestDisallowInterceptTouchEvent(true); } else { getParent().requestDisallowInterceptTouchEvent(false); return true; } break; } mGestureDetector.onTouchEvent(event); return true; } else { getParent().requestDisallowInterceptTouchEvent(false); return super.dispatchTouchEvent(event); } } 重写dispatchTouchEvent,如果在滑动视图里,会与系统的滑动事件冲突,这里就用到了事件分发机制,因为柱状图都是左右滑动的,所以我们只需要拦截左右滑动就可以了,在这里我们判断如果左右滑动距离大于上下滑动,就执行左右滑动,否则不执行滑动事件。具体Android事件分发我整理一片博客http://blog.csdn.net/yulu5216/article/details/51306160,感兴趣的可以看看
onSizeChanged,当布局高宽改变时会重新绘制图片高宽:
@Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { this.canvasHeight = h; this.canvasWidth = w; coordinateRect = new RectF(marginLeft, marginTop + taggingHeight, canvasWidth - marginRight, canvasHeight - marginBottom); horizontalNum = (int) (coordinateRect.width() / dip2px(60)); updateTagging(); updateCutoffwidth(); }设置最大和最小值:
private boolean initMaxAndMin(int start, int stop) { if (stop <= 0) { return true; } if (stop > this.xRawDatas.size()) { stop = this.xRawDatas.size(); } List<Float> yRawDataNews = new ArrayList<>(); for (int i = 0; i < this.yRawData.size(); i++) { yRawDataNews.addAll(yRawData.get(i).subList(start, stop)); } float maxValueNews = getMaxArray(yRawDataNews); float minValueNews = getMinArray(yRawDataNews); if (isInteger) { if (maxValueNews >= 0 && minValueNews >= 0) { minValueNews = 0; maxValueNews = ((int) ((maxValueNews - 1) / 5 + 1)) * 5; yIndex = 5; } else if (maxValueNews <= 0 && minValueNews < 0) { maxValueNews = 0; minValueNews = ((int) ((minValueNews) / 5)) * 5; yIndex = 0; } else { float proportion = Math.abs(maxValueNews / minValueNews); // 比例 if (proportion >= 4) { maxValueNews = ((int) ((maxValueNews - 1) / 4 + 1)) * 4; minValueNews = -maxValueNews / 4; yIndex = 4; } else if (proportion >= 1) { maxValueNews = ((int) ((maxValueNews - 1) / 3 + 1)) * 3; minValueNews = -maxValueNews * 2 / 3; yIndex = 3; } else if (proportion >= 1 / 4) { minValueNews = ((int) ((minValueNews) / 3)) * 3; maxValueNews = -minValueNews * 2 / 3; yIndex = 2; } else { minValueNews = ((int) ((minValueNews) / 4)) * 4; maxValueNews = -minValueNews / 4; yIndex = 1; } } } else { if (minValueNews < 0 && maxValueNews > 0) { float proportion = Math.abs(maxValueNews / minValueNews); // 比例 if (proportion >= 4) { minValueNews = -maxValueNews / 4; yIndex = 4; } else if (proportion >= 1) { minValueNews = -maxValueNews * 2 / 3; yIndex = 3; } else if (proportion >= 1 / 4) { maxValueNews = -minValueNews * 2 / 3; yIndex = 2; } else { maxValueNews = -minValueNews / 4; yIndex = 1; } } else if (minValueNews >= 0 && maxValueNews > 0) { yIndex = 5; minValueNews = 0; } else if (minValueNews < 0 && maxValueNews <= 0) { yIndex = 0; maxValueNews = 0; } } if (maxValueNews == minValueNews) { minValueNews = +1; } if (maxValueNews == maxValue && minValue == minValueNews) { return true; } maxValue = maxValueNews; minValue = minValueNews; if (isCompanyUpdate) { if (maxValue > 300000000 || minValue < -300000000) { companyNews = "亿" + company; priceWeight = 100000000; } else if (maxValue > 3000000 || minValue < -3000000) { companyNews = "百万" + company; priceWeight = 1000000; } else if (maxValue > 30000 || minValue < -30000) { companyNews = "万" + company; priceWeight = 10000; } else if (maxValue > 300 || minValue < -300) { companyNews = "百" + company; priceWeight = 100; } else { companyNews = company; } } else { companyNews = company; } if (minValue < 0) { marginBottom = dip2px(30); } coordinateRect = new RectF(marginLeft, marginTop + taggingHeight, canvasWidth - marginRight, canvasHeight - marginBottom); return false; }start和stop是可视区域的开始和结束位置的下标,返回值判断是否最大和最小值是否改变,如果改变则刷新页面
最后我们上图:
代码下载地址:http://download.csdn.net/detail/yulu5216/9686662最后我们上图: