开发项目需求,需要写一个可伸缩的头部。本来打算直接使用Material支持库来实现效果,Android Design Support Library 的 代码实验——几行代码,让你的 APP 变得花俏。后来发现,这个库目前不能很好的支持复杂的内容,尤其是包含多个tab的时候。Google了一下,一些库都是只支持基本布局的,实现起来不方便,于是只好自己写了。正好之前研究了一番UltraPullToRefresh的实现方法,原理相通,修改了一些逻辑就大功告成了。
源码地址: https://github.com/captainbupt/PseudoMaterialHeader
下面是效果图(虽然界面是丑了点,不过都是可自定义,美化工作很轻松):

来一个开发完成的效果图,看起来好多了~

一、 基本模块
之前的博客中已经讲解过MotionEvent的传递机制了,
MotionEvent事件传递个人总结。因此,想实现这个效果,主要是需要自定义一个ViewPager,来自己分配MotionEvent事件。同时,为了实现自定义效果,header和content都是通过接口来实现事件的传递。
主要类有:
- ContentHandler:Content类需要实现的接口,用来判断是否到顶端(如,ListView第一项显示)。其中DefaultContentHandler已经支持了大部分的基础View
- HeaderHandler:Header类需要实现的接口。其中的方法会被父布局给调用,从而实时的通知Header 高度的变化,包括当前比例。Header可以根据这些信息自定义动画效果。
- MaterialHeaderLayout:继承自ViewGroup。会负责MotionEvent事件的传递和分发,以及屏幕的滚动效果。
- MotionEventIndicator:一个辅助类。用来记录MotionEvent的值,并且计算偏移量,时间,是否到达边界值等。
二、 ViewGroup实现 主要重写的方法如下:
-
onFinishInflate:用来统计子view数量,并分配header和content -
onMeasure:重绘时会调用,用来获取header的高度,同时使子view也开始计算其自身宽高 -
onLayout:重绘时会调用,用来定义子View的位置
-
dispatchTouchEvent:主要的逻辑代码。通过Indicator的辅助,计算MotionEvent的传递方法。主要的判断在ACTION_MOVE中,通过判断方向和速度来进行滚动或者传递。 -
SchollerChecker:辅助滑动类。保证滑动准确无误。
大部分的代码和逻辑都参照了UltraPullToRefresh的实现。
三、坑 遇到的最主要的一个问题是关于LayoutParams的问题。之前没有进行详细研究,导致一直报ClassCastException。参考网上文章,原来每个ViewGroup基本都定义了自己的LayoutParams,用来实现不同的布局效果(如LinearLayout只关心权重和margin,而RelativeLayout则需要获取各种依赖属性)。而默认的ViewGroup.LayoutParams是只包含高和宽这两个属性的,而我们在计算高度的时候使用到了margin,因此会产生不兼容的问题。查阅资料后得知,ViewGroup是通过调用generateLayoutParams()来传入xml文件中的参数的,因此需要重写这个方法,并自己定义一个LayoutParams读取AttributeSet中的各个属性。同时,也要重写checkLayoutParams(),才能保证布局验证通过。
另外在传递事件的时候,考虑到content中可能需要横向滑动,因此不能够完全屏蔽MotionEvent事件,因此修改MotionEvent中的Y值,将横向滑动事件传递了进去。不过实际验证发现,这样会造成严重的卡顿效果,可能是跟同时滑动有关。因此,还是使用了比较传统的方法,即通过判断角度来决定是否传递事件。
四、小结 通过这样一个重写,对MtionEvent的传递机制理解的更深了。同时,通过模仿UltraPullToRefresh,自己尝试了dispatchTouchEvent的操作过程。相信以后如果还需要使用到类似的动画效果,也能够很快的自己实现出来。Anyway,虽然不提倡重复造轮子,但一定要明白轮子是怎么造的,只是简单的使用别人开发好的控件,还是无法使自己的技能水平得到进阶。
|