前言:接着上一篇《Android PullToRefresh 分析之二、UI结构》,这一篇主要分析是如何响应手势事件的,即我们手指滑动的时候促发的一系列响应,该篇将详细讲清楚。
@Override
public final boolean onInterceptTouchEvent(MotionEvent event) {
// 1、如果不是刷新加载模式,让子View接收触摸事件
if (!isPullToRefreshEnabled()) {
return false;
}
final int action = event.getAction();
// 2、如果当前手势被释放或手指抬起,让子View接收触摸事件
if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
mIsBeingDragged = false;
return false;
}
// 3、如果手指按下,并且已经处在滑动刷新加载状态,不让子View接收触摸事件
if (action != MotionEvent.ACTION_DOWN && mIsBeingDragged) {
return true;
}
switch (action) {
case MotionEvent.ACTION_MOVE: {
// If we're refreshing, and the flag is set. Eat all MOVE events
// 4、如果在刷新加载时不允许再次滑动而且正在刷新状态,不让子View接收触摸事件
// 即正在加载数据时不允许用户再次滑动刷新,只能等待该次数据加载完成
if (!mScrollingWhileRefreshingEnabled && isRefreshing()) {
return true;
}
if (isReadyForPull()) {
final float y = event.getY(), x = event.getX();
final float diff, oppositeDiff, absDiff;
// We need to use the correct values, based on scroll
// direction
switch (getPullToRefreshScrollDirection()) {
case HORIZONTAL:
diff = x - mLastMotionX;
oppositeDiff = y - mLastMotionY;
break;
case VERTICAL:
default:
diff = y - mLastMotionY;
oppositeDiff = x - mLastMotionX;
break;
}
absDiff = Math.abs(diff);
// 滑动距离大于滑动事件触发的最小距离,刷新方向上滑动的距离大于非刷新方向的距离
if (absDiff > mTouchSlop && (!mFilterTouchEvents || absDiff > Math.abs(oppositeDiff))) {
// 第一个条目完全可见且为向下滑动,设置当前为 头部刷新
if (mMode.showHeaderLoadingLayout() && diff >= 1f && isReadyForPullStart()) {
mLastMotionY = y;
mLastMotionX = x;
// 5、触发头部刷新,不让子View接收触摸事件
mIsBeingDragged = true;
if (mMode == Mode.BOTH) {
mCurrentMode = Mode.PULL_FROM_START;
}
// 最后一个条目完全可见且为向上滑动,设置当前为 尾部加载
} else if (mMode.showFooterLoadingLayout() && diff <= -1f && isReadyForPullEnd()) {
mLastMotionY = y;
mLastMotionX = x;
// 6、触发尾部加载,不让子View接收触摸事件
mIsBeingDragged = true;
if (mMode == Mode.BOTH) {
mCurrentMode = Mode.PULL_FROM_END;
}
}
}
}
break;
}
case MotionEvent.ACTION_DOWN: {
if (isReadyForPull()) {
mLastMotionY = mInitialMotionY = event.getY();
mLastMotionX = mInitialMotionX = event.getX();
mIsBeingDragged = false;
}
break;
}
}
return mIsBeingDragged;
}
@Override
public final boolean onTouchEvent(MotionEvent event) {
......
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE: {
if (mIsBeingDragged) {
mLastMotionY = event.getY();
mLastMotionX = event.getX();
pullEvent();
return true;
}
break;
}
......
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP: {
if (mIsBeingDragged) {
mIsBeingDragged = false;
if (mState == State.RELEASE_TO_REFRESH
&& (null != mOnRefreshListener || null != mOnRefreshListener2)) {
setState(State.REFRESHING, true);
return true;
}
// If we're already refreshing, just scroll back to the top
if (isRefreshing()) {
smoothScrollTo(0);
return true;
}
// If we haven't returned by here, then we're not in a state
// to pull, so just reset
setState(State.RESET);
return true;
}
break;
}
}
return false;
}
private void pullEvent() {
final int newScrollValue;
final int itemDimension;
final float initialMotionValue, lastMotionValue;
switch (getPullToRefreshScrollDirection()) {
case HORIZONTAL:
initialMotionValue = mInitialMotionX;
lastMotionValue = mLastMotionX;
break;
case VERTICAL:
default:
initialMotionValue = mInitialMotionY;
lastMotionValue = mLastMotionY;
break;
}
switch (mCurrentMode) {
case PULL_FROM_END:
newScrollValue = Math.round(Math.max(initialMotionValue - lastMotionValue, 0) / FRICTION);
itemDimension = getFooterSize();
break;
case PULL_FROM_START:
default:
newScrollValue = Math.round(Math.min(initialMotionValue - lastMotionValue, 0) / FRICTION);
itemDimension = getHeaderSize();
break;
}
setHeaderScroll(newScrollValue);
if (newScrollValue != 0 && !isRefreshing()) {
float scale = Math.abs(newScrollValue) / (float) itemDimension;
......
if (mState != State.PULL_TO_REFRESH && itemDimension >= Math.abs(newScrollValue)) {
setState(State.PULL_TO_REFRESH);
} else if (mState == State.PULL_TO_REFRESH && itemDimension < Math.abs(newScrollValue)) {
setState(State.RELEASE_TO_REFRESH);
}
}
}
protected final void setHeaderScroll(int value) {
......
if (mLayoutVisibilityChangesEnabled) {
if (value < 0) {
mHeaderLayout.setVisibility(View.VISIBLE);
} else if (value > 0) {
mFooterLayout.setVisibility(View.VISIBLE);
} else {
mHeaderLayout.setVisibility(View.INVISIBLE);
mFooterLayout.setVisibility(View.INVISIBLE);
}
}
......
switch (getPullToRefreshScrollDirection()) {
case VERTICAL:
scrollTo(0, value);
break;
case HORIZONTAL:
scrollTo(value, 0);
break;
}
}
if (mState != State.PULL_TO_REFRESH && itemDimension >= Math.abs(newScrollValue)) {
setState(State.PULL_TO_REFRESH);
} else if (mState == State.PULL_TO_REFRESH && itemDimension < Math.abs(newScrollValue)) {
setState(State.RELEASE_TO_REFRESH);
}
if (mState == State.RELEASE_TO_REFRESH
&& (null != mOnRefreshListener || null != mOnRefreshListener2)) {
setState(State.REFRESHING, true);
return true;
}
就是释放的时候回调刷新加载的方法,我们就可以通过设置监听的方式在回调方法中来处理刷新加载的事件啦~
在该篇中,我们也是搞清楚了一个问题,就是如何响应手指滑动事件的。在下篇中《Android PullToRefresh 分析之四、扩展RecyclerView》我们主要讲解如何判断刷新加载View是如何判断是否第一个条目完全显示,最后一个条目完全显示的,然后编写一个自己的扩展 PullToRefreshRecyclerView。