标签: Android布局下拉刷新pullToRefresh源码
2015-04-02 14:48
2448人阅读
收藏
举报
PullToRefresh 这个库用的是非常至多,github 今天主要分析一下源码实现.
我们通过ListView的下拉刷新进行分析,其它的类似。
整个下拉刷新 父View是LinearLayout, 在LinearLayout添加了Header View ,Footer View,和ListView
PullToRefreshBase 是父类 扩展了 LinearLayout水平布局 如果我们使用ListView 需要观看子类 PullToRefreshAdapterViewBase -> PullToRefreshListView
初始化代码在PullToRefreshBase init方法中
重点代码:
-
-
- mRefreshableView = createRefreshableView(context, attrs);
- addRefreshableView(context, mRefreshableView);
-
-
- mHeaderLayout = createLoadingLayout(context, Mode.PULL_FROM_START, a);
- mFooterLayout = createLoadingLayout(context, Mode.PULL_FROM_END, a);
-
-
- handleStyledAttributes(a);
- updateUIForMode();
- protected void updateUIForMode() {
- final LinearLayout.LayoutParams lp = getLoadingLayoutLayoutParams();
-
- if (this == mHeaderLayout.getParent()) {
- removeView(mHeaderLayout);
- }
- if (mMode.showHeaderLoadingLayout()) {
- addViewInternal(mHeaderLayout, 0, lp);
- }
-
-
- if (this == mFooterLayout.getParent()) {
- removeView(mFooterLayout);
- }
- if (mMode.showFooterLoadingLayout()) {
- addViewInternal(mFooterLayout, lp);
- }
-
-
- refreshLoadingViewsSize();
-
-
-
- mCurrentMode = (mMode != Mode.BOTH) ? mMode : Mode.PULL_FROM_START;
- }
//这里有2个LoadingView,一个是加入到LinearLayout中去了,还有一个是加入到ListView本身的Header里面
看看handleStyledAttributes方法 定位到子类复写的地方
FrameLayout frame = new FrameLayout(getContext());
mHeaderLoadingView = createLoadingLayout(getContext(), Mode.PULL_FROM_START, a);
mHeaderLoadingView.setVisibility(View.GONE);
frame.addView(mHeaderLoadingView, lp);
mRefreshableView.addHeaderView(frame, null, false);//添加LoadingView到ListView Header上
//headerView一共有2个LoadingView,一个是被加入到LinearLayout一个是被加入到ListView的HeaderView
addViewInternal方法就是加入到LinearLayout父类中
看看LoadingLayout 有2种 FlipLoadingLayout 和 RotateLoadingLayout 一般我们用旋转的加载动画
左边一个旋转图片,右边是文字和时间提示
第一个LoadingLayout主要显示 :下拉刷新,放开以刷新
第二个LoadingLayout显示松手后的文字:正在载入...
结构是这样
当UI初始化好,下面看看onTouch 下拉捕获事件
- public final boolean onTouchEvent(MotionEvent event) {
- if (!isPullToRefreshEnabled()) {
- return false;
- }
-
- if (!mScrollingWhileRefreshingEnabled && isRefreshing()) {
- return true;
- }
- if (event.getAction() == MotionEvent.ACTION_DOWN && event.getEdgeFlags() != 0) {
- return false;
- }
- switch (event.getAction()) {
- case MotionEvent.ACTION_MOVE: {
- if (mIsBeingDragged) {
- mLastMotionY = event.getY();
- mLastMotionX = event.getX();
- pullEvent();
- return true;
- }
- break;
- }
-
- case MotionEvent.ACTION_DOWN: {
- if (isReadyForPull()) {
- mLastMotionY = mInitialMotionY = event.getY();
- mLastMotionX = mInitialMotionX = event.getX();
- 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 (isRefreshing()) {
- smoothScrollTo(0);
- return true;
- }
-
-
-
- setState(State.RESET);
-
- return true;
- }
- break;
- }
- }
-
- return false;
- }
- 看看pullEvent方法
- 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);
- }
- }
- }
- 再看看setHeaderScroll方法代码
- protected final void setHeaderScroll(int value) {
- if (DEBUG) {
- Log.d(LOG_TAG, "setHeaderScroll: " + value);
- }
-
- if (DEBUG) {
- Log.d(LOG_TAG, "setHeaderScroll:" + value );
- }
-
-
- final int maximumPullScroll = getMaximumPullScroll();
- value = Math.min(maximumPullScroll, Math.max(-maximumPullScroll, value));
-
- if (mLayoutVisibilityChangesEnabled) {
- if (value < 0) {
- mHeaderLayout.setVisibility(View.VISIBLE);
- } else if (value > 0) { <span style="font-family: Arial, Helvetica, sans-serif;">
- mFooterLayout.setVisibility(View.VISIBLE);
- } else {
- mHeaderLayout.setVisibility(View.INVISIBLE);
- mFooterLayout.setVisibility(View.INVISIBLE);
- }
- }
-
- if (USE_HW_LAYERS) {
-
-
-
-
-
- ViewCompat.setLayerType(mRefreshableViewWrapper, value != 0 ? View.LAYER_TYPE_HARDWARE
- : View.LAYER_TYPE_NONE);
- }
-
-
- switch (getPullToRefreshScrollDirection()) {
- case VERTICAL:
- scrollTo(0, value);
- break;
- case HORIZONTAL:
- scrollTo(value, 0);
- break;
- }
- }
setState(State.REFRESHING, true);//拉倒最顶部 松手,会执行onRefreshing方法,回调我们实现的任务接口 也就是OnRefreshListener
- protected void onRefreshing(final boolean doScroll) {
- if (mMode.showHeaderLoadingLayout()) {
- mHeaderLayout.refreshing();
- }
- if (mMode.showFooterLoadingLayout()) {
- mFooterLayout.refreshing();
- }
-
- if (doScroll) {
- if (mShowViewWhileRefreshing) {
-
-
- OnSmoothScrollFinishedListener listener = new OnSmoothScrollFinishedListener() {
- @Override
- public void onSmoothScrollFinished() {
- callRefreshListener();
- }
- };
-
- switch (mCurrentMode) {
- case MANUAL_REFRESH_ONLY:
- case PULL_FROM_END:
- smoothScrollTo(getFooterSize(), listener);
- break;
- default:
- case PULL_FROM_START:
- smoothScrollTo(-getHeaderSize(), listener);
- break;
- }
- } else {
- smoothScrollTo(0);
- }
- } else {
-
- callRefreshListener();
- }
- }
- 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);
- }
- }
- }
总结:状态包括下拉刷新,松手刷新,正在刷新,Loading隐藏。移动UI还是用的scrollTo最基本的代码. 动画部分可以看LoadingLayout的2个子类
主要的就这些,还有很多细节没有分析。若有问题请指出谢谢。