【情景】
公司需要实现这么一个效果:
首先是一个可以左右滑动的ViewPager,当中的每个页面都有一个listView,而且需要实现下拉刷新功能。
【想法】
关于下拉刷新,直接使用GitHub上面的开源项目Android-PullToRefresh,毕竟使用起来很方便。
关于ViewPager,嵌套Fragment实现左右滑动,很传统的用法,没有什么多说的。
【问题1】
当我按照想法写出来后,基本实现了功能,但是有一个小bug,具体情况是这样的:
1、当我手指在ListView的Item上进行左右滑动的时候,并不能触发ViewPager的左右滑动切换。
2、当我手指在空白处或者在Item的分割线上进行滑动,才可以触发ViewPager的切换。
3、当我手指在ListView的Item上进行左右滑动的时候,有时会触发Item的点击事件(和滑动距离、速度有关)。
【分析1】
很明显是滑动冲突,而且第三点尤为重要,也就是说我进行左右滑动的时候,ViewPager并没有拦截该滑动事件,而是传递给了Item,Item拦截了后触发了点击操作。
【解决1】
因为用到了第三方库,为了其他地方使用时的安全稳定,我没有进行修改PullToRefreshListView,而是修改了ViewPager,使得当我进行左右滑动的时候它可以拦截并消费事件。
代码如下:
public class ViewPage extends ViewPager {
private int lastX;
private int lastY;
public ViewPage(Context context) {
super(context);
}
public ViewPage(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean intercepted = false;
int x = (int) ev.getX();
int y = (int) ev.getY();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
//默认不拦截down事件,否则子view将无法收到事件
intercepted = false;
break;
case MotionEvent.ACTION_MOVE:
int deltaX = x - lastX;
int deltaY = x - lastY;//未使用到
//如果水平位移大于30,则拦截
if (Math.abs(deltaX) > 30) {
intercepted = true;
} else {
intercepted = false;
}
break;
case MotionEvent.ACTION_UP:
intercepted = false;
break;
}
lastX = x;
lastY = y;
return intercepted;
}
}
【结果1】
很出乎意料的是,这次事情变得更加严重了。无论我怎么滑,ViewPager都不能左右切换了。
【分析2】
我试了试,在空白处滑动也没有效果。
我坚信解决思路是没有问题的。
忽然间想起在ViewPager中嵌套ListView时,是不需要进行滑动冲突的处理的,因为ViewPager内部已经帮我们处理了这个冲突。
所以我无法在空白处滑动的原因很简单,就是我抛弃了ViewPager的滑动冲突的处理结果。
【解决2】
最后的返回结果中,如果我自己判断需要拦截或者ViewPager自己判断出需要拦截时,都需要进行拦截。代码如下:
public class ViewPage extends ViewPager {
private int lastX;
private int lastY;
public ViewPage(Context context) {
super(context);
}
public ViewPage(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean intercepted = false;
int x = (int) ev.getX();
int y = (int) ev.getY();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
intercepted = false;
break;
case MotionEvent.ACTION_MOVE:
int deltaX = x - lastX;
int deltaY = x - lastY;
Log.e("ViewPage", "deltaX=" + Math.abs(deltaX) + ",deltaY=" + Math.abs(deltaY));
if (Math.abs(deltaX) > 30) {
intercepted = true;
} else {
intercepted = false;
}
break;
case MotionEvent.ACTION_UP:
intercepted = false;
break;
}
lastX = x;
lastY = y;
//只修改了最后的返回结果
//ViewPager自己处理了滑动冲突事件,所以如果viewPager自己确认拦截的话,也需要拦截。不能丢弃事件。
return intercepted || super.onInterceptTouchEvent(ev);
}
}
【结果2】
运行后试了试,果然解决了。特此记录。