Ultra-Pull-To-Refresh的一些问题
阿里廖祜秋的android-Ultra-Pull-To-Refresh是个非常好的库. 虽然有些许bug,但是掩盖不了它的精彩.
https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh
解决android-Ultra-Pull-To-Refresh下拉刷新组件中嵌套ViewPager的一个bug issues#133
UPTR和ViewPager一起使用是, 滑动事件会有一些冲突.这个issues里面提供了几个方法.
方法一:
http://dengyin2000.iteye.com/blog/2232210
评价: 按照你的博客来,还是没能解决问题。ViewPager可正常滑动,但是此时下拉刷新却很容易出不来。方法二:
将
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();
因为在横向滑动的过程中,可能会触发下拉导致mPtrIndicator.isInStartPosition()这个条件不成立,导致不会调用supper方法。
offsetY在PtrIndicator做了offsetY = offsetY/mResistance 处理。(这个不影响,不过我觉得还是应该改成offsetY * mPtrIndicator.getResistance())
让横向滑动的mPagingTouchSlop小一点可以让横滑容易一点。
感谢 @liaohuqiu 提供这么伟大的库,我从中学到了很多。
评价: 我试了你的方案,发现viewpager在move的过程中只要稍稍有垂直方向的移动便会回到原来的位置,实际效果就是很难翻页。
- 方法三: (这个是解释原因的)
if (mDisableWhenHorizontalMove && !mPreventForHorizontal && (Math.abs(offsetX) > mPagingTouchSlop && Math.abs(offsetX) > Math.abs(offsetY))) {
if (mPtrIndicator.isInStartPosition()) {
mPreventForHorizontal = true;
}
}
在以上代码中,作者的offsetX是touchevent与上一次 touchevent的差。错误在于作者将mPagingTouchSlop与offsetX做比较。因为offsetX实质是ΔX,不是距离。正确的比较方案应该是mPagingTouchSlop与(当前touchevent-按下时touchevent)做比较,进而给mPreventForHorizontal赋值
- 方法四:
我找到了一个解决这个问题的好方法,代码如下:
mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
@Override
public void onPageScrollStateChanged(int state) {
mPtrFrame.setEnabled(state == ViewPager.SCROLL_STATE_IDLE);
}
});
我使用这段代码,效果挺好的。只要是滑动ViewPager时便不会将下拉刷新滑动出来了。这种方法同样也适用于官方的SwipeRefreshLayout和ViewPager配合。
我测试了这个项目和官方的下拉刷新控件,都能完美解决问题。我相信这段代码同样适用于你们的问题。
评价: 我使用的是该方案. 效果不错. 感谢.
- 方法五:
目前我通过修改PtrFramLayout源码解决了这个问题,目前的几种使用场景如下:
1. ViewPager放在PtrFramLayout的顶部。
2. PtrFramLayout中嵌套ScrollView,ScrollView中放一个ViewGroup,ViewGroup的顶部放ViewPager。
3. ViewPager中嵌套PtrFramLayout,好吧其实前三种就是PtrFramLayout和ViewPager相互嵌套啦。
4. ViewPager放在Layout的非顶部。
其中第三种滑动没有问题,没有冲突,主要就是第一种和第二种。需要修改的代码仅仅一行:
if (mDisableWhenHorizontalMove && !mPreventForHorizontal && (Math.abs(offsetX) > mPagingTouchSlop && Math.abs(offsetX) > Math.abs(offsetY))) {
if (mPtrIndicator.isInStartPosition()) {
mPreventForHorizontal = true;
}
}
把上述代码的if判断的Math.abs(offsetX) > mPagingTouchSlop这一句去掉就可以了,完整代码如下:
if (mDisableWhenHorizontalMove && !mPreventForHorizontal && Math.abs(offsetX) > Math.abs(offsetY)) {
if (mPtrIndicator.isInStartPosition()) {
mPreventForHorizontal = true;
}
}
原因是,我们既然要禁用横向滑动的拦截,那么判断操作为横向并且要禁用横向拦截时给mPreventForHorizontal赋值为true即可,并不需要判断滑动距离。
同时PtrFramLayout的第113、114行代码就无用了,可以注释了,第54行mPagingTouchSlop成员变量也无用,可以注释了。
使用的同学请注意还是需要调用PtrFrameLayout.disableWhenHorizontalMove(true)来灵活控制是否需要拦截。
评价: 达哥的解决方案. 横向滑动的问题解决了. 但是下拉刷新必须要非常垂直才能拉出.
博文在此: http://blog.csdn.net/yanzhenjie1003/article/details/51319181
android-Ultra-Pull-To-Refresh嵌套上下滑动View时滑动冲突 [这里的解决方案来自达哥的博文]
http://blog.csdn.net/yanzhenjie1003/article/details/51319181
其实PtrFramLayout的刷新接口PtrHandler提供了一个方法checkCanDoRefresh(…)来检查是否允许刷新,我们只需要在这里做判断返回true和false就OK了。
然而我们的作者liaohuqiu同学也是想的非常周到,提供了一个PtrDefaultHandler类,实现了和SwipeRefreshLayout同样的判断,so看到这里的同学肯定知道怎么改了吧,我们先来看看PtrDefaultHandler源码:
public abstract class PtrDefaultHandler implements PtrHandler {
public static boolean canChildScrollUp(View view) {
if (android.os.Build.VERSION.SDK_INT < 14) {
if (view instanceof AbsListView) {
final AbsListView absListView = (AbsListView) view;
return absListView.getChildCount() > 0
&& (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0)
.getTop() < absListView.getPaddingTop());
} else {
return view.getScrollY() > 0;
}
} else {
return view.canScrollVertically(-1);
}
}
/**
* Default implement for check can perform pull to refresh
*
* @param frame
* @param content
* @param header
* @return
*/
public static boolean checkContentCanBePulledDown(PtrFrameLayout frame, View content, View header) {
return !canChildScrollUp(content);
}
@Override
public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header) {
return checkContentCanBePulledDown(frame, content, header);
}
}
所以我们在使用刷新接口的时候使用PtrDefaultHandler就好了,重写checkCanDoRefresh()方法:
private ScrollView scrollView;
...
private PtrDefaultHandler defaultHandler = new PtrDefaultHandler() {
@Override
public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header) {
return !canChildScrollUp(scrollView);
}
@Override
public void onRefreshBegin(PtrFrameLayout frame) {
// 做刷新的操作
...
}
};