Android自定义View之区块选择器

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android自定义View之区块选择器相关的知识,希望对你有一定的参考价值。

参考技术A 先来看下效果吧:

我们来分析这个view需要实现哪些效果。

别害怕有这么多的功能,我们一个一个来实现。首先是刻度尺,这个简单。由于完整的刻度尺是比屏幕宽度大的,因此我们先来了解几个概念:

这里手机屏幕的宽度是width,刻度尺的宽度的时maxWidth,我们其实只需要绘制手机屏幕可见的部分就可以了,这里的offset表示手机屏幕的左边与刻度尺左边的偏移量。

了解了这个概念,我们就来开始写吧,定义一个View,处理下构造都指向3个参数的那个,然后统一做初始化:

我们在onMeasure中处理了wrap_content的高度。然后在onSizeChanged中获取尺寸参数:

接着就开始绘制吧:

这里的titles代表了刻度的标识,每一个元素代表一个刻度(这里我字节写死了,实际上可以通过方法set,也不一定是时间,能代表刻度的都可以)。通过rate设置长短刻度的比例,这里我设置了1:1。运行一下看看,目前仅仅能看到从0开始,看不到完整的刻度尺,我们需要实现touch事件产生移动才有效果。

我们重写onTouchEvent来实现滑动效果:

我们计算出每次move事件的X方向的变化量dx,然后通过这个dx改变offset,并且处理一下边界的情况。然后调用postInvalidate刷新界面。
运行一下看看!现在我们可以滑动刻度尺了。但是好像还有点问题,平时我们使用ScrollView的时候用力划一下,可以看到手指离开了屏幕,但是内容还可以继续滚动。而目前我们自定义的这个view只能通过手指滑动,如果手指离开屏幕就不能滑动了。这样的体验显然不够好,我们来实现这个惯性滑动的效果吧!

要实现惯性滑动,我们需要用到两个类:VelocityTracker,OverScroller。
VelocityTracker简介
view滑动助手类OverScroller

velocityTracker.computeCurrentVelocity方法的第二个参数表示最大惯性速度,这里我设置8000,避免刻度尺过快的滑动。通过调用scroller.fling方法将计算出的速度交给scroller,然后在computeScroll方法中获取当前值,并与上一次的值做差算出变化量dx,同样用这个dx变化offset刷新界面实现滑动效果。

刻度尺完成了,接下来是不可选的灰色区域。我采用两个int值表示在刻度尺的区域,刻度尺的每个刻度表示一个最小单位,前一个int表示在刻度尺的起始位置,后一个int表示占据的刻度数量。

我用一个list存放设置的不可选区域,然后在另一个list中存放转换成RectF的位置信息。这里的RectF是在相对于整体刻度尺而言的,因此绘制到屏幕的时候需要减去offset,并且需要考虑只有部分在屏幕可见的情况。避免在onDraw方法中创建过多临时变量,我声明一个成员变量tempRect,用来保存绘制时的临时参数。

完成了不可选区域,可选区域也是同样的。由于只能有一个可选区域,我们只需要定义一个RectF。额外需要考虑与不可选区域相交时会变色,我定了一个overlapping表示是否相交,通过RectF的intersects方法判断。

通过前面的分析,我们知道这个view中的事件有很多种:点击,移动刻度尺,移动选中区域,扩展选中区域。我们定义这四种类型便于后续的事件处理:

然后改造一下onTouchEvent:

performClick会在你重写onTouchEvent时as提示你需要重写的方法,因为你可能没有考虑到如果给这个view设置OnClickListener的情况。如果你没有在onTouchEvent中调用performClick,那么setOnClickListener方法就失效了。

你可能注意到这一次比较复杂,并且还有一个linking字段,表示是否正在联动,我解释一下这个联动的概念:通过gif其实你可能注意到,当我移动或者扩展选中区域的时候,如果移动到了屏幕的边界,后面的刻度尺就会跟着移动,实际上这个时候选中区域在屏幕中的位置没有改变,只是刻度尺移动了。一开始我也是通过dx来改变offset,但是存在一个问题,移动到屏幕边缘之后,手指可以移动的区域已经很小了,不会产生足够的dx(手指不移动的话,不会有新的touch事件产生)。最好的体验是我把手机移动到屏幕边缘,刻度尺就会自己按照一定的速率移动直到最大offset或者最小offset。于是我使用了Handler,当满足条件后发送消息,表示开始进行联动,会按照固定速度产生一个dx改变offset。当然,在离开屏幕边缘的时候还需要及时取消handler的任务。

至此,功能基本已经实现了,运行一下看看效果吧~

后面需要做什么那?现在这个view只能自己玩,我需要它与其他view有交互,比如选中什么区域,状态的改变生么的。

声明两个接口,并在适当时候回调它们的方法,这样外部就能感知view的状态变化。

后面的话就是根据业务添加一些api了,例如添加不可选区域,改变刻度范围什么,一切都看需求了。

想学习更多android知识,或者获取相关资料请加入Android开发交流群:1018342383。 有面试资源系统整理分享,Java语言进阶和Kotlin语言与Android相关技术内核,APP开发框架知识, 360°Android App全方位性能优化。Android前沿技术,高级UI、Gradle、RxJava、小程序、Hybrid、 移动架构师专题项目实战环节、React Native、等技术教程!架构师课程、NDK模块开发、 Flutter等全方面的 Android高级实践技术讲解。还有在线答疑

Android自定义View(9) 《动画 插值器简介》

参考技术A 在Android中,我们经常会需要去绘制一些自己需要的控件,所以继承自View的自定义View就产生了。这篇文章主要介绍动画中的一些常用的插值器。关于插值器的使用这里就不再叙述了,有需要可以查看 Android自定义View(5) 《自定义View,动画篇 视图动画》 。

加速减速插值器,刚开始和结束的时候速度会比较慢,中间会加速

加速插值器,速度越来越快

减速插值器动画开始的时候速度加速到最大值,接着越来越慢

线性插值器,速率保持恒定

弹性插值器,模拟了控件来回弹跳的样子‘

初始偏移插值器,会在动画开始的时候先向前偏移然后再开始动画

结束偏移插值器,表示在结束时沿动画方向继续运动一段距离再结束动画

开始和结束均偏移,是AnticipateInterpolator和OvershootInterpolator的合体。

循环插值器,其构造函数

其实系统的插值器使用都是比较简单的,那么我们如何来自定义一个属于自己的插值器呢?首先我们来看一下LinearInterpolator的源码

在这里我们要注意有这个方法

这就是我们自定义插值器的关键了,我们需要实现Interpolator接口,并复写上述方法。该方法的input就是动画的执行进度,范围是0~1,根据动画的执行时间匀速输出,我们通过这个值来计算出动画实际的动画值,这个概念看过 Android自定义View(6) 《自定义View,动画篇 属性动画 ValueAnimator》 就可以理解了。线性插值器在这个方法中输出了input,也就是未做任何处理直接输出了,所以动画也就按0~1的进度匀速执行了。
接下来我们开始定义一个属于自己的简单的插值器,我们来直接实现动画的倒序播放

内容很简单,就是将原本0至1的输出结果,变为1至0,也就是倒序播放
接下来我们用之前的缩放例子来测试一下我们的插值器

这里我们可以看到我们原本设置的是从1缩放到0.5,实际的运行结果却是从0.5开始放大到1,所以使用了我们的插值器后成功实现了倒放,自定义的插值器成功了~

视图动画仅支持插值器的使用,但是这里我们再介绍一个Animator特有的Evaluator的用法,刚刚我们说了,自定义插值器其实就是将动画的进度在指定的时长内分成了0到1,用来表示动画执行的进度。在介绍Evaluator之前呢,我们再来看之前我们使用的ValueAnimator。

我们通过addUpdateListener来监听当前的动画值,而这个动画值与我们所设定的范围和动画的进度是有直接关系的,那么如何将我们的动画进度与具体的动画值联系到一起呢,这个时候就需要用到我们的Evaluator了。比如我们此时初始化了一个0f到500f的动画,那么我们可以自定义在0到1的动画进度内如何去返回具体的动画的值。

这里呢我们实现了最基础的线性变化的Evaluator,fraction也就是动画的进度,范围0到1,我们就在动画开始值的进度上加上跟随时间线性变化的值,所以我们在这里实现了和线性插值器一样的动画,注意TypeEvaluator的泛型要与你动画中的设定的值动画的类型一致,否则会出错。

下面我们再修改一下,把它变成瞬移,在动画执行到一半时直接放在动画结束的位置

当进度超过0.5时我们直接返回结束的动画值,否则就放在初始值不动

所以再次验证了我们的Evaluator生效啦

动画值的控制一方面可以利用插值器,另一方面Animator也可以使用Evaluator来实现各种插值器所完成的效果,所以在实际的开发中我们可以灵活运用~

以上是关于Android自定义View之区块选择器的主要内容,如果未能解决你的问题,请参考以下文章

flowable设计器自定义自己的人员选择器

LayUI laydate日期选择器自定义 快捷选中今天昨天 本周本月等等

LayUI laydate日期选择器自定义 快捷选中今天昨天 本周本月等等

Xamarin Forms Shell 如何使用自定义渲染器自定义选项卡

Android 自定义view之悬浮动画

Android面试自定义View