欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android中ViewPager带来的滑动卡顿问题解决要点解析

程序员文章站 2024-03-04 22:01:48
问题说明: 当swiperefreshlayout中放置了viewpager控件,两者的滑动会相互冲突.具体表现为viewpager的左右滑动不顺畅,容易被swipere...

问题说明:
当swiperefreshlayout中放置了viewpager控件,两者的滑动会相互冲突.具体表现为viewpager的左右滑动不顺畅,容易被swiperefreshlayout拦截(即出现刷新的view).

问题原因:
viewpager本身是处理了滚动事件的冲突,它在横向滑动时会调用requestdisallowintercepttouchevent()方法使父控件不拦截当前的touch事件序列.但是swiperefreshlayout的requestdisallowintercepttouchevent()方法什么也没有做,所以仍然会拦截当前的touch事件序列.

问题分析:
为什么swiperefreshlayout的requestdisallowintercepttouchevent()方法什么都不做?

首先swiperefreshlayout继承自viewgroup.

在requestdisallowintercepttouchevent()方法什么都不做的情况下,用户可以从底部下拉刷新一次拉出loadingview.
如果方法调用viewgroup的requestdisallowintercepttouchevent()方法, 可以解决viewpager的兼容问题,但是用户在界面底部下拉至头部后,无法继续下拉,需要手指放开一次才能拉出loadingview.
目标分析:
那么为了更加顺滑地滚动,想要的效果当然是一次性拉出loadingview.既然viewpager在左右滑动时才会调用requestdisallowintercepttouchevent()方法,那么swiperefreshlayout只应该在上下滑动时才拦截touch事件.

具体逻辑如下:

记录是否调用了requestdisallowintercepttouchevent()方法,并且设置为true.
在swiperefreshlayout中判断是否是上下滑动.
如果同时满足1,2,则调用super.requestdisallowintercepttouchevent(true).
否则调用super.requestdisallowintercepttouchevent(false).
注意:因为viewgroup的requestdisallowintercepttouchevent方法设置true后,touch事件在dispatchtouchevent()方法中就会被拦截,所以需要在dispatchtouchevent()方法中判断是否为上下滑动.

实现代码(部分):

//非法按键
private static final int invalid_pointer = -1;

//dispatch方法记录第一次按下的x
private float minitialdispatchdownx;

//dispatch方法记录第一次按下的y
private float minitialdispatchdowny;

//dispatch方法记录的手指
private int mactivedispatchpointerid = invalid_pointer;

//是否请求拦截
private boolean hasrequestdisallowintercept = false;

@override
public void requestdisallowintercepttouchevent(boolean b) {
  hasrequestdisallowintercept = b;
  // nope.
}

@override
public boolean dispatchtouchevent(motionevent ev) {
  switch (ev.getaction()) {
    case motionevent.action_down:
      mactivedispatchpointerid = motioneventcompat.getpointerid(ev, 0);
      final float initialdownx = getmotioneventx(ev, mactivedispatchpointerid);
      if (initialdownx != invalid_pointer) {
        minitialdispatchdownx = initialdownx;
      }
      final float initialdowny = getmotioneventy(ev, mactivedispatchpointerid);
      if (minitialdispatchdowny != invalid_pointer) {
        minitialdispatchdowny = initialdowny;
      }
      break;
    case motionevent.action_move:
      if (hasrequestdisallowintercept) {
        //解决viewpager滑动冲突问题
        final float x = getmotioneventx(ev, mactivedispatchpointerid);
        final float y = getmotioneventy(ev, mactivedispatchpointerid);
        if (minitialdispatchdownx != invalid_pointer && x != invalid_pointer &&
            minitialdispatchdowny != invalid_pointer && y != invalid_pointer) {
          final float xdiff = math.abs(x - minitialdispatchdownx);
          final float ydiff = math.abs(y - minitialdispatchdowny);
          if (xdiff > mtouchslop && xdiff * 0.7f > ydiff) {
            //横向滚动不需要拦截
            super.requestdisallowintercepttouchevent(true);
          } else {
            super.requestdisallowintercepttouchevent(false);
          }
        } else {
          super.requestdisallowintercepttouchevent(false);
        }
      }
      break;
    case motionevent.action_up:
    case motionevent.action_cancel:
      if (ev.getaction() == motionevent.action_up || ev.getaction() == motionevent.action_cancel) {
        hasrequestdisallowintercept = false;
      }
      break;
  }

  return super.dispatchtouchevent(ev);
}

private float getmotioneventy(motionevent ev, int activepointerid) {
  final int index = motioneventcompat.findpointerindex(ev, activepointerid);
  if (index < 0) {
    return -1;
  }
  return motioneventcompat.gety(ev, index);
}

private float getmotioneventx(motionevent ev, int activepointerid) {
  final int index = motioneventcompat.findpointerindex(ev, activepointerid);
  if (index < 0) {
    return -1;
  }
  return motioneventcompat.getx(ev, index);
}