PullToReFresh 實現 RecycleView 橫向滑動的刷新和加載更多

項目地址:https://github.com/moscoper/PullToRefresh.git

前言

一般的刷新和加載更多的效果都是豎直方向的(下拉刷新和上拉加載更多),本篇文章就利用 PullToReFresh 和 RecycleView 來實現水平方向的刷新和加載更多的效果。我們暫且叫它 PullToReshHorizontalRecycleView。

PullToReshHorizontalRecycleView

我們在 PullToRefresh 的擴展 這篇文章中介紹過如何利用 PullToReFresh 對其他可以滑動的控件添加下拉和上拉的效果。PullToReshHorizontalRecycleView 的實現和其類似。

繼承 PullToRefreshBase

新建類 PullToReshHorizontalRecycleView 繼承 PullToRefreshBase<RecyclerView>。並實現 getPullToRefreshScrollDirectioncreateRefreshableView,isReadyForPullStartisReadyForPullEnd 這四個方法。

getPullToRefreshScrollDirection

方法 getPullToRefreshScrollDirection 的返回值決定了滑動的方向。返回 Orientation.HORIZONTAL 時爲水平滑動;返回Orientation.VERTICAL時爲豎直滑動。這裏我們需要的是水平方向的滑動所以返回Orientation.HORIZONTAL

@Override public Orientation getPullToRefreshScrollDirection() {
    return Orientation.HORIZONTAL;
  }

createRefreshableView

方法createRefreshableView的返回值是要添加刷新效果的控件,這裏我們需要爲橫向滑動的 RecycleView 添加刷新效果所以就返回橫向滑動的 RecycleView。

@Override protected RecyclerView createRefreshableView(Context context, AttributeSet attrs) {
    RecyclerView recyclerView = new RecyclerView(context,attrs);
    LinearLayoutManager mannagerTwo = new LinearLayoutManager(context);
    mannagerTwo.setOrientation(LinearLayoutManager.HORIZONTAL);

    recyclerView.setLayoutManager(mannagerTwo);

    return recyclerView;
  }

isReadyForPullStart

方法isReadyForPullStart返回true時表示可以開始刷新的效果了;返回false時表示還不滿足可以觸發刷新的效果。我們這裏觸發展示刷新效果的條件是 RecycleView 的第一個 item 的左邊緣在屏幕左邊緣的右側。所以isReadyForPullStart的具體實現可以如下:

@Override protected boolean isReadyForPullStart() {
    return isFirstItemVisible();
  }
private boolean isFirstItemVisible() {
    final RecyclerView.Adapter adapter = mRefreshableView.getAdapter();

    if (null == adapter || adapter.getItemCount() ==0) {
      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 (getFirstVisiblePosition() <= 1) {
        final View firstVisibleChild = mRefreshableView.getChildAt(0);
        if (firstVisibleChild != null) {
          return firstVisibleChild.getLeft() >= mRefreshableView.getLeft();
        }
      }
    }

    return false;
  }

isReadyForPullEnd

方法isReadyForPullEnd返回true時表示滿足觸發加載更多效果的條件,否則表示不滿足。這裏滿足觸發加載更多效果的條件是 RecycleView 最後一個 item 的右邊緣在屏幕右邊緣的左邊。所以isReadyForPullEnd的實現如下:

@Override protected boolean isReadyForPullEnd() {
    return isLastItemVisible();
  }
private boolean isLastItemVisible() {
    final RecyclerView.Adapter adapter = mRefreshableView.getAdapter();

    if (null == adapter || adapter.getItemCount()==0) {
      if (DEBUG) {
        Log.d(LOG_TAG, "isLastItemVisible. Empty View.");
      }
      return true;
    } else {
      final int lastItemPosition = adapter.getItemCount() - 1;
      final int lastVisiblePosition = getLastVisiblePosition();

      if (DEBUG) {
        Log.d(LOG_TAG, "isLastItemVisible. Last Item Position: "
            + lastItemPosition
            + " Last Visible Pos: "
            + lastVisiblePosition);
      }

      /**
       * This check should really just be: lastVisiblePosition ==
       * lastItemPosition, but PtRListView internally uses a FooterView
       * which messes the positions up. For me we'll just subtract one to
       * account for it and rely on the inner condition which checks
       * getBottom().
       */
      if (lastVisiblePosition >= lastItemPosition - 1) {
        final int childIndex = lastVisiblePosition - getFirstVisiblePosition();
        final View lastVisibleChild = mRefreshableView.getChildAt(childIndex);
        if (lastVisibleChild != null) {
          return lastVisibleChild.getRight() <= mRefreshableView.getRight();
        }
      }
    }

    return false;
  }

完整代碼

public class PullRefreshRecyclerView extends PullToRefreshBase<RecyclerView> {
  public PullRefreshRecyclerView(Context context, Mode mode) {
    super(context, mode);
  }

  public PullRefreshRecyclerView(Context context) {
    super(context);
  }

  public PullRefreshRecyclerView(Context context, AttributeSet attrs) {
    super(context, attrs);
  }

  public PullRefreshRecyclerView(Context context, Mode mode, AnimationStyle animStyle) {
    super(context, mode, animStyle);
  }

  @Override public Orientation getPullToRefreshScrollDirection() {
    return Orientation.HORIZONTAL;
  }

  @Override protected RecyclerView createRefreshableView(Context context, AttributeSet attrs) {
    RecyclerView recyclerView = new RecyclerView(context,attrs);
    LinearLayoutManager mannagerTwo = new LinearLayoutManager(context);
    mannagerTwo.setOrientation(LinearLayoutManager.HORIZONTAL);
    //recyclerView.addItemDecoration(
    //    new DividerItemDecoration(this, DividerItemDecoration.HORIZONTAL_LIST));
    recyclerView.setLayoutManager(mannagerTwo);

    return recyclerView;
  }

  public void addItemDecoration(RecyclerView.ItemDecoration itemDecoration){
    mRefreshableView.addItemDecoration(itemDecoration);
  }

  public void setAdapter(RecyclerView.Adapter adapter){
    mRefreshableView.setAdapter(adapter);
  }

  @Override protected boolean isReadyForPullEnd() {
    return isLastItemVisible();
  }

  @Override protected boolean isReadyForPullStart() {
    return isFirstItemVisible();
  }

  private boolean isLastItemVisible() {
    final RecyclerView.Adapter adapter = mRefreshableView.getAdapter();

    if (null == adapter || adapter.getItemCount()==0) {
      if (DEBUG) {
        Log.d(LOG_TAG, "isLastItemVisible. Empty View.");
      }
      return true;
    } else {
      final int lastItemPosition = adapter.getItemCount() - 1;
      final int lastVisiblePosition = getLastVisiblePosition();

      if (DEBUG) {
        Log.d(LOG_TAG, "isLastItemVisible. Last Item Position: "
            + lastItemPosition
            + " Last Visible Pos: "
            + lastVisiblePosition);
      }

      /**
       * This check should really just be: lastVisiblePosition ==
       * lastItemPosition, but PtRListView internally uses a FooterView
       * which messes the positions up. For me we'll just subtract one to
       * account for it and rely on the inner condition which checks
       * getBottom().
       */
      if (lastVisiblePosition >= lastItemPosition - 1) {
        final int childIndex = lastVisiblePosition - getFirstVisiblePosition();
        final View lastVisibleChild = mRefreshableView.getChildAt(childIndex);
        if (lastVisibleChild != null) {
          return lastVisibleChild.getRight() <= mRefreshableView.getRight();
        }
      }
    }

    return false;
  }

  private int getFirstVisiblePosition(){
    int position = 0;
   RecyclerView.LayoutManager manager =  mRefreshableView.getLayoutManager();
    if (manager instanceof LinearLayoutManager){
      LinearLayoutManager linearLayoutManager = (LinearLayoutManager) manager;
      return  linearLayoutManager.findFirstVisibleItemPosition();
    }

    return position;
  }

  private int getLastVisiblePosition(){
    int position = 0;
    RecyclerView.LayoutManager manager =  mRefreshableView.getLayoutManager();
    if (manager instanceof LinearLayoutManager){
      LinearLayoutManager linearLayoutManager = (LinearLayoutManager) manager;
      return  linearLayoutManager.findLastVisibleItemPosition();
    }
    return position;
  }

  private boolean isFirstItemVisible() {
    final RecyclerView.Adapter adapter = mRefreshableView.getAdapter();

    if (null == adapter || adapter.getItemCount() ==0) {
      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 (getFirstVisiblePosition() <= 1) {
        final View firstVisibleChild = mRefreshableView.getChildAt(0);
        if (firstVisibleChild != null) {
          return firstVisibleChild.getLeft() >= mRefreshableView.getLeft();
        }
      }
    }

    return false;
  }

}

結語

PullToReshHorizontalRecycleView 的用法和原有的 PullToReFresh 的控件是一樣的,這裏不再贅述。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章