android-Ultra-Pull-To-Refresh/SwipeRefreshLayout嵌套ViewPager/ScrollView滑动冲突解决
2016-05-04 23:36
246 查看
android-Ultra-Pull-To-Refresh/SwipeRefreshLayout嵌套ViewPager滑动冲突解决
欢迎大家关注Android开源网络框架NoHttp:https://github.com/yanzhenjie/NoHttp在线直播视频和代码下载:http://pan.baidu.com/s/1miEOtwG
版权声明:转载请注明本文转自Yolanda的CSDN博客: http://blog.csdn.net/yanzhenjie1003
QQ交流群1:46523908
QQ交流群2:46505645
群资源非常宝贵,请不要重复加群,谢谢。
前戏
每次必不可少的前戏又来了。发文时Android-PullToRefresh这个框架已经停止维护3年了,很多人在关心我们现在用什么框架好,这里给大家推荐两个。一个是可爱可亲起可恨的Google官方v4包自带的
SwipeRefreshLayout,一个是liaohuqiu同学在Github上发表的
android-Ultra-Pull-To-Refresh,我强烈推荐使用后者,后者扩展性好,而且是国人自己开发的框架,交互设计也符合Google官方的操作规范。
android-Ultra-Pull-To-Refresh托管地址:https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh/
英文版官网:http://android-ultra-ptr.liaohuqiu.net/
中文版官网:http://android-ultra-ptr.liaohuqiu.net/cn
力荐
android-Ultra-Pull-To-Refresh的原因是它特别易于扩展,而且api丰富简单,作者也是咱中国人,但是根本原因还是这个框架确实特别好用。而且
SwipeRefreshLayout有的功能和动画它都有。
高潮
现在引出我们的问题,我们好多人都使用SwipeRefreshLayout,如果不需要修改Header和动画的话,应该可以满足大多数需求了。上面也说了
SwipeRefreshLayout有的动画和功能
android-Ultra-Pull-To-Refresh都有,但是我们现在的情况是,不论使用这两者其中的哪个框架和
ViewPager互相嵌套的时候(比如作为Banner),二者与
ViewPager都会发生滑动冲突。
SwipeRefreshLayout与ViewPager嵌套滑动冲突
先来解决个人认为次要且相对简单的SwipeRefreshLayout的冲突问题,只需要在
ViewPager滑动的时候禁用了
SwipeRefreshLayout就可以了,代码如下:
ViewPager mViewPager; SwipeRefreshLayout mRefreshLayout; ... mViewPager.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { int action = event.getAction(); switch (action) { case MotionEvent.ACTION_DOWN:// 经测试,ViewPager的DOWN事件不会被分发下来 case MotionEvent.ACTION_MOVE: mRefreshLayout.setEnabled(false); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: mRefreshLayout.setEnabled(true); break; } return false; } });
做个简单的解释,当我们手指在ViewPager上按下或者移动的时候让SwipeRefreshLayout不可用,当我们手指抬起或者滑动到ViewPager之外的时候让SwipeRefreshLayout可用。
此方法同样适用于android-Ultra-Pull-To-Refresh(达哥做了测试后发现不是很准),但是是不是有点麻烦了,每次都需要这么处理一下,或者要继承系统的View做个小的封装。因此推荐看官使用android-Ultra-Pull-To-Refresh,我们接下来解决android-Ultra-Pull-To-Refresh与ViewPager嵌套时的滑动冲突。
android-Ultra-Pull-To-Refresh与ViewPager嵌套滑动冲突
当我们不亦乐乎的在项目中使用android-Ultra-Pull-To-Refresh时,当我们用android-Ultra-Pull-To-Refresh和ViewPager互相嵌套的时候猛然发现,二者居然有滑动冲突,我们在框架主页的issues中也能看到这个问题,但是一直没有被解决,心中上万只草泥马奔腾而过,这时候达哥我就要出来来拯救世界了,看我们如何来解决这个问题。
我们在android-Ultra-Pull-To-Refresh的源码托管ReadMe的末尾发现work with ViewPager: disableWhenHorizontalMove(),翻译过来就是和ViewPager一起使用,但是我们调用了这个方法后发现然并卵,那么问题在哪里呢?
达哥阅读了android-Ultra-Pull-To-Refresh的PtrFramLayout源码后发现修改了这个bug,这里直接提供修改方法,我们打开PtrFramLayout.java这个类,找到308行代码:
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) > mPagingTouchSlop && Math.abs(offsetX) > Math.abs(offsetY))) { if (mPtrIndicator.isInStartPosition()) { mPreventForHorizontal = true; }
原因是,我们既然要禁用横向滑动的拦截,那么判断操作为横向并且要禁用横向拦截时给
mPreventForHorizontal赋值为
true即可,并不需要判断滑动距离。
同时
PtrFramLayout的第113、114行代码就无用了,可以注释了,第54行
mPagingTouchSlop成员变量也无用,可以注释了。
使用的同学请注意还是需要调用
PtrFrameLayout.disableWhenHorizontalMove(true)来灵活控制是否需要拦截。
收尾福利
问题描述:SwipeRefreshLayout和
android-Ultra-Pull-To-Refresh嵌套
ScrollView、
ListView、
GrdiView、
RecyclerView时,页面往上滑再往下滑时还没滑到顶部就触发了下拉刷新。
SwipeRefreshLayout嵌套上下滑动View时滑动冲突
解决SwipeRefreshLayout冲突方案,我们来看看
SwipeRefreshLayout源码发现一个方法
canChildScrollUp(),意思是子View可以向上滚动吗,当返回true的时候
SwipeRefreshLayout就不能被下拉刷新了,细细分析了代码之后发现这里只对子View做了判断,而我们实际开发中往往会在
SwipeRefreshLayout中再嵌套一个ViewGroup,再在这个ViewGroup中放一个ScrollView,这时候我们说的问题就出现了。
canChildScrollUp()方法源码
public boolean canChildScrollUp() { if (android.os.Build.VERSION.SDK_INT < 14) { if (mTarget instanceof AbsListView) { final AbsListView absListView = (AbsListView) mTarget; return absListView.getChildCount() > 0 && (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0).getTop() < absListView.getPaddingTop()); } else { return ViewCompat.canScrollVertically(mTarget, -1) || mTarget.getScrollY() > 0; } } else { return ViewCompat.canScrollVertically(mTarget, -1); } }
所以我们需要重写
SwipeRefreshLayout的
canChildScrollUp()方法,完整代码如下:
public class MySwipeRefreshLayout extends android.support.v4.widget.SwipeRefreshLayout { private boolean canChildScrollUp; public MySwipeRefreshLayout(Context context) { super(context); } public MySwipeRefreshLayout(Context context, AttributeSet attrs) { super(context, attrs); } public void setCanChildScrollUp(boolean canChildScrollUp) { this.canChildScrollUp = canChildScrollUp; } @Override public boolean canChildScrollUp() { return canChildScrollUp; } }
当我们监听到
SwipeRefreshLayout中可以上下滑动的View向上滚动了就调用
swipeRefreshLayout.setChildScrollUp(true),当我们监听到
SwipeRefreshLayout中可以上下滑动的View向下滚动并且已经到顶部了,就调用
swipeRefreshLayout.setChildScrollUp(false)。
但是我们每次嵌套都需要对的子View的滑动做判断,所以我们动态的指定
SwipeRefreshLayout中的contentView,并且使用
SwipeRefreshLayout的
canChildScrollU()方法中的代码来判断,简直完美啊:
public class MySwipeRefreshLayout extends android.support.v4.widget.SwipeRefreshLayout { private View contentView; public MySwipeRefreshLayout(Context context) { super(context); } public MySwipeRefreshLayout(Context context, AttributeSet attrs) { super(context, attrs); } public void setContentView(View contentView) { this.contentView = contentView; } @Override public boolean canChildScrollUp() { if (android.os.Build.VERSION.SDK_INT < 14) { if (contentView instanceof AbsListView) { final AbsListView absListView = (AbsListView) contentView; return absListView.getChildCount() > 0 && (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0).getTop() < absListView.getPaddingTop()); } else { return ViewCompat.canScrollVertically(contentView, -1) || contentView.getScrollY() > 0; } } else { return ViewCompat.canScrollVertically(contentView, -1); } } }
和android-Ultra-Pull-To-Refresh嵌套上下滑动View时滑动冲突
其实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) { // 做刷新的操作 ... } };
完美啊,有疑问的同学下面留言,收官睡觉。
欢迎大家关注我的Android开源网络框架NoHttp,源码托管地址:https://github.com/yanzhenjie/NoHttp
相关文章推荐
- Android编程权威指南学习笔记01
- Android做客户端,Web做服务端(Xml数据格式)
- MediaController是如何依附在VideoView上的
- Android进程
- Android里面的大BOSS---Context
- Android使用Google自带的截屏方法
- Android解惑 - 为什么要用Fragment.setArguments(Bundle bundle)来传递参数
- Android中使用Picasso将图片直接转换为圆形
- 谷歌不再销售Nexus 9 Android平板
- Cordova android框架详解
- Android属性动画(二) ------ 站在巨人的肩膀上学习总结
- android对话框介绍
- ImageView的属性android:scaleType作用
- Android定时器之Handler的postDelyed方法
- android坐标
- 安卓真机模拟 com.android.ddmlib.AdbCommandRejectedException: device offline
- Android Studio教程从入门到精通
- 帧动画和补间动画
- Android Studio开发环境搭建
- android-使用AsyncTask做下载进度条