前言
上一篇做了一个滑动折叠的Header控件,主要就是练习了一下滑动事件冲突的问题,控件和文章写的都不怎么样。本来想通过这篇文章的控件,整合一下前面六篇文章的内容的,结果写的太复杂了,就算了,没有新的技术知识,功能也和之前的安卓滚动选择控件类似,不过在写的过程还是有点难度的,用来熟悉自定义view知识还是很不错的。
需求
这里我也不知道应该怎么描述这个控件,标题里用的大小自动变换的类ViewPager,一开始我把它叫做模仿桌面切换的多页面切换控件。大致就是和电视那种切换页面时,中间页面大,边上页面小,切换到中间会有变大的动画效果,我是觉得这样的控件很炫酷。
核心思想如下:
- 1、类似viewpager,但同时显示两种页面,中间为主页面,左右为小页面,小页面大小一样,间距排列
- 2、左右滑动可以将切换页面,超过页面数量大小不能滑动,滑动停止主界面能自动移动到目标位置
效果图
编写代码
这里代码写的还是挺简单的,没有用到ViewPager那样的Adapter,也没有处理预加载问题,滑动起来不是特别流畅,页面放置到顶层时切换很突兀,但是还是达到了一开始的设计要求吧!
1 | koltin复制代码import android.animation.ValueAnimator |
desktop_layer_layout_style.xml
1 | xml复制代码<?xml version="1.0" encoding="utf-8"?> |
主要问题
这里用到的知识之前六篇文章都已经讲过了,主要就是有几点实现起来复杂了一些,下面讲讲。
页面的自动缩放
讲解页面的缩放之前,需要先将一下页面的摆放。这里以四分之一为间距来摆放来自XML的view,第一个view放在中间,其他都在其右边按顺序排列。
所以页面的缩放,只和view的位置有关,而view的位置又只和当前控件左右滑动的距离有关,变量就是当前控件横坐标上的滑动值scrollX。根据view的原始index可以得到每个view可见时的滑动值范围,在通过这个范围和实际的滑动值scrollX,进行映射换算得到其缩放比例。这里用到了抛物线进行换算:
1 | css复制代码// 公式:y = 1 - (x - 1).toDouble().pow(2.0) |
滑动范围的限定
滑动范围的限定和上面类似,边界就是第一个或者最后一个view移动到正中间的范围,只要实际的滑动值scrollX在这个范围内,那滑动就是有效的。
页面层级提升与恢复
页面层级的提升在我之前文章:手撕安卓侧滑栏也有用到,就是自己把view放到children的最后去,实际上ViewGroup提供了类似的功能:bringChildToFront,但是原理是一样的。
1 | java复制代码 @Override |
这里的提升view不止一个了,而且后面还要恢复,即不能打乱children的顺序。所以我在onFinishInflate中用一个数组保存下这些子view的原始顺序,使用的时候用这个数组就行,children里面的顺序不用管,只要让需要显示的view放在最后就行。我这里因为间距是四分之一的宽度,最多可以显示五个view,所以在onLayout的最后将这五个view得到,并按顺序放到children的最后。
onDraw探讨
这里我还想对onDraw探讨一下,一开始我以为既然onMeasure、onLayout中都需要去调用child的measure和layout,那能不能在onDraw里面自己去绘制child,不用自带的,结果发现这是不行的。onDraw实际是View里面的一个空方法,实际对页面的绘制是在控件的draw方法中,那重写draw方法自己去绘制child呢?实际也不行,当把draw方法里面的super.draw时提示报错:
也就是说必须继承super.draw这个方法,点开源码发现,super.draw已经把child绘制了,而且onDraw方法也是从里面传出来的。所以没办法,乖乖用bringChildToFront放到children最后去,来提升层级吧,不然也不会提供这一个方法来是不是?
本文转载自: 掘金