PullToRefresh 的源码分析

来源:互联网 发布:网络小额度贷款 编辑:程序博客网 时间:2024/06/13 13:45

前言

上篇文章介绍了 PullToRefresh 的继承关系。本片开始将介绍 PullToRefresh 的源码。由于时间的关系,关于PullToRefresh 的源码笔者也将分成两篇来介绍。本篇文章主要介绍 PullToRefresh 的下拉刷新是如何实现的。在下一篇中,将介绍如何扩展 PullToRefresh,并编写 demo。

PullToRefreshBase

从上篇文章中我们知道 IPullToRefresh 是 PullToRefresh 的基类,PullToRefreshBase 实现了 IPullToRefresh。所以我猜测 PullToRefresh 的下拉刷新是在 PullToRefreshBase 中实现的。要实现下拉刷新的效果就牵扯到 View 的事件传递机制。所以我们先从 onInterceptTouchEvent 、onTouchEvent 和 dispatchTouchEvent 方法分析。但是在 PullToRefreshBase 中只有 onInterceptTouchEvent 和 onTouchEvent 。我们先看 onInterceptTouchEvent。

onInterceptTouchEvent

    @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 事件,实现滑动的效果。

    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 分支

    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 方法。

    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。

0 0
原创粉丝点击