今天来更新一发“自定义实现 ViewPager 指示器”。 ViewPager 指示器相信大家都用过吧,从一开始 JW 大神的 ViewPagerIndicator ,到现在 Material Design 中的 TabLayout 。GitHub 上还有其他形形色色的指示器。那么肯定有人会问:既然有了这么多的指示器可以用,那为什么还要自己自定义呢?其实,我们学习了自定义指示器之后,可以知道 ViewPager 指示器的原理,还可以提高我们代码的水平哦!那还等什么,一起来学习吧。
首先放上一张效果图,亮亮眼:
接下来我们来大致地分析一下思路: ViewPager 指示器我们可以看作是一个横向的 LinearLayout ,相对应的 Tab 可以直接使用 TextView 来实现。而 LinearLayout 中有许多个 TextView ,当我们点击其中的 TextView 时, ViewPager 就切换到对应的 item 上。而当我们手动滑动 ViewPager 时,根据 OnPageChangeListener 来动态地改变指示器。好了,基本上思路就是这样了,下面就来看看代码了。
自定义的属性 attrs.xml :
1 | <?xml version="1.0" encoding="utf-8"?> |
自定义的属性基本上就以上几种,如果自己有其他的需求,可以另外添加。
之后我们就创建一个类,名字就叫 ViewPagerIndicator 了:
1 | public class ViewPagerIndicator extends LinearLayout { |
上面的代码主要是初始化了自定义属性,还有得到了 tabWidth 以便后面使用。
当然,如果用户旋转了屏幕,那么 tabWidth 是会改变的。所以我们应该在 onSizeChanged
里重新赋值:
1 | @Override |
这样,无论是横屏还是竖屏,在屏幕上可见的 Tab 数量永远是固定的(即 visibleTabNum 的值)。之后,我们先来“画”出 Tab 被选中时底下的那条横线。
1 | @Override |
mViewPager 的赋值是在setViewPager(ViewPager viewPager)
方法中完成的,这个方法放在下面去讲。而其中的 offset 是偏移量。当用户滑动切换 ViewPager 时,Tab 底下的横线应该也要做相应的位移,而这就是由 offset 来完成的。调用 setOffset(float offset)
方法,可以引起视图重绘。另外横线的高度 indicatorHeight 可以由用户自定义的,这里的代码还是比较简单的,相信大家都可以看懂的。
到这就来讲讲 setViewPager 方法了。当我们想要把 ViewPager 和 ViewPagerIndicator 关联起来时,可以给外部设置一个 setViewPager(ViewPager viewPager)
方法,那下面就是该方法的源码了:
1 | /** |
从方法内部可以看出,我们要得到 ViewPager 的 adapter 。如果 adapter 为空则抛出异常。之后根据 adapter 的 count 数量去创建相对应的 TextView 作为 Tab 。下面为 createTextView
方法:
1 | // 添加textview到ViewPagerIndicator中 |
在 createTextView
方法中,使用了addView 来动态地添加 Tab 。这里有一处比较巧妙的地方:我们把当前 TextView 的索引 i 存储到了 Tag 中。而当用户点击 Tab 时,在监听器中我们取出那个 Tag 值,这样就知道了用户点击的是哪个 Tab 了,并且让 ViewPager 切换到那个页面下。
好了,我们再回过头继续看之前的 setViewPager(ViewPager viewPager)
方法,我们看到给 viewPager 设置了 OnPageChangeListener 。在 OnPageChangeListener 的 onPageScrolled 方法中,根据当前的 position 和 positionOffset 就可以完成选中时那条横线的移动。并且为了选中的 Tab 出现在屏幕中,ViewPagerIndicator 也要用 scrollTo 方法来做相应地移动。而在 onPageSelected 方法中,我们把选中的 Tab 中的字体颜色更改为已选中的颜色,之前选中的改成未选中颜色。
到这里,整体完成得差不多了。但是如果我们想让 ViewPagerIndicator 可以滑动的话,还要重写 onInterceptTouchEvent(MotionEvent ev)
和 onTouchEvent(MotionEvent event)
两个方法。
1 | @Override |
在 onInterceptTouchEvent(MotionEvent ev)
中,若滑动的距离超过 touchSlop ,则拦截该触摸事件自己处理,否则传递给子View。而在 onTouchEvent(MotionEvent event)
中,使用了 scrollBy 来处理滑动,并且设置了边界值的检查。
在这里,整体代码讲解完成了。其实 ViewPagerIndicator 本质就是使用了 OnPageChangeListener 以及当用户点击时切换 ViewPager 到指定页面,并没有太难的地方。以后我们自己也可以实现各种炫酷的 ViewPagerIndicator 了!
下面提供源码的下载链接:
have a nice day !~~