PullToRefreshListView原理解析(二)

本文解析了PullToRefreshListView的工作原理,详细介绍了其通过拦截触摸事件来判断何时触发刷新和加载更多功能,并展示了关键代码片段。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值