股票场内基金交易,没时间盯盘?
前言
上篇文章介绍了 PullToRefresh 的继承关系。本片开始将介绍 PullToRefresh 的源码。由于时间的关系,关于PullToRefresh 的源码笔者也将分成两篇来介绍。本篇文章主要介绍 PullToRefresh 的下拉刷新是如何实现的。在下一篇中,将介绍如何扩展 PullToRefresh,并编写 demo。
PullToRefreshBase
从上篇文章中我们知道 IPullToRefresh 是 PullToRefresh 的基类,PullToRefreshBase 实现了 IPullToRefresh。所以我猜测 PullToRefresh 的下拉刷新是在 PullToRefreshBase 中实现的。要实现下拉刷新的效果就牵扯到 View 的事件传递机制。所以我们先从 onInterceptTouchEvent 、onTouchEvent 和 dispatchTouchEvent 方法分析。但是在 PullToRefreshBase 中只有 onInterceptTouchEvent 和 onTouchEvent 。我们先看 onInterceptTouchEvent。
onInterceptTouchEvent
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
@Override public final boolean onInterceptTouchEvent(MotionEvent event) { if (!isPullToRefreshEnabled()) { return false; } final int action = event.getAction(); 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 (!isCanMove){ // break; //} 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) { mCurrentMode = Mode.PULL_FROM_START; } } else if (mMode.showFooterLoadingLayout() && diff <= -1f && isReadyForPullEnd()) { mLastMotionY = y; mLastMotionX = x; 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; } |
在 onInterceptTouchEvent 方法中,主要是判断是否满足下拉或者上拉的条件。其中 isReadyForPullStart() 方法和 isReadyForPullEnd() 方法都是在具体或者某一类的刷新控件中实现的,他们返回 true 的条件就是滑动到顶端或者低端。比如 PullToRefreshAdapterViewBase 中,PullToRefreshAdapterViewBase 是 AbsListView 的子类现实下拉刷新时的父类。如果满足下拉或者上拉的条件时 mIsBeingDragged 为 true。即触摸事件被 PullToRefreshBase 拦截。此时触摸事件将有 PullToRefreshBase 的 onTouchEvent 来处理。接下来请看 onTouchEvent。
onTouchEvent
在 onTouchEvent 的 ACTION_MOVE 分之里会调用 pullEvent 方法来处理 ACTION_MOVE 事件,实现滑动的效果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
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; switch (mCurrentMode) { case PULL_FROM_END: mFooterLayout.onPull(scale); break; case PULL_FROM_START: default: mHeaderLayout.onPull(scale); 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); } } } |
第 30 行,实现了 PullToRefreshBase 的滑动,是使用 View 的 scrollTo 方法来实现的。
第 32 到 42 行,调用相应得头部或者底部布局的 onPull 方法实现滑动时的动画效果。
第 44 到 48 行,对滑动状态的改变 mState = State.RELEASE_TO_REFRESH。
接下来看 onTouchEvent 的 ACTION_UP 分支
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
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; } |
第 7 行,条件满足调用 setState 方法。在 setState 方法里 State.REFRESHING 分支将调用 onRefreshing 方法。在 onRefreshing 方法里会监听是否滑动到了头部的顶端或者底部的底端。如果是,将调用 callRefreshListener 方法。请看 callRefreshListener 方法。
1 2 3 4 5 6 7 8 9 10 11 12 |
private void callRefreshListener() { if (null != mOnRefreshListener) { mOnRefreshListener.onRefresh(this); } else if (null != mOnRefreshListener2) { if (mCurrentMode == Mode.PULL_FROM_START) { mOnRefreshListener2.onPullDownToRefresh(this); } else if (mCurrentMode == Mode.PULL_FROM_END) { mOnRefreshListener2.onPullUpToRefresh(this); } } } |
在 callRefreshListener 方法中调用了一系列的刷新的监听者的回调方法。这些监听者正是我们自己实现的。在这些方法里可以完成我们需要的刷新操作,比如,重写请求数据。当我们完成这些操作后要主动调用 onRefreshComplete 方法。在 onRefreshComplete 方法中回调用状态重置的方法。重置 PullToRefresh 的状态。
结语
本篇文章就介绍到这里,下篇文章将介绍如何扩展 PullToRefresh。
想获得去掉 5 元限制的证券账户吗?

如果您想去掉最低交易佣金 5 元限制,使用微信扫描左边小程序二维码,访问微信小程序「优财助手」,点击底部菜单「福利」,阅读文章「通过优财开证券账户无最低交易佣金 5 元限制」,按照文章步骤操作即可获得免 5 元证券账户,股票基金交易手续费率万 2.5。
请注意,一定要按照文章描述严格操作,如错误开户是无法获得免 5 元证券账户的。