PullToRefreshListView的父类是PullToRefreshAdapterViewBase<T extends AbsListView>,PullToRefreshAdapterViewBase的父类是
PullToRefreshBase<T extends View>。
PullToRefreshBase是公共父类,它的结构如下。从上往下,分别为headView,泛型T,footView

headView和footView不用说了,上拉或者下拉显示出来的UI。至于显示成什么样子,那事他们自己的事,重点是PullToRefreshBase要告诉他们什么时候是
上拉了什么时候是下拉了。PullToRefreshListView的重点也都在这个类里面。至于不需要显示headView和footView时,怎么操作那是泛型T的事。泛型T就是
需要对什么控件添加上拉和下拉效果,可能是WebView,也可能是ListView,这里提高了扩展性。最终PullToRefreshListView传入的泛型T就是ListView。所以
PullToRefreshBase的重点就是什么时候拦截事件来通知headView和footView执行上拉下拉效果。
1.拦截事件onInterceptTouchEvent
public final boolean onInterceptTouchEvent(MotionEvent event) {
if (!isPullToRefreshEnabled()) {
return false;
}
final int action = event.getAction();
// 事件事
if (mMode == Mode.REFROTATE && action == MotionEvent.ACTION_UP) {
}
if (action == MotionEvent.ACTION_CANCEL
|| action == MotionEvent.ACTION_UP) {
mIsBeingDragged = false;
return false;
}
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
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;
mIsBeingDragged = true;
if (mMode == Mode.BOTH || mMode == Mode.FLIP_ROTATE) {
mCurrentMode = Mode.PULL_FROM_START;
}
} else if (mMode.showFooterLoadingLayout() && diff <= -1f
&& isReadyForPullEnd()) {
mLastMotionY = y;
mLastMotionX = x;
mIsBeingDragged = true;
if (mMode == Mode.BOTH || mMode == Mode.FLIP_ROTATE
|| mMode == Mode.REFROTATE) {
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;
}
1.首先ACTION_DOWN肯定要返回false,因为按下时还不知道是否要显示headView或者footView,要是返回true,滑动事件就传递不到ListView了。
2.action != MotionEvent.ACTION_DOWN && mIsBeingDragged
ACTION_MOVE时,是否返回true,主要看mIsBeingDragged是否为true,那它什么时候为true呢。
2.1 isRefreshing()的时候
public final boolean isRefreshing() {
return mState == State.REFRESHING || mState == State.MANUAL_REFRESHING;
}
2.2
if (absDiff > mTouchSlop
&& (!mFilterTouchEvents || absDiff > Math
.abs(oppositeDiff))) {
if (mMode.showHeaderLoadingLayout() && diff >= 1f
&& isReadyForPullStart()) {
mLastMotionY = y;
mLastMotionX = x;
mIsBeingDragged = true;
if (mMode == Mode.BOTH || mMode == Mode.FLIP_ROTATE) {
mCurrentMode = Mode.PULL_FROM_START;
}
} else if (mMode.showFooterLoadingLayout() && diff <= -1f
&& isReadyForPullEnd()) {
mLastMotionY = y;
mLastMotionX = x;
mIsBeingDragged = true;
if (mMode == Mode.BOTH || mMode == Mode.FLIP_ROTATE
|| mMode == Mode.REFROTATE) {
mCurrentMode = Mode.PULL_FROM_END;
}
}
}
只要有上下滑动就能进入第一层if,
absDiff > Math.abs(oppositeDiff)
就是判断滑动时垂直移动距离大于水平移动距离,即上下滑动。
里面一层if主要就是判断,
mMode.showHeaderLoadingLayout()
判断mode,真正使用时自己会设置的,只要设置没问题就是true.
diff >= 1f
就不说了
isReadyForPullStart()
是抽象方法,看子类。子类主要在isFirstItemVisible方法
private boolean isFirstItemVisible() {
final Adapter adapter = mRefreshableView.getAdapter();
if (null == adapter || adapter.isEmpty()) {
if (DEBUG) {
Log.d(LOG_TAG, "isFirstItemVisible. Empty View.");
}
return true;
} else {
/**
* This check should really just be:
* mRefreshableView.getFirstVisiblePosition() == 0, but PtRListView
* internally use a HeaderView which messes the positions up. For
* now we'll just add one to account for it and rely on the inner
* condition which checks getTop().
*/
if (mRefreshableView.getFirstVisiblePosition() <= 1) {
final View firstVisibleChild = mRefreshableView.getChildAt(0);
if (firstVisibleChild != null) {
return firstVisibleChild.getTop() >= mRefreshableView
.getTop();
}
}
}
return false;
}
可以看到,泛型是ListView时,就是判断是不是在最顶部。另一个if代码块意思也差不多
2.执行事件onTouchEvent
不用想也知道,当onInterceptTouchEvent返回true时(mIsBeingDragged为true),onTouchEvent肯定要做处理了.比如ACTION_MOVE的时候
case MotionEvent.ACTION_MOVE: {
if (mIsBeingDragged) {
mLastMotionY = event.getY();
mLastMotionX = event.getX();
pullEvent();
return true;
}
break;
}
执行pullEvent,主要就是一系列计算,让headView或者footView滑动。这里就不说了
over