您的位置:首页 > 移动开发 > Android开发

一个文件实现安卓滚轮选择控件

2017-05-27 10:14 302 查看

一个文件实现安卓滚轮选择控件

标签:
安卓轻量级滚轮
2017-05-27 10:14
234人阅读 评论(3)收藏举报

本文章已收录于:


分类:

Android(1)




作者同类文章X

版权声明:本文为博主原创文章,未经博主允许不得转载。

目录(?)[+]
先看看效果
用法
原理
测量
触摸事件
绘制
总结

这一次跟大家分享的是一个非常轻量的滚轮选择控件,只有1个文件,一共400多行代码,源码已经上传到github:
https://github.com/huzenan/EasyPickerView

我们知道如果想在项目里,尤其是大的项目里使用别人造好的“轮子”,假如说我只想用一个好看的滚轮选择控件,而必须引入好几个文件甚至十几个文件,那真的是很不情愿的。这个时候,非常轻量级的EasyPickerView就能解决问题了,只要一个文件,代码也很少,心情好了还能做些修改以适应自己的需求,还不是美滋滋的。

先看看效果



用法

效果还是很棒棒的,用法也很简单,首先是布局文件:



支持以下几个属性:

- epvTextSize:字符的大小

- epvTextColor:字符的颜色

- epvTextPadding:字符的间距

- epvTextMaxScale:中间字符缩放的最大值

- epvTextMinAlpha:两端字符最小alpha值

- epvRecycleMode:是否为循环模式

- epvMaxShowNum:显示多少个字符

注意:epvTextMaxScale不能和epvTextMinAlpha设置相同的数值,否则不会显示。

看完会发现,如果最后一个属性epvMaxShowNum设置了偶数会怎么样呢,效果如下:



设置为偶数的时候,字符上下各显示一半。

然后就是在使用时设置一下数据集,再设置一下监听了:



原理

整个View的原理,是在触摸过程中改变一些属性值,然后重绘整个View来达到滚动的效果。过程中用于控制滚动效果的属性值包括:

- curIndex:当前选中项

- offsetIndex:本次滚动中偏移的项

- offsetY:本次滑动的Y坐标偏移量

- oldOffsetY:在fling之前的offsetY

后面会解释这几个属性值的作用。

1.测量

首先是测量,宽度的测量比较简单,match_parent模式下的宽度直接使用父布局给的建议宽度,wrap_content模式下的宽度为:

宽度 = 最大字符宽度 + 左右padding

其中“最大字符宽度”需要根据设置的数据集进行更新,以数据集里最宽的那个数据的宽度为准。

然后是高度的测量,match_parent模式下使用建议高度,wrap_content模式下,首先计算单个字符高度,然后计算内容区域的高度:

内容高度 = (单个字符高度 + 字符间距) x 显示的字符数

这里为啥字符间距不是 (显示的字符数 - 1) 个呢,其实是为了在上下两端各留出一半字符间距的距离,这样在滑动的时候不会显得很突兀(可以给上下两端的字符一个消失前缓冲的空间),直接看下图解比较清晰:



2.触摸事件

由于要实现类似ListView的fling快速滑动的效果,我们需要使用Scroller和VelocityTracker,简要的讲,Scroller用于跟踪滑动的轨迹,VelocityTracker用于跟踪触摸点的速度。

我们重写onTouchEvent方法,每一个触摸事件都要跟踪触摸点的速度,因此最开始先更新一下VelocityTracker:



然后看下ACTION_DOWN



当手指按下时,若还处于滚动状态,则先结束滚动,包括让Scroller停止对轨迹的跟踪,和停止整个View的滑动(finishScroll方法做一些恢复操作),然后记录按下点的Y坐标值。finishScroll方法在ACTION_UP中也有调用,因此等到下面再讲。

接着是ACTION_MOVE



这里使用offsetY来记录手指距离按下点的Y坐标的偏移量(向上滑动时offsetY为负数,向下滑动时offsetY为正数),并使用isSliding来记录本次触摸事件是否触发了滑动状态,若没有则在手指抬起时判定为一次点击事件。首次move事件时isSliding为false,此时需要手指滑动距离大于一个阈值时才开始滑动,这个阈值即scaledTouchSlop,同样的也限制了最大以及最小的滚动速度(后面用到),这几个值都可以通过ViewConfiguration获取系统建议的值,以保持和系统一样的风格:



之后的move事件,由于isSliding设置为true,因此不需要再判断阈值了。然后调用了reDraw方法:



reDraw方法首先计算了当前选中项curIndex在该事件过后需要偏移的量,为整数值,看图解清晰些:



如上图得到的偏移量 i 值即为2,接着判断,如果是循环模式则可以直接更新offsetIndex,否则还需要计算偏移过后是否在数据集的范围内,是才更新offsetIndex。然后通过postInvalidate方法刷新视图。若以上条件都不满足,则直接结束滚动。

最后是ACTION_UP



获取Y方向的速度getScrollYVelocity方法很简单:



最后将isSliding设置为false表示本次触摸已经结束,接着回收VelocityTracker:



可是为啥这样就能让整个View做到像ListView那样的fling效果呢,原因是我们调用了Scroller的fling方法,把计算出来的Y方向上的速度作为初速度传递给了Scroller,且将Y方向上的值设置为0,并且调用了invalidate进行了重绘,此时会回调View的一个方法computeScroll:



我们在ACTION_UP时记录了fling开始前的偏移量oldOffsetY,再加上Scroller的getCurrY方法获取计算出来的当前滑动的偏移量,即为新的offsetY。

reDraw已经看过,现在来看看finishScroll:



步骤有点多,但都是一些判断与计算,首先是结束后的停留位置(需要向上还是向下滚动多一行),v 值计算的是结束时offsetY不满一个padding的距离,还是看看清晰的图解:



计算出 v 值后会判断,如果大于 centerPadding/2 ,则说明是向下滑动,且超过了centerPadding的一半,需要在当前偏移量上加1,否则如果小于 -centerPadding/2 ,则说明是向上滑动,并且也超过了centerPadding的一般,需要在当前偏移量上减1。

下一步会重置当前选中项,看看重置的方法:



很简单的计算,循环模式下,用模除计算出此时的选中项,非循环模式下限制滚动的范围即可。

接下来是计算回弹的距离,也可以说是纠正滑动距离,这里用最终计算得到的offsetIndex(index此时已纠正过),乘以centerPadding得到完整的偏移距离,再减去offsetY就得到需要纠正的偏移距离。当然这只是一种计算方法,也可以使用 v 值来计算,很随意的。

然后是更新我们的监听接口,接着reset重置上面提到的四个属性值,最后调用postInvalidate重绘。

至于moveTo,原理和处理move事件相同,只不过此时调用的是Scroller的startScroll方法而不是fling方法。而moveBy更简单,传入的是偏移的数量,再通过getNowIndex计算得到此时的选中项,然后直接调用moveTo方法即可。

就这样,我们在处理触摸事件的过程中,更新了偏移量和选中项,这些数据将在每一次绘制的时候,决定着某一个数据项应该绘制在什么位置。

绘制

所有属性值相关的操作都讲完了,接下来只剩下绘制了,重写onDraw方法,我们分步来看看。

首先是裁剪出我们的内容区域,后面在绘制字符时会讲为什么需要裁剪,这里cx和cy分别是整个View的中心点x和y的坐标值,在测量后就可以得到:



接着开始绘制字符。首先计算出数据集长度size,centerPadding为每一行字符加上padding占多高 ,至于half参数,是为了实现中间向两端滑动时逐渐变小的效果,以中间项为标准,分别往两端计算:



接着开始遍历数据集,并根据以上的参数,把范围内的数据绘制到屏幕中:



计算稍微有点酸爽,我们一个个看。首先是遍历的范围,看看index的取值,我们假设最多显示3个,curIndex为0(可以忽略不看offsetIndex,因为其作用只是在本次触摸中将curIndex增加一个偏移量而已),此时index取值范围是-2到2,上下两端各多出1个,这就是为什么需要剪裁了,保证多余的不显示在区域内,并且保证在滑动时两端能有字符滑入显示区域内,看下图解:



接着判断如果是循环模式,则在超过数据集范围时更新下index,然后开始绘制字符,其中scale表示在1.0基础上,增加的textMaxScale的百分比,然后设置一下画笔,再绘调用Canvas的drawText方法制字符即可。这里drawText方法的第三个参数为字符的baseLine,若想将字符以某个位置居中显示,baseLine可以这么算:

baseLine = 绘制的中点的Y坐标 - (上坡度 + 下坡度) / 2

具体的概念就不展开来讲了,网上有很多文章都有具体的讲解,可以学习学习。

总结

总结一下,整个View的核心原理就在于处理偏移量和选中项,然后通过这几个属性进行绘制,因此无论什么样的View,制定好规则,只要根据这样的思路都能很轻易很快速地实现了。

欢迎拍砖,也欢迎关注我的github:
https://github.com/huzenan



顶 3 踩 0
 
 
上一篇Android共享元素场景切换动画的实现

  相关文章推荐


Github安卓流行布局开源库

2017年最牛逼的分类Android项目源码免费一次性打包下载!

2017年最牛逼的分类Android项目源码免费一次性打包下载!

Android 开源组件和第三方库汇总

Android 开源组件和第三方库汇总

140款Android开源优秀项目源码

Android 1000实例代码集结(二 )

各种Android UI开源框架

GitHub最常用的开源库总结

2017年Android开源项目及库汇总
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android 安卓
相关文章推荐