SwipeRefreshLayout布局滑动冲突检测分析
问题现象:
使用SwipeRefreshLayout布局实现下拉刷功能,布局方式是SwipeRefreshLayout嵌套FrameLayout,然后FrameLayout中嵌套ListView。进行下拉刷新往下滑动ListView时,但是ListView没有滑到顶,往下滑动时,但是显示刷新进度条。
分析:
出现这个现象怀疑是出现滑动冲突之类的问题,所以查看SwipeRefreshLayout的onInterceptTouchEvent查是什么原因引起了这个现象
onInterceptTouchEvent()函数如下:
public boolean onInterceptTouchEvent(MotionEvent ev) {
ensureTarget();
final int action = MotionEventCompat.getActionMasked(ev);
if (mReturningToStart && action == MotionEvent.ACTION_DOWN) {
mReturningToStart = false;
}
if (!isEnabled() || mReturningToStart || canChildScrollUp()
|| mRefreshing || mNestedScrollInProgress) {
// Fail fast if we're not in a state where a swipe is possible
return false;
}
switch (action) {
case MotionEvent.ACTION_DOWN:
setTargetOffsetTopAndBottom(mOriginalOffsetTop - mCircleView.getTop(), true);
mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
mIsBeingDragged = false;
final float initialDownY = getMotionEventY(ev, mActivePointerId);
if (initialDownY == -1) {
return false;
.........................................
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mIsBeingDragged = false;
mActivePointerId = INVALID_POINTER;
break;
}
return mIsBeingDragged;
}
其中的关键函数为canChildScrollUp这个函数用来检测
SwipeRefreshLayout中嵌套的布局mTarget是否可以滑动。这里mTarget是FrameLayout布局。
然后查看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);
}
}
发现嵌套的 mTarget为AbsListView,ScrollView 或者scrollY大于0下面才会返回false
if (!isEnabled() || mReturningToStart || canChildScrollUp()
|| mRefreshing || mNestedScrollInProgress) {
// Fail fast if we're not in a state where a swipe is possible
return false;
}
不拦截此次事件,由于mTarget为FrameLayout因此
SwipeRefreshLayout拦截了此次下滑事件,所以便会出现ProgressBar。
结论:
由于mTarget为FrameLayout被SwipeRefreshLayout中的canChildScrollUp检测为不可滑动,所以ListView没有滑到顶却被SwipeRefreshLayout拦截下滑事件。由此提供了一种解决SwipeRefreshLayout滑动冲突的思路即重写canChildScrollUp函数