Skip to content

解决android-Ultra-Pull-To-Refresh下拉刷新组件中嵌套ViewPager的一个bug #133

@dengyin2000

Description

@dengyin2000

PtrFrameLayout的dispatchTouchEvent方法实现没有考虑到requestDisallowInterceptTouchEvent,这样导致ViewPager不能通过requestDisallowInterceptTouchEvent解决PrtFrameLayout和ViewPager的事件冲突。

解决办法请看我的博客。http://dengyin2000.iteye.com/blog/2232210

Activity

Lee-swifter

Lee-swifter commented on Aug 10, 2015

@Lee-swifter

我试了下,按照你的博客来,还是没能解决问题。ViewPager可正常滑动,但是此时下拉刷新却很容易出不来。

liaohuqiu

liaohuqiu commented on Aug 10, 2015

@liaohuqiu
Owner

PtrFrameLayout.disableWhenHorizontalMove(true) 这个不能解决问题吗?

Lee-swifter

Lee-swifter commented on Aug 11, 2015

@Lee-swifter

试了下,没什么效果,可能是因为我用的是第三方的banner,不是原生ViewPager的缘故。
我再尝试一下其他的方法吧,谢谢 @liaohuqiu @dengyin2000

Aspsine

Aspsine commented on Sep 12, 2015

@Aspsine

public boolean dispatchTouchEvent(MotionEvent e) {

}

move事件中的

if (mDisableWhenHorizontalMove && !mPreventForHorizontal && (Math.abs(offsetX) > mPagingTouchSlop && Math.abs(offsetX) > Math.abs(offsetY))) {
    if (mPtrIndicator.isInStartPosition()) {
        mPreventForHorizontal = true;
    }
}
if (mPreventForHorizontal) {
    return dispatchTouchEventSupper(e);
}

修改为:

if (mDisableWhenHorizontalMove && (Math.abs(offsetX) > mPagingTouchSlop && Math.abs(offsetX) > Math.abs(offsetY * mPtrIndicator.getResistance()))) {
    return dispatchTouchEventSupper(e);
}

然后将
mPagingTouchSlop = conf.getScaledTouchSlop() * 2,
改为
mPagingTouchSlop = conf.getScaledTouchSlop();

  1. 因为在横向滑动的过程中,可能会触发下拉导致mPtrIndicator.isInStartPosition()这个条件不成立,导致不会调用supper方法。
  2. offsetY在PtrIndicator做了offsetY = offsetY/mResistance 处理。(这个不影响,不过我觉得还是应该改成offsetY * mPtrIndicator.getResistance())
  3. 让横向滑动的mPagingTouchSlop小一点可以让横滑容易一点。

感谢 @liaohuqiu 提供这么伟大的库,我从中学到了很多。

xiaoluome

xiaoluome commented on Oct 28, 2015

@xiaoluome

@Aspsine 你好,你的这种改法也是解决不了问题啊,我用的是GridView里面有Item是横向RecyclerView,还有轮播图ViewPager,都有点问题,横向滑动时,都会往下一点移动,导致不能横向

Aspsine

Aspsine commented on Oct 28, 2015

@Aspsine

@ixiaoluo
好久不见。之前写的,粗心没测试。现在更新了一下,测试没问题。
希望能帮到你。

mmchong3173

mmchong3173 commented on Nov 3, 2015

@mmchong3173

@Aspsine 请问你更新在哪里。

Aspsine

Aspsine commented on Nov 4, 2015

@Aspsine

@mmchong3173
我更新了之前给的解决方案。从这楼开始倒数第5楼。

mmchong3173

mmchong3173 commented on Nov 4, 2015

@mmchong3173

@Aspsine 我看到了,那意味着我只能下载源码修改后再依赖到项目里面咯?我现在是这么使用的compile 'in.srain.cube:ultra-ptr:1.0.11'

Aspsine

Aspsine commented on Nov 4, 2015

@Aspsine

@mmchong3173
是的。也可以以lib项目引入,修改源码,测试好。然后build一次,在build目录里面找到aar包,然后删除依赖项目,直接引用aar。等秋大 fix了以后再更换依赖。总之public 的api不变。

mmchong3173

mmchong3173 commented on Nov 4, 2015

@mmchong3173

@Aspsine 你好,我试了你的方案,发现viewpager在move的过程中只要稍稍有垂直方向的移动便会回到原来的位置,实际效果就是很难翻页。我的xml布局是直接在PtrFrameLayout里面嵌套一个v4包的Viewpager,而且我也把此方案放到了该项目的demo中,效果也是一样的,不知道是不是我使用的方式不对,望能指出,谢谢。

21 remaining items

xanaduo

xanaduo commented on Aug 25, 2016

@xanaduo

能不能把改的放到这个库里面,这样你们问题是解决了,但是库还是有问题吧

pzhangleo

pzhangleo commented on Aug 25, 2016

@pzhangleo

现在库的所有人没有在维护了吧,好多pr都没有处理,我建议咱们fork一个,大家一起维护吧

frankfancode

frankfancode commented on Dec 7, 2016

@frankfancode

场景:
PtrFrameLayout 中有竖向 Recyclerview,竖向Recyclerview 中有 ViewPager 写得横向轮播图 和 横向 Recyclerview。

问题:
横向滑动轮播图时,卡顿断续。

解决方法:
修改 PtrFrameLayout 中代码,

mPagingTouchSlop = conf.getScaledTouchSlop() * 2;

改成

 mPagingTouchSlop = conf.getScaledTouchSlop();

使 横滑 灵敏。

if (mDisableWhenHorizontalMove && !mPreventForHorizontal && (Math.abs(offsetX) > mPagingTouchSlop && Math.abs(offsetX) > Math.abs(offsetY))) {
    if (mPtrIndicator.isInStartPosition()) {
        mPreventForHorizontal = true;
    }
}
if (mPreventForHorizontal) {
    return dispatchTouchEventSupper(e);
}

改成

if (mDisableWhenHorizontalMove && (Math.abs(offsetX) > mHorizontalMoveSlop && Math.abs(offsetX) > Math.abs(offsetY))) {
    mPreventForHorizontal = true;
}

if (mPreventForHorizontal) {
    dispatchTouchEventSupper(e);
    return true;
  /*一旦横向滑动,就禁止竖向滑动,免得滑了一部分PtrFrameLayout将事件夺走 又开始竖向滑动了。因为大拇指向左滑动时的弧线垂直导数会越来越大  offsetY 可能会 大过 offsetX */

}

新增全局变量
private int mHorizontalMoveSlop=20;//设置水平滑动的灵敏性

之所以不用 TouchSlop 是因为 不同的手机上的 TouchSlop 大小不一样,改之后效果不一样,一加上的是 24,moto 上的 是 72 ,72对于横滑来说太大了,我都统一改小了。不同的手机上效果就一样了

感谢秋大 和 Aspsine 。

tangjie1989

tangjie1989 commented on Jan 4, 2017

@tangjie1989

mPagingTouchSlop = conf.getScaledTouchSlop() * 2; 修改为 mPagingTouchSlop = conf.getScaledTouchSlop() recyclerview+viewpager 外套下拉刷新 改完后viewpager滑动好很多。

hui453694187

hui453694187 commented on Jan 11, 2017

@hui453694187

请问在WebView 的头部 有一个 banner 的时候导致, banner 左右滑动很容易触发下拉刷新, 这种 如何处理, 如何自己处理事件的分发, 自定义手指下滑距离和左右滑动距离来判断是下啦,还是滑动banner ?

imliujun

imliujun commented on Feb 15, 2017

@imliujun

这个问题都这么久了,我感觉还是我的解决方案效果最好。 @yanzhenjie 你的这个方案是解决了左右滑的问题,但是下拉刷新变得非常不灵敏

#181 是我提交的PR

shenminjie

shenminjie commented on Mar 7, 2017

@shenminjie

感谢 @yanzhenjie 的解决方案,但你这个存在着下拉刷新不太灵敏,用户体验也不太好。我这里有个解决方案,在你这个基础上,添加HorizontalMoveView获取水平滑动的手势监听,当手势在HorizontalMoveView才进行拦截,这样就减少了其他区域的下拉事件的冲突,提高下拉刷新的灵敏。

public interface HorizontalMoveView {
   /**
     * 获取当前事件
     * MotionEvent.ACTION_DOWN
     * MotionEvent.ACTION_MOVE
     * 用于阻断手势
     */
    int getCurrentTouchEvent();
}

PtrFrameLayout.java:

  /**
     * 声明全局变量
     * 水平滑动的视图
     */
    private HorizontalMoveView mHorizontalMoveView;

    /**
     * 设置冲突手势的view,当手势在此view中,才阻断其事件
     * 提高用户体验
     *
     * @param horizontalMoveView horizontalMoveView 水平滑动的view
     */
    public void setHorizontalMoveView(HorizontalMoveView horizontalMoveView) {
        mHorizontalMoveView = horizontalMoveView;
    }

line 317:

if (mDisableWhenHorizontalMove && !mPreventForHorizontal && Math.abs(offsetX) > Math.abs(offsetY)) {
                    //判断手势是否在水平滚动的view中,如果没有正常下拉刷新,如果水平上,直接阻挡
                    boolean isTouch = mHorizontalMoveView != null &&
                            (mHorizontalMoveView.getCurrentTouchEvent() == MotionEvent.ACTION_DOWN || mHorizontalMoveView.getCurrentTouchEvent() == MotionEvent.ACTION_MOVE);
                    if (mPtrIndicator.isInStartPosition() && isTouch) {
                        mPreventForHorizontal = true;
                    }
                }

各位有需要可以查看我的fork

dingbuoyi

dingbuoyi commented on Mar 9, 2017

@dingbuoyi

@lzyzsd 这个PR什么时候可以合并进去。。。这个issue还是挺严重的

dingbuoyi

dingbuoyi commented on Mar 9, 2017

@dingbuoyi

@Lee-swifter 你的方法不推荐了,就算能解决VIEW PAGER的问题,也不适用于横向 recycle view。

dingbuoyi

dingbuoyi commented on Mar 9, 2017

@dingbuoyi

@pip1998
@imliujun
你们的代码都试过,我测试场景是scrollview里面嵌套了横向recycleview,都没有效果

msdx

msdx commented on Apr 20, 2017

@msdx

我也遇到了这个问题,不过我的问题不复杂,退回1.0.9之后可以解决。但是我看了一下PtrFrameLayout的实现,有些粗浅看法:
在Android的事件模型中,一个事件会经过分发、拦截、消费这三个过程。顶层往下分发,也可以拦截事件不让其往下分发,如果下层没有消费事件,上层才消费。
由于上层可以中断事件向下层的传递,而存在着这样的场景:上层可能需要拦截事件向下层的传递,而下层可能又需要事件。所以会有requestDisallowInterceptTouchEvent的情景。
而在PtrFrameLayout中,对事件的处理选择了在分发的这个过程中处理,从出发点上它就已经忽略(忽视)了下层不想让上层拦截事件的情况。
事件是否不允许被拦截使用mGroupFlags标志位判断,不过很遗憾该变量是@hide

pzhangleo

pzhangleo commented on Apr 21, 2017

@pzhangleo

考虑重启一个项目,来完善这个库吧,作者已经没时间维护了吧

yunmenggyy

yunmenggyy commented on Nov 2, 2017

@yunmenggyy

大家好,我也遇到了这个问题,我的页面顶级是PtrFrameLayout,然后里面包含了一个scrollView,scrollView中包含一个圆形的自定义sickBar。我在滑动SickBar的时候,事件会被外层消费掉。我也直接使用方法requestDisallowInterceptTouchEvent但是没效果 最终的解决方案是在自定义的sickBar中添加一个触摸监听,滑动的时候调用PtrFrameLayoutsetEnabled(false),然后滑动结束以后调用setEnabled(true)`

nailperry-zd

nailperry-zd commented on Dec 27, 2017

@nailperry-zd

@yanzhenjie 使用你的方案后,下拉刷新变得很不灵敏了

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @dingbuoyi@captain-miao@msdx@Aspsine@frankfancode

        Issue actions

          解决android-Ultra-Pull-To-Refresh下拉刷新组件中嵌套ViewPager的一个bug · Issue #133 · liaohuqiu/android-Ultra-Pull-To-Refresh