以前一直想写一个自己用的下拉插件,最近自己也看了很多的下拉插件。所以总结了一下,自己写了一个下拉刷新插件。实现的这个下拉刷新的框架,并不是自己的原创,在完成过程中是参考了很多开源的框架,并把自己认为比较好的东西借鉴了过来,从而形成我的东西。

实现原理

1、LoadingLayout是实现刷新头部和尾部布局的主要文件,而CustomLayout则继承自LoadingLayout的抽象类,它定义了一些自定义方法。在LoadingLayout中声明了刷新文字、时间、图片等设置和状态的改变方法。

【1】Header

Headr布局文件主要是实现下拉动画实现,据下拉的距离来改变它的状态,从而显示不同的样式。

【2】Footer

Footer布局文件是自动加载更多等状态显示已经上拉刷新状态改变,从而显示不停的样式。

2、PullRefreshBase是这个刷新框架最重要的基本组件。它是一个继承LinearLayout的抽象类,主要是实现了刷新视图T与Header和Footer以及手势滑动、状态改变的处理逻辑。由于传入的是泛型T,所以它可以支持多种刷新组件的组合。比如LinearLayout、GridView、WebView、RecycleView等,同时也可以传入自己的自定义视图来实现。

【1】OnRefreshListener刷新接口监听。

/**
    * 下拉松手后调用
    *
    * @param refreshView 刷新的view
    */
   void onPullDownRefresh(final PullRefreshBase<V> refreshView);

    /**
     * 加载更多或上拉时调用
     *
     * @param refreshView 刷新的view
     */
    void onPullUpToRefresh(final PullRefreshBase<V> refreshView);

【2】通过对onInterceptTouchEvent方法重写,来对Touch进行拦截来判断滑动手势的操作。同时再根据是否拦截判断是否要传递给子视图。而onTouchEvent方法则根据拦截操作的标识来判断是否消费事件,不然就传递给父视图操作。

/**
     * Touch事件拦截器
     *
     * @param event
     * @return
     */
    @Override
    public final boolean onInterceptTouchEvent(MotionEvent event) {
        if (!isInterceptTouchEventEnabled()) {
            return false;
        }

        if (!isPullLoadEnabled() && !isPullRefreshEnabled()) {
            return false;
        }

        final int action = event.getAction();
        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
            mIsHandledTouchEvent = false;
            return false;
        }

        if (action != MotionEvent.ACTION_DOWN && mIsHandledTouchEvent) {
            return true;
        }

        switch (action) {
            case MotionEvent.ACTION_DOWN:
                mLastMotionY = event.getY();
                mIsHandledTouchEvent = false;
                break;

            case MotionEvent.ACTION_MOVE:
                final float deltaY = event.getY() - mLastMotionY;
                final float absDiff = Math.abs(deltaY);
                // 这里有三个条件:
                // 1,位移差大于mTouchSlop,这是为了防止快速拖动引发刷新
                // 2,isPullRefreshing(),如果当前正在下拉刷新的话,是允许向上滑动,并把刷新的HeaderView挤上去
                // 3,isPullLoading(),理由与第2条相同
                if (absDiff > mTouchSlop || isPullRefreshing() || isPullLoading()) {
                    mLastMotionY = event.getY();
                    // 第一个显示出来,Header已经显示或拉下
                    if (isPullRefreshEnabled() && isReadyForPullDown()) {
                        // 1,Math.abs(getScrollY()) > 0:表示当前滑动的偏移量的绝对值大于0,表示当前HeaderView滑出来了或完全
                        // 不可见,存在这样一种case,当正在刷新时并且RefreshableView已经滑到顶部,向上滑动,那么我们期望的结果是
                        // 依然能向上滑动,直到HeaderView完全不可见
                        // 2,deltaY > 0.5f:表示下拉的值大于0.5f
                        mIsHandledTouchEvent = (Math.abs(getScrollYValue()) > 0 || deltaY > 0.5f);
                        // 如果截断事件,我们则仍然把这个事件交给刷新View去处理,典型的情况是让ListView/GridView将按下
                        // Child的Selector隐藏
                        if (mIsHandledTouchEvent) {
                            mRefreshableView.onTouchEvent(event);
                        }
                    } else if (isPullLoadEnabled() && isReadyForPullUp()) {
                        // 原理如上
                        mIsHandledTouchEvent = (Math.abs(getScrollYValue()) > 0 || deltaY < -0.5f);
                    }
                }
                break;

            default:
                break;
        }

        return mIsHandledTouchEvent;
    }

3、总体布局视图

android 下拉刷新官方 android下拉刷新框架_android 下拉刷新官方

基本组件

1、框架内置提供PullRefreshRecyclerview,PullRefreshListView,PullRefreshWebView这三种刷新组件。PullRefreshRecyclerview最强大,它可以实现LinearView和GridView。而之所以提供PullRefreshListView,是主要供自定义的ListView和adapter来实现速度更快和多种样式的ListView。

【1】PullRefreshRecyclerview是一个自定义的下拉刷新RecyclerView扩展。我们知道RecylerView是support-v7包中的新组件,是一个强大的滑动组件,与经典的ListView相比,同样拥有item回收复用的功能。可以支持GridView与ListView,它的功能更加强大。

这里主要参考了HeaderAndFooterRecyclerViewLRecyclerView

RecyclerAdapter:这个adapter是继承RecyclerView.Adapter的抽象类。主要封装了实现Header、Footer和Contents之间逻辑处理的关系。主要包括:

/**
  * 点击事件接口
  */
  public interface OnItemClickLitener {
     void onItemClick(View view, int position);
     boolean onItemLongClick(View view, int position);
  }


  void addHeaderView(View header)
  void addFooterView(View footer) 
  int getCount()

 /**
  *数据处理逻辑
  */
  void add(D object)
  void addAll(Collection<? extends D> collection)
  void addAll(D[] items)
  void insert(D object, int index)
  void insertAll(D[] object, int index)
  void insertAll(Collection<? extends D> object, int index)
  void remove(D object)
  void remove(int position)
  void clear()
  void sort(Comparator<? super D> comparator) 


 /**
  * 创建视图,交给子类实现方法
  *
  * @param parent
  * @param viewType
  * @return
  */
 public abstract BaseViewHolder OnCreateViewHolder(ViewGroup parent, int viewType);

BaseViewHolder:这个ViewHolder是继承RecyclerView.ViewHolder的抽象类,主要是将视图处理与adapter的逻辑处理分离开来了。因此更加便于处理和操作。在建立adapter时就会要求生成BaseViewHolder。

以上RecyclerAdapter和BaseViewHolder主要参考了EasyRecyclerView

【2】PullRefreshListView是一个自定义的下拉刷新ListView扩展。之所以保留是为了更加方便的自定义和使用自己定义的adapter。

【3】PullRefreshWebView是一个自定义的下拉刷新WebView扩展。只支持下拉刷新。

2、框架内包括了几种常用的基本刷新样式,Headr样式主要包括:DefaultHeaderLoadingLayout、IndicatorViewHeaderLoadingLayout、ProgressBarHeaderLoadingLayout、RotateHeaderLoadingLayout这几种,当然你可以自定义自己的样式。Footer样式主要括:DefaultFooterLoadingLayout、IndicatorViewFooterLoadingLayout。

【1】header样式是继承CustomLoadingLayout,主要是覆盖某些必要的方法,如果要自定义可以参考DefaultHeaderLoadingLayout的样式和布局来操作。footer样式和header样式是一样的继承CustomLoadingLayout,只是处理的逻辑和显示的文字少一些。

【2】样式截图:

a、hader样式:

DefaultHeaderLoadingLayout:

android 下拉刷新官方 android下拉刷新框架_android 下拉刷新官方_02

IndicatorViewHeaderLoadingLayout:

android 下拉刷新官方 android下拉刷新框架_抽象类_03

ProgressBarHeaderLoadingLayout:

android 下拉刷新官方 android下拉刷新框架_下拉刷新_04

RotateHeaderLoadingLayout:

android 下拉刷新官方 android下拉刷新框架_抽象类_05

b、footer样式:

DefaultFooterLoadingLayout:

android 下拉刷新官方 android下拉刷新框架_android_06

IndicatorViewFooterLoadingLayout:

android 下拉刷新官方 android下拉刷新框架_下拉刷新_07

具体用法请看demo