手撸一个Android柱形图和线型图的组合图表
来源:互联网 发布:圣剑英雄传 源码 编辑:程序博客网 时间:2024/04/28 09:53
项目开发中经常用到统计图表,网上也有很多的图表类库,比如 :MPAndroidChart,XCL-chart,hellochart,AChartEngine等等,以前我最常用的就是MPAndroidChart,这个库做的非常细致用起来也简单。
但是用别人的东西好处就是快方便,坏处就是不好维护了,而且它们也只是实现了一些主流的效果,当我们面对产品经理天马行空的想法的时候,总有一些效果是这些库无法实现的。所以掌握Android中的一些绘制的基本技能是非常重要的。因为当需求来临的时候,只能自己撸啦。
前几天有个需求,要求柱形图和线型图组合,柱形图的数据依赖左边Y轴,还得分成3段,线型图有3条,右边的Y轴分成3份分别对应3条线的数据。好吧,找不到轮子只能自己造了。
先上个图:
首先在onSizeChanged()方法中初始化宽高。
@Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { screenWidth = getMeasuredWidth(); screenHeight = getMeasuredHeight(); //设置矩形的顶部 底部 右边Y轴的3部分每部分的高度 getStatusHeight(); leftWhiteRect = new Rect(0, 0, 0, screenHeight); rightWhiteRect = new Rect(screenWidth - leftMargin * 2 - 10, 0, screenWidth, screenHeight); topWhiteRect = new Rect(0,0,screenWidth,topMargin/2); bottomWhiteRect = new Rect(0, (int) yStartIndex,screenWidth,screenHeight); super.onSizeChanged(w, h, oldw, oldh); }
然后就是在onDraw()中绘制,先绘制柱形图,因为线型图的X坐标在柱形图的中间,在画线型图的路径就好画多了。每个柱形图有3个部分,从最下面的开始绘制矩形,第二个的底部坐标就是第一个的顶部,以此类推。其实onDraw()中其实就是一些对坐标的计算,只要计算对了 使用android提供的绘制的api绘制就可以了。
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); leftPoints.clear(); rightPoints.clear(); canvas.drawColor(BG_COLOR); if (winds == null || mBarData == null || humidity == null || temperature == null) return; //重置3条线 linePathW.reset(); linePathW.incReserve(winds.size()); linePathH.reset(); linePathH.incReserve(winds.size()); linePathT.reset(); linePathT.incReserve(winds.size()); checkTheLeftMoving(); textPaint.setTextSize(DensityUtil.dip2px(getContext(), 10)); barPaint.setColor(Color.WHITE); canvas.drawRect(bottomWhiteRect, barPaint); canvas.drawRect(topWhiteRect, barPaint); //画矩形 drawBars(canvas); canvas.save(); //画线型图 canvas.drawPath(linePathW, linePaint); canvas.drawPath(linePathH, linePaint); canvas.drawPath(linePathT, linePaint); //画线上的点 drawCircles(canvas);// linePath.rewind(); //画X轴 下面的和上面的 canvas.drawLine(xStartIndex, yStartIndex, screenWidth - leftMargin, yStartIndex, axisPaint); canvas.drawLine(xStartIndex, topMargin / 2, screenWidth - leftMargin, topMargin / 2, axisPaint); //画左边和右边的遮罩层 int c = barPaint.getColor(); leftWhiteRect.right = (int) xStartIndex; barPaint.setColor(Color.WHITE); canvas.drawRect(leftWhiteRect, barPaint); canvas.drawRect(rightWhiteRect, barPaint); barPaint.setColor(c); //画左边的Y轴 canvas.drawLine(xStartIndex, yStartIndex, xStartIndex, topMargin / 2, axisPaint); //画左边的Y轴text drawLeftYAxis(canvas); //左边Y轴的单位 canvas.drawText(leftAxisUnit, xStartIndex - textPaint.measureText(leftAxisUnit) - 5, topMargin/2, textPaint); //画右边的Y轴 canvas.drawLine(screenWidth - leftMargin * 2 - 10, yStartIndex, screenWidth - leftMargin * 2 - 10, topMargin / 2, axisPaint); //画右边Y轴text drawRightYText(canvas); }
然后是当我们的数据太多绘制超出了view的宽度之后,可以通过手势滑动来看到后面的数据。这部分可以通过Scroller来滑动通过VelocityTracker和Scroller来处理手抬起后再滑动一段距离停下
@Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: lastPointX = event.getX(); scroller.abortAnimation();//如果在滑动终止动画 initOrResetVelocityTracker();//初始化速度跟踪器 break; case MotionEvent.ACTION_MOVE: float movex = event.getX(); movingThisTime = lastPointX - movex; leftMoving = leftMoving + movingThisTime; lastPointX = movex; invalidate(); velocityTracker.addMovement(event);//将用户的action添加到跟踪器中。 break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: velocityTracker.addMovement(event); velocityTracker.computeCurrentVelocity(1000, maxVelocity);//根据已经到达的点计算当前速度。 int initialVelocity = (int) velocityTracker.getXVelocity();//获得最后的速度 velocityTracker.clear(); //通过scroller让它飞起来 scroller.fling((int) event.getX(), (int) event.getY(), -initialVelocity / 2, 0, Integer.MIN_VALUE, Integer.MAX_VALUE, 0, 0); invalidate(); lastPointX = event.getX(); recycleVelocityTracker();//回收速度跟踪器 break; default: return super.onTouchEvent(event); } if (mGestureListener != null) { mGestureListener.onTouchEvent(event); } return true; }
然后是添加动画,使用属性动画
private float percent = 1f; private TimeInterpolator pointInterpolator = new DecelerateInterpolator(); public void startAnimation(int duration){ ValueAnimator mAnimator = ValueAnimator.ofFloat(0,1); mAnimator.setDuration(duration); mAnimator.setInterpolator(pointInterpolator); mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { percent = (float) animation.getAnimatedValue(); invalidate(); } }); mAnimator.start(); }
上面是一个0-1的属性动画来改变percent 的值,所以percent 就是从0慢慢增长到1的值,我们绘制的时候对于Y坐标的处理就可以用Y坐标的值乘以percent 。这样Y值就是从0到它本来的值慢慢增长完成动画。
最后是给每个柱形图添加点击事件。通过GestureDetector中的onGestureListener中的onSingleTapUp实现,关键是计算我们点击的坐标的范围是不是在一个柱形图的范围内。demo中只计算了X轴的坐标。原理,当我们绘制柱形图的时候,每绘制一个,将讲它的x坐标的左边的坐标和右边的坐标存起来。循环判断我们点击的位置是不是在左边和右边的坐标之间如果是就返回当前的i,反之就是点击的无效的位置。
private int identifyWhichItemClick(float x, float y) { float leftx = 0; float rightx = 0; for (int i = 0; i < mBarData.size(); i++) { leftx = leftPoints.get(i); rightx = rightPoints.get(i); if (x < leftx) { break; } if (leftx <= x && x <= rightx) { return i; } } return INVALID_POSITION; }
项目地址:https://github.com/chsmy/EasyChartWidget
- 手撸一个Android柱形图和线型图的组合图表
- 手撸一个Android柱形图和线型图的组合图表
- 手撸一个Android线形图表
- 手撸一个Android柱状图表
- 手撸一个Android饼状图表
- 手撸一个Android饼状图表(空心圆)
- 设置图表的线型、属性和格式化字符串
- EXCEL完成一个柱形图与折线图组合图表
- 一个支持颜色、线型、线宽和填充选择的组合框
- 使用Aspose.Cells生成Excel的线型图表
- FusionCharts图表中组合图的实现
- MATLAB 线型 颜色 和 标记点组合使用
- 手撸一个简易Android数据库框架
- MPAndroidChart 图表柱状图,折线图,和组合图
- 由制图线型符号的改进说起—GDI+的线型绘制和ArcObjects线型绘制比较
- D3.js初体验 —— 线型图表与坐标轴的绘制
- 一个platform总线型的i2c设备的注册
- 手撸一个简单的单链表
- Hive中count统计数量的多种使用方式
- 【JavaScript】JavaScript原型链之原型对象、实例和构造函数三者之间的关系
- Fabric Prerequisites
- nginx的conf详解
- c# winform捕获全局异常,并记录日志
- 手撸一个Android柱形图和线型图的组合图表
- ionic学习之cordova获取IMEI:
- HyperLedger(1)启动Fabric及创建channel、部署chaincode
- Java NIO 内存映射文件
- php中static静态变量的使用
- JS原型对象,原型链
- 目标检测衡量标准
- Android ClassLoader与JAVA ClassLoader详解及对比
- HyperLedger(2)动态添加channel、organization