很早就想来完善之前写的走势图(一)了,结果两个月感觉天天都在忙,无脑写代码,还偶尔通宵,尼玛啊,一脸要死的样子,现在总算有点时间了~~~~

这篇是在第一篇的基础上进行的完善和改进,主要就是加入了动画和触摸效果,效果图如下:

在之前的基础上,首先改善的第一点就是重新计算了x轴和y轴文字的坐标,让这些文字显示出来的位置更准确,主要就是计算了这些文字的宽高,然后根据这些文字的宽高来计算它们相对x轴和y轴的偏移量,改进后的代码如下:

        Paint.FontMetrics fm = textPaint.getFontMetrics();if(isShowXText){     //绘制x轴文字float h1 = fm.descent - fm.ascent; //x轴文字高度,相对x轴向下偏移的值for(int x1 = 0;(x1 * 4) < points.size();x1++){String xStr = points.get(x1 * 4).getDate();float xw = textPaint.measureText(xStr);  //x轴文字的宽度,为了计算文字宽度的中心点canvas.drawText(points.get(x1 * 4).getDate(), left + xDivider * x1 - xw/2,bottom + h1, textPaint);}}if(isShowYText){    //绘制y轴数值float h2 = Math.abs(fm.descent + fm.ascent);  //y轴文字相对于文字baseline的高度,为了计算高度的中心点float avIncome = maxPoint.getIncome() / 5;for(int y1 = 1;y1 <=5 ;y1++){String income = new DecimalFormat("0.00").format(avIncome * y1);float textWidth = textPaint.measureText(income);  //y轴文字宽度,相对y轴向左偏移的值canvas.drawText(income,left - textWidth - 5,bottom - yDivider * y1 + h2/2, textPaint);}}

在上面代码中计算文字宽高,为了保证横竖刻度线和文字的中心点是对齐的(强迫症!!!???)

接下来要做的就是加入x和y方向上的动画效果,这里就用属性动画来实现这种效果:

    private float phaseY = 1f;  private int xCount = 0;

首先定义的这两个属性,就是要通过属性动画来改变的两个值,xCount是用来控制点的个数,phaseY用来控制所有点的y值的变化,主要在描点的时候来控制:

    private void drawDataLines(Canvas canvas, List<Point> points){for(int i=0;i<xCount;i++){Point point1 = points.get(i == 0? 0: i-1);Point point2 = points.get(i);Location location1 = getLocation(point1,i == 0?0: i-1 );Location location2 = getLocation(point2, i);canvas.drawCircle(location2.x, location2.y, 5f, textPaint);if(i > 0){canvas.drawLine(location1.x, location1.y, location2.x,location2.y, linePaint);}}}
    private Location getLocation(Point point, int n){if(specY == 0 || specX == 0){specY = (bottom - top) / maxPoint.getIncome();specX = (right - left) / (points.size() - 1);}float x = n * specX + left;float y = bottom  - point.getIncome() * specY * phaseY;return new Location(x, y);}

上面的drawDataLines()方法就是描点连线的方法,用xCount来控制for循环的最大值,也就控制了需要描点的数量;getLocation()方法对点的一个转换的方法,里面对获取点的y值的时候加入了phaseY,来控制y值的变化,然后就是实现两个属性动画:

    public void animX(long duration){ObjectAnimator animator = ObjectAnimator.ofInt(this, "xCount", 0, points.size());animator.setDuration(duration);animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {invalidate();}});animator.start();}public void animY(long duration){ObjectAnimator animator = ObjectAnimator.ofFloat(this, "phaseY", 0f, 1f);animator.setDuration(duration);animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {invalidate();}});animator.start();}

可以看到,animX()方法我用的ofInt来控制的,因为点的个数只能是整数,这样就行了;而animY()方法中,用了ofFloat来控制,让phaseY用0变到1,这样来实现所有点的y值从0变到原值,这里要注意的是对于这两个属性一定要定义set()方法,因为ObjectAnimator是通过反射来调用属性的set方法来赋值的。这个时候你就可以在外面调用这两个方法了,去体验一下流畅顺滑的动画效果(还有一点要注意的是,执行动画的时候会频繁调用invalidate(),所以在执行OnDraw()之前,如果有用到path来存放线条,一定要记得reset,不然执行动画的是时候,点越多,就会越卡)~~~~~

最后要实现的就是手触摸屏幕时,显示出手触摸在哪个点上,并通过指示线显示出具体的值,先来定义一些要用到的关键属性:

    private Paint indicatorPaint;  //指示线画笔private Paint indicateValuePaint; //绘制x和y值画笔private Paint indicateRectPaint;  //绘制x和y值显示区域的画笔private boolean isTouch = false;private float[] indicates = new float[8];  //横竖指示线的坐标private RectF rectY;    //显示y值的矩形区域private RectF rectX;    //显示x值的矩形区域private Point touchedPoint;   //触摸到的点private Location xValueLocation;  //绘制x值的坐标private Location yValueLocation;  //绘制y值的坐标

对于这些属性的初始化就不放上来了,这里的关键就是手指触摸时坐标的计算,下面是主要计算代码:

    private void setIndicators(float x, float y){if(x < left){x = left;}if(x > right){x = right;}int position = new BigDecimal((x-left)/specX).setScale(0,BigDecimal.ROUND_HALF_UP).intValue();Point point = points.get(position);touchedPoint = point;Location location = getLocation(point, position);//计算手指触摸时,两条指示线的坐标并赋值indicates[0] = left;indicates[1] = location.y;indicates[2] = right;indicates[3] = location.y;indicates[4] = location.x;indicates[5] = top;indicates[6] = location.x;indicates[7] = bottom;Paint.FontMetrics fm = indicateValuePaint.getFontMetrics();//计算两条指示线在坐标轴上显示值的矩形区域String date = point.getDate();String income =  new DecimalFormat("0.00").format(point.getIncome());float dateWidth = indicateValuePaint.measureText(date);float incomeWidth = indicateValuePaint.measureText(income);float height = fm.bottom - fm.top;//绘制x值的矩形区域rectX.left = location.x - dateWidth/2 - 5;rectX.top = bottom;rectX.right = location.x + dateWidth/2 + 5;rectX.bottom = bottom + height;//绘制y值的矩形区域rectY.left = left - incomeWidth - 10;rectY.top = location.y - height/2;rectY.right = left;rectY.bottom = location.y + height/2;//计算绘制x,y值的坐标xValueLocation.x = rectX.left + 5;xValueLocation.y = rectX.bottom - fm.descent;yValueLocation.x = rectY.left + 5;yValueLocation.y = rectY.bottom - fm.descent;}

关键的计算在代码都给出了注释,方法中传进来的x,y就是手指触摸时的屏幕上的坐标,这里对手指触摸是做了一个判断,离哪个点更近,哪个点就是被触摸的那个点。接着就是重写该view 的onTouchEvent()方法:

    @Overridepublic boolean onTouchEvent(MotionEvent event) {float x = event.getX();float y = event.getY();switch (event.getAction()){case MotionEvent.ACTION_DOWN:isTouch = true;setIndicators(x, y);break;case MotionEvent.ACTION_MOVE:setIndicators(x, y);break;case MotionEvent.ACTION_UP:isTouch = false;break;}invalidate();return true;}

在Down和Move的事件中,调用了上面的setIndicators()的方法,需要的东西都准备完了,接着要来根据这些值画出指示线和值了了:

    //绘制手触摸的时候的指示线和对应的值private void drawIndicateLines(Canvas canvas){canvas.drawLines(indicates, indicatorPaint);canvas.drawRect(rectX, indicateRectPaint);canvas.drawRect(rectY, indicateRectPaint);canvas.drawText(touchedPoint.getDate(), xValueLocation.x, xValueLocation.y, indicateValuePaint);String income =  new DecimalFormat("0.00").format(touchedPoint.getIncome());canvas.drawText(income, yValueLocation.x, yValueLocation.y, indicateValuePaint);}

上面这个方法中,draw方法绘制的内容由上到下依次为:

①绘制两条指示线;②绘制x值显示的矩形背景;③绘制y值显示的矩形背景;④绘制x值;⑤绘制y值。

最后再onDraw中调用:

       if(isTouch){drawIndicateLines(canvas);}

到这里,最开始图上的效果就基本实现了

自定义view走势图(二、加入动画和触摸事件)相关推荐

  1. 自定义view走势图(三、贝塞尔曲线)

    在开发中,对于走势图和统计图,会有用平滑的曲线来进行展示的需求,我首先想到的就是贝塞尔曲线.那么贝塞尔曲线是啥呢,贴上两张图多看一会就明白了 图一     图二 上面图一是二阶贝塞尔曲线,图二是三阶贝 ...

  2. android+清除循环动画,android自定义View之(4)-一键清除动画

    android自定义View之(四)------一键清除动画 1.前言: 自己也是参考别人的一些自定义view例子,学习了一些基本的自定义view的方法.今天,我参考了一些资料,再结合自已的一些理解, ...

  3. 自定义 View 之 QQ 个人主页视差动画效果

    博主声明: 转载请在开头附加本文链接及作者信息,并标记为转载.本文由博主 威威喵 原创,请多支持与指教. 本文首发于此   博主:威威喵  |  博客主页:https://blog.csdn.net/ ...

  4. android自定义View之(四)------一键清除动画

    1.前言: 自己也是参考别人的一些自定义view例子,学习了一些基本的自定义view的方法.今天,我参考了一些资料,再结合自已的一些理解,做了一个一键清除的动画.当年,我实现这个是用了几张图片,采用F ...

  5. 自定义View -- 蜘蛛网图

    经常会在游戏中看到用蜘蛛网图表达用户在游戏中的各种表现,感觉还蛮直观蛮有趣的,刚好最近也在学习自定义View的相关知识,就自己做一个蜘蛛网图,作为笔记了. 先放一张我感觉对自己挺有帮助,从网上找的以这 ...

  6. android自定义u形线,Android实战之自定义View折线图

    如果你对自定义View还不是很了解,那么这篇文章将从实战的角度带你一步一步的编写出一个符合规范的自定义View. 需求:假设有一个病人,他不定时的将自己的血压值录入到我们的客户端,而我们要做的就是将他 ...

  7. Android自定义View初探(二)——仿360垃圾清理

    明天就是五一劳动节了,在这里先祝各位程序猿劳动节快乐,别在加班了! 自从尝试过写自定义View(Android自定义View初探(一)--饼图)之后,每当看到别人的应用时,总是在想别人的实现方式,或许 ...

  8. 用C#开发的双色球走势图(二)

    昨晚由于时间的原因只写了一部分内容,今天将这一部分内容补充完毕,多谢各位园友的支持. 这是用C#开发的双色球走势图(一)新的园友可以看昨晚写的内容,以免脱节.首先回复园友的评论,有说好的有说不好的,本 ...

  9. android自定义View学习(二)----自定义绘图

    自定义绘图 自定义视图中最重要的部分是它的外观.根据您的应用需求,自定义绘图可以很容易或复杂.本篇涵盖了一些最常见的操作 onDraw() 绘制自定义视图中最重要的步骤是重写该onDraw()方法.参 ...

最新文章

  1. k8s-harbor安装
  2. 从地理分类的计算机网格,第2章-地理空间象计算机表达(6-9学时).doc
  3. java 删除文件失败_java 文件删除失败(被进程占用)
  4. HTTPS是如何加密的
  5. 图解TCP数据报结构以及三次握手(非常详细)
  6. 浅谈 Celery 分布式队列
  7. python自动配置文件_【python接口自动化】- ConfigParser配置文件的使用
  8. kettle变量(param命名参数)
  9. 编写一个Java程序,在程序中包含一个Employee类,Employee类包含name、age、salary三个成员变量
  10. java 一一对应的替换_java 如何实现按表替换
  11. php探针教程,php探针程序的推荐
  12. 【DIY小记】Win10编程字体主题设置全攻略
  13. Flink简介与部署
  14. Outlook 2016 配置QQ邮箱
  15. 什么是机器学习里的优化?
  16. 统计图表插件Chart.js(前端常用图表)
  17. java编程将HTML文件转换成PDF文件
  18. 【明解C语言】之指针初阶详解
  19. 转: 网页设计中的一些色彩搭配技巧
  20. 10、第二章物理层习题及参考答案

热门文章

  1. 服务器系统 固态硬盘速度慢,SSD写入速度太慢,官方给出的速度是400多 而我的只能达到最高七八十 是怎么回事呢?...
  2. 励志!打过杂、送过外卖,他逆袭为昇思MindSpore优秀布道师!
  3. Number、Number()和new Number()
  4. 爱思助手(I4)可能造成iphone4的IOS7.0.4内置程序丢失
  5. 发送短信验证码的原理
  6. centos安装升级ruby
  7. Xshell7家庭版
  8. Catia许可证释放及使用优化管理方案
  9. java程序员入门_Java程序员入门:简介
  10. SAP MM 计划协议5500025001 为什么不能维护SA Release Creation Profile?