前两天做一个项目的时候,有这么个需求,如下图:
整个实现用的是TabPageIndicator + ViewPager ,现在的要求是上面的TabPageIndicator 和 下面的 ViewPager 都要能根据相应的数据做到TabPageIndicator限制是否可以点击(只限制点击,要保持能滑动)以及ViewPager是否可以滑动。(蛋疼的需求)
首先看到这个问题的时候,第一想到的时候看源码有没有提供类似的处理机制,很遗憾,我没有找到。
那看来只能自己来实现了,首先是TabPageIndicator,一开始看到它有个setEnable()方法,以为这么简单就搞定了,结果才发现其实是然并卵。其实这个方法是因为它继承了HorizontalScrollView 带过来的方法,真正是不起作用的。
既然简单的实现不了,那就只能,拿出本办法了,查看源码再做相应的修改了。
在查看源码的过程中,我主要看到有这么两个方法:
private void addTab(int index, CharSequence text, int iconResId) {
final TabView tabView = new TabView(getContext());
tabView.mIndex = index;
tabView.setFocusable(true);
tabView.setOnClickListener(mTabClickListener);
tabView.setText(text);
if (iconResId != 0) {
tabView.setCompoundDrawablesWithIntrinsicBounds(iconResId, 0, 0, 0);
}
mTabLayout.addView(tabView, new LinearLayout.LayoutParams(0, MATCH_PARENT, 1));
}
可以看到在这里,它对每一个tabView进行了相应的设置,并监听了点击事件。然后是接下来的这个重点方法了:
@Override
public void setCurrentItem(int item) {
if (mViewPager == null) {
throw new IllegalStateException("ViewPager has not been bound.");
}
mSelectedTabIndex = item;
mViewPager.setCurrentItem(item);
final int tabCount = mTabLayout.getChildCount();
for (int i = 0; i < tabCount; i++) {
final View child = mTabLayout.getChildAt(i);
final boolean isSelected = (i == item);
child.setSelected(isSelected);
if (isSelected) {
animateToTab(item);
}
}
}
可以看到它重写了ViewPager的setCurrentItem()方法。我想看到这一步的时候,小伙伴们是否已经明白了?在这里我们要实现的代码片段就出来了。
public void setTabViewState(int max){
final int tabCount = mTabLayout.getChildCount();
for (int i = 0; i < tabCount; i++) {
final View child = mTabLayout.getChildAt(i);
if (i <= max) {
child.setEnabled(true) ;
} else {
child.setEnabled(false) ;
}
}
}
看吧,这就是我抽取出来的公共方法,如法炮制有木有。一个循环,一个限制,搞定。
好吧,既然TabPageIndicator已经搞定了,那么剩下的就是ViewPager了,一开始觉得这块应该也很简单,但实际上却花了些时间。
为了避免重复制造轮子,我先去伟大的百毒上搜了一圈,发现也有人遇到过需要限制ViewPager滑动的问题,顿时眼前一亮,索嘎,顿时觉得会上网真是件极好的事情。but…..,一个个试了之后发现限制是可以限制了,但是跟我项目的需求有出入,又一个然而并没有什么卵用!!
好吧,既然不符合,那我就自己在这个基础改改总行了吧,嗯,想到就开始行动。折腾来折腾去,发现还是不行,总是差那么一点。看来这最后一步才真的是决定性的一大步啊!
好,正逢抗战70周年,就好好发挥下我们劳动人民的一不怕苦,二不怕累的勤劳精神吧。
接下来可得来点干货了,不然要被打了估计。
在解决这个问题之前我们很有必要搞清楚Android 上的Touch 事件分发机制。在这里非常感谢sunzn’Blog博主的这篇文章,地址是:点击跳转
在了解了Touch事件分发机制后,我决定重写onTouchEvent()方法,具体原因就不说了,相信你们看了上面那篇文章已经明了。(ps:我会告诉你其实我也不知道,哈哈,尼玛,坑爹有木有!!)
首先说下思路,我觉得这个远远比之前提供源码要好得多,在这之前呢,我查看了ViewPager的源码实现,具体请自行百毒查看。或者你可以查看这篇博文:点击跳转。发现ViewPager的滑动也是处理Touch事件。那就简单了,我只需要在点击滑动的时候,首先判断是左滑还是右滑,然后在相应的地方处理下自己的业务逻辑,做出下判断,如果达到设定的限制,就拦截事件,否则就不处理。好了,有了理论基础,就要开始实践了。
代码如下:
package com.viewpagerindicator.sample.view;
import android.annotation.SuppressLint;
import android.content.Context;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
public class CustomViewPager extends ViewPager {
private int maxLimit = 0 ;
private float start = 0 ;
public int getMaxLimit() {
return maxLimit;
}
public void setMaxLimit(int maxLimit) {
this.maxLimit = maxLimit;
}
public CustomViewPager(Context context) {
super(context);
}
public CustomViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN :
start = event.getX() ;
Log.i("CustomViewPager", "start==--> "+start);
break;
case MotionEvent.ACTION_MOVE :
float med = event.getX() ;
int difference = (int) (med - start) ;
Log.i("CustomViewPager", "med==--> "+med+",difference--->"+difference);
if (difference > 0) { // 右滑, 页面向左
Log.i("CustomViewPager", "右滑");
} else if (difference < 0) {
Log.i("CustomViewPager", "左滑");
int item = getCurrentItem() + 1 ;
if (item > maxLimit) {
Log.i("CustomViewPager", "maxLimit");
return true ;
}
}
break ;
case MotionEvent.ACTION_UP :
float end = event.getX() ;
Log.i("CustomViewPager", "end==--> "+end);
break ;
default:
break;
}
return super.onTouchEvent(event) ;
}
}
代码未做清理,弄好了,赶紧跑一下,是骡子是马得拉出来溜溜才知道,一跑,大功告成!(运行结果看最开始的图。)YEAR,nice!今天真是个好日子,BTW,今天我生日,在这个好日子里,没别的啥庆祝的,特此记录下这个功能,希望能帮助到有需要的朋友。(ps:唉,由于本人不怎么写博客,技术也一般,请各位看官留情,小弟感激涕零!)
在文章最后,我会把整个工程上传。
ps:由于第一次上传的资源文件,代码被加密了,因此重新上传一份未加密的。对之前下载过的兄弟说句抱歉了。
下载地址