内容简介:产品要做一个支持最开始的想法时用MPAndroidChart来做,可用这个库有些细节满足不了产品的需求 如选中的label标签要用选中颜色及回滚功能,然后就很没底,找了很多类似功能的自定义控件的类比,做之前也咨询了一位大佬(在此感谢扔物线大神),觉得薄荷尺子的逻辑和这个需求很类似,就决定用自定义view来实现。自己以前写过的自定义view都比较简单,自己刚开始做的时候压力挺大的,挺担心自己做不出来影响项目进度的,不过一时也没有好的办法,只能逼着自己去做,主要参考之前仿写薄荷尺子的大神的博客,做了四天下来,总
产品要做一个支持 横向滚动 中心区域选中 惯性滚动 停止时回滚到中心位置 点击选中 的图表需求 效果图如下:
最开始的想法时用MPAndroidChart来做,可用这个库有些细节满足不了产品的需求 如选中的label标签要用选中颜色及回滚功能,然后就很没底,找了很多类似功能的自定义控件的类比,做之前也咨询了一位大佬(在此感谢扔物线大神),觉得薄荷尺子的逻辑和这个需求很类似,就决定用自定义view来实现。自己以前写过的自定义view都比较简单,自己刚开始做的时候压力挺大的,挺担心自己做不出来影响项目进度的,不过一时也没有好的办法,只能逼着自己去做,主要参考之前仿写薄荷尺子的大神的博客,做了四天下来,总算有点眉目,把demo拿给产品过目也比较满意,这个效果的实现也渐渐领略到开源的魅力,看到自己做出来的效果贼开心贼有成就感,周末打算分享出来,希望能对大家有所帮助,项目中有什么问题请不吝赐教,感激不尽。 目前有些代码可能还不够完善,后续还需要处理嵌套滚动的问题,但主体思路已经比较清晰了
话不多说,效果如下:
源码地址: github.com/SilenceBurs…参考博客:
之前仿写薄荷尺子的大神 很多代码甚至注释都被我毫不留情的copy过来了 :grin: blog.csdn.net/totond/arti…
scoller相关及多点触控相关 请看其系列博客 blog.csdn.net/u012422440/…
根据实现的步骤拆分为如下功能点
- 自定义属性的设置及使用
- draw 绘制图表
- 触摸控制并处理多指触控问题(手指拖动图表可移动)
- 惯性滚动(根据手指释放时的速度计算图表需要滚动的距离)
- 回滚 (up时或者惯性滚动结束 需要回滚到选中位置)
- 点击选中 (根据点击的坐标,计算需要选中的下标并选中)
1.自定义属性的设置及使用
在attr文件中声明该控件的一些自定义属性,在构造方法中解析,设置控件的属性即可
2. draw 绘制图表
绘制图表其实主要时数学问题,具体坐标的计算就不再赘述了
请教扔物线的时候,我问他会不会有性能问题,他就说了一点, 屏幕外不要绘制 我们就只需绘制屏幕上用户看到的内容即可,之前之后的就不用绘制了
但由于如果只绘制屏幕显示区域的话,左右两侧的点需要计算path连接而且在滚动时文字的显示会有突然显示或隐藏的问题,所以把绘制区域加长,左右两侧均多绘制一个label的距离 绘制区域为绿色加红色
我们根据x轴方向当前已滚动的距离getScrollX()计算第一个显示的label下标,再加上控件宽度和一个label距离(右侧多绘制的一个label的距离)计算出最后一个label的下标,只需要绘制两个下标中间即可,其他的就是数学问题了。
多个点的连接使用的贝塞尔曲线,代码参考自: www.jianshu.com/p/98088ff77…
3. 触摸控制并处理多指触控问题(手指拖动图表可移动)
触摸控制是根据第一个event点移动的距离,调用view的scrollBy方法滚动view,主要代码如下
//处理滑动 计算现在的event坐标和上一个触摸事件的坐标来计算偏移量 决定scrollBy的多少 @Override public boolean onTouchEvent(MotionEvent event) { ... switch (event.getAction()) { case MotionEvent.ACTION_DOWN: ... //记录首个触控点的id mActivePointerId = event.findPointerIndex(event.getActionIndex()); ... mLastX = event.getX(); ... break; case MotionEvent.ACTION_MOVE: if (mActivePointerId == INVALID_ID || event.findPointerIndex(mActivePointerId) == INVALID_ID) { break; } //计算首个触控点移动后的坐标 float moveX = mLastX - event.getX(mActivePointerId); if (Math.abs(moveX) > IGNORE_MOVE_OFFSET) { ... mLastX = event.getX(mActivePointerId); scrollBy((int) moveX, 0); } break; case MotionEvent.ACTION_UP: mActivePointerId = INVALID_ID; mLastX = 0; ... break; case MotionEvent.ACTION_CANCEL: mActivePointerId = INVALID_ID; mLastX = 0; ... break; } return true; } 复制代码
scrollBy方法内部会调用scrollTo方法,重写了scrollTo方法在里面进行一些选中下标的判断和最小最大滚动位置的拦截
@Override public void scrollTo(int x, int y) { //默认左边缘为x最小值-半个控件的宽度 if (x < mMinPosition) { x = mMinPosition; } //默认右边缘为x最大值+半个控件的宽度 if (x > mMaxPosition) { x = mMaxPosition; } if (x != getScrollX()) { super.scrollTo(x, y); } mSelectIndex = scrollX2Index(x); } 复制代码
注意在move事件中需要根据第一个触控点id计算移动距离,直接调用event.getX()方法,会有多点触控问题(复现步骤:一个手指滑动后,按下第二个手指,第一个手指抬起,view会自动滚动) 因为后面会有点击事件的判断,所以在move时判断如果移动距离小于IGNORE_MOVE_OFFSET = 2.5时,忽略,这样当手机滑动比较慢时,会有部分滑动事件被忽略掉的情况,不过2.5这个值自己滑动时觉得体验还可以,再大的话慢速滑动会有卡顿,太小的话点击事件的判定会过于精确。
4. 惯性滚动(根据手指释放时的速度计算图表需要滚动的距离)
惯性滚动的实现需要用到VelocityTracker计算up事件时的速度,OverScroller处理fling事件 主要思路时,当up事件发生时,判断手指速度,若速度小于最小值, scrollBackToExactPosition()
直接将当前选中下标滚动到中心区域;若速度小于最大值按原速度计算否则按最大速度计算,根据此速度 当前x方向偏移量 可scrollTo的最小、最大值调用fling方法,并调用 invalidate();
方法, invalidate();
内部几次回调会调用view的draw方法,在view的draw方法中调用 computeScroll()
方法,若惯性滚动未结束,调用scrollTo方法将view滚动到该速度应滚动到的位置,再调用 postInvalidate();
,几次回调又会重新调用view的draw方法,循环调用scrollTo将view再进行滚动 如此实现惯性滚动 直至滚动结束
5. 回滚 (up时或者惯性滚动结束 需要回滚到选中位置)
这个主要也是数学题,需要回滚的距离过大时,使用OverScroller慢速回滚;若过小则立刻回弹
//触摸事件或惯性滚动结束后 应滚动到中心位置 private void scrollBackToExactPosition() { float rightPosition = mSelectIndex * mParent.getXLabelInterval() - (float) getWidth() / 2; if (Math.abs(getScrollX() - rightPosition) > IGNORE_OFFSET) { int dx = Math.round(rightPosition - getScrollX()); if (Math.abs(dx) > MIN_SCROLLER_DP) { //渐变回弹 mOverScroller.startScroll(getScrollX(), getScrollY(), dx, 0, 500); invalidate(); } else { //立刻回弹 scrollBy(dx, 0); 复制代码
6. 点击选中 (根据点击的坐标,计算需要选中的下标并选中)
点击事件的判定:最开始的想法是,判断事件如果是down紧接up即为点击,后来发现这种判定比较苛刻,因为有些点击事件会引起略微的move事件,所以在move事件中判断如果move距离较短,则忽略,这种方法的判定目前没有发现问题,如果大家有好的想法,欢迎讨论。 判定为点击事件后,要根据点击点的坐标位置和当前已滚动的距离,计算出点击点所在的下标,改变需要选中的下标,滚动到指定下标
这个控件的一点一个功能的实现,过程之中问题不断,问题解决又是惊喜,希望自己多些信心,多点努力,年后的第一篇博客,收拾行装,又上征程,加油,我们都是追梦人
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- JQuery选中select组件被选中的值方法
- JQuery选中select组件被选中的值方法
- python – Django检查是否选中了复选框
- 在ng-repeat内Checkbox默认选中
- Xshell 配置 鼠标选中即复制,右键即粘贴的功能
- 手撕一个让人 “欲罢不能” 的水波纹选中控件
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。