欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android中RecyclerView實(shí)現(xiàn)分頁(yè)滾動(dòng)的方法詳解

 更新時(shí)間:2017年04月24日 15:26:10   作者:Yongyu  
RecyclerView實(shí)現(xiàn)滾動(dòng)相信對(duì)大家來(lái)說(shuō)都不陌生,但是本文主要給大家介紹了利用Android中RecyclerView實(shí)現(xiàn)分頁(yè)滾動(dòng)的思路和方法,可以實(shí)現(xiàn)翻頁(yè)功能,一次翻一頁(yè),也可以實(shí)現(xiàn)翻至某一頁(yè)功能。文中給出了詳細(xì)的示例代碼,需要的朋友可以參考借鑒,下面來(lái)一起看看吧。

一、需求分析

最近公司項(xiàng)目要實(shí)現(xiàn)一個(gè)需求要滿足以下功能:

      1)顯示一個(gè) list 列表, item 數(shù)量不固定。

      2)實(shí)現(xiàn)翻頁(yè)功能,一次翻一頁(yè)。

      3)實(shí)現(xiàn)翻至某一頁(yè)功能。

下面介紹通過(guò) RecyclerView 實(shí)現(xiàn)該需求的實(shí)現(xiàn)過(guò)程(效果圖如下)。

二、功能實(shí)現(xiàn)

2.1 OnTouchListener 記錄當(dāng)前開(kāi)始滑動(dòng)位置

要實(shí)現(xiàn)翻頁(yè)滑動(dòng)首先我們要確定是向前翻頁(yè)還是向后翻頁(yè),這里通過(guò)記錄開(kāi)始翻頁(yè)前當(dāng)前的位置和滑動(dòng)后的位置比較即可得知,下面選擇手指觸摸按下時(shí)滑動(dòng)的位置為當(dāng)前開(kāi)始滑動(dòng)位置:

 //當(dāng)前滑動(dòng)距離
 private int offsetY = 0;
 private int offsetX = 0;
 //按下屏幕點(diǎn)
 private int startY = 0;
 private int startX = 0;
@Override
  public boolean onTouch(View v, MotionEvent event) {
   //手指按下的時(shí)候記錄開(kāi)始滾動(dòng)的坐標(biāo)
   if (event.getAction() == MotionEvent.ACTION_DOWN) {
    //手指按下的開(kāi)始坐標(biāo)
    startY = offsetY;
    startX = offsetX;
   }
   return false;
  }
 }

好了,當(dāng)我們確定了滑動(dòng)方向,下面要考慮的就是如何實(shí)現(xiàn)滑動(dòng)?

2.2 scrollTo(int x, int y) 和 scrollBy(int x, int y) 實(shí)現(xiàn)滑動(dòng)

滑動(dòng)我們最容易想到的方法就是 scrollTo(int x, int y)scrollBy(int x, int y) 這兩個(gè)方法, scrollTo(int x, int y) 是將當(dāng)前 View 的內(nèi)容滑動(dòng)至某一位置, scrollBy(int x, int y) 是將當(dāng)前 View 內(nèi)容相對(duì)于當(dāng)前位置滑動(dòng)一定的距離,其實(shí) scrollBy(int x, int y) 內(nèi)部是調(diào)用了 scrollTo(int x, int y) 方法實(shí)現(xiàn)的 一開(kāi)始想用 scrollTo(int x, int y) 去實(shí)現(xiàn),但是簡(jiǎn)單看了下源碼發(fā)現(xiàn), RecyclerView 不支持這個(gè)方法:

@Override
 public void scrollTo(int x, int y) {
  Log.w(TAG, "RecyclerView does not support scrolling to an absolute position. "
    + "Use scrollToPosition instead");
 }

所以這里我們就選擇使用 scrollBy(int x, int y) 去實(shí)現(xiàn)滑動(dòng)。

2.3 OnFlingListener 和 OnScrollListener 調(diào)用滑動(dòng)時(shí)機(jī)

上面我們決定使用 scrollBy(int x, int y) 去實(shí)現(xiàn)滑動(dòng),那么現(xiàn)在我們就要確定 scrollBy(int x, int y) 這個(gè)方法的調(diào)用時(shí)機(jī),我們知道當(dāng)我們滑動(dòng) RecyclerView 的時(shí)候一般分為兩種情況,一種是手指在屏幕上面緩慢滑動(dòng)(Scroll),另一種是飛速滑動(dòng)(onFling),經(jīng)過(guò)一番查閱,發(fā)現(xiàn) RecyclerView 中有這兩種狀態(tài)的監(jiān)聽(tīng),那么我們一起看一下這兩種狀態(tài)的方法定義,先看 onFling(int velocityX, int velocityY)

Note: 由于使用了 RecyclerView 的 OnFlingListener,所以 RecycleView 的版本必須要 recyclerview-v7:25.0.0 以上。

/**
 * This class defines the behavior of fling if the developer wishes to handle it.
 * <p>
 * Subclasses of {@link OnFlingListener} can be used to implement custom fling behavior.
 *
 * @see #setOnFlingListener(OnFlingListener)
 */
 public static abstract class OnFlingListener {
  /**
  * Override this to handle a fling given the velocities in both x and y directions.
  * Note that this method will only be called if the associated {@link LayoutManager}
  * supports scrolling and the fling is not handled by nested scrolls first.
  *
  * @param velocityX the fling velocity on the X axis
  * @param velocityY the fling velocity on the Y axis
  *
  * @return true if the fling washandled, false otherwise.
  */
  public abstract boolean onFling(int velocityX, int velocityY);
 }

方法的注釋寫的也很清楚,當(dāng)這個(gè)方法被調(diào)用并且返回 true 的時(shí)候系統(tǒng)就不處理滑動(dòng)了,而是將滑動(dòng)交給我們自己處理。所以我們可以監(jiān)聽(tīng)這個(gè)方法,當(dāng)我們執(zhí)行快速滑動(dòng)的時(shí)候在這個(gè)方法里面計(jì)算要滑動(dòng)的距離并執(zhí)行 scrollBy(int x, int y) 實(shí)現(xiàn)滑動(dòng),然后直接返回 true,表示滑動(dòng)我們自己處理了,不需要系統(tǒng)處理。

處理代碼如下:

 //當(dāng)前滑動(dòng)距離
 private int offsetY = 0;
 private int offsetX = 0;
 //按下屏幕點(diǎn)
 private int startY = 0;
 private int startX = 0;
 //最后一個(gè)可見(jiàn) view 位置
 private int lastItemPosition = -1;
 //第一個(gè)可見(jiàn)view的位置
 private int firstItemPosition = -2;
 //總 itemView 數(shù)量
 private int totalNum;
@Override
  public boolean onFling(int velocityX, int velocityY) {
   if (mOrientation == ORIENTATION.NULL) {
    return false;
   }
   //獲取開(kāi)始滾動(dòng)時(shí)所在頁(yè)面的index
   int page = getStartPageIndex();
   //記錄滾動(dòng)開(kāi)始和結(jié)束的位置
   int endPoint = 0;
   int startPoint = 0;
   //如果是垂直方向
   if (mOrientation == ORIENTATION.VERTICAL) {
    //開(kāi)始滾動(dòng)位置,當(dāng)前開(kāi)始執(zhí)行 scrollBy 位置
    startPoint = offsetY;
    if (velocityY < 0) {
     page--;
    } else if (velocityY > 0) {
     page++;
    } else if (pageNum != -1) {
     if (lastItemPosition + 1 == totalNum) {
      mRecyclerView.scrollToPosition(0);
     }
     page = pageNum - 1;
    }
    //更具不同的速度判斷需要滾動(dòng)的方向
    //一次滾動(dòng)一個(gè) mRecyclerView 高度
    endPoint = page * mRecyclerView.getHeight();
   } else {
    startPoint = offsetX;
    if (velocityX < 0) {
     page--;
    } else if (velocityX > 0) {
     page++;
    } else if (pageNum != -1) {
     if (lastItemPosition + 1 == totalNum) {
      mRecyclerView.scrollToPosition(0);
     }
     page = pageNum - 1;
    }
    endPoint = page * mRecyclerView.getWidth();
   }
   //使用動(dòng)畫處理滾動(dòng)
   if (mAnimator == null) {
    mAnimator = ValueAnimator.ofInt(startPoint, endPoint);
    mAnimator.setDuration(300);
    mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
     @Override
     public void onAnimationUpdate(ValueAnimator animation) {
      int nowPoint = (int) animation.getAnimatedValue();
      if (mOrientation == ORIENTATION.VERTICAL) {
       int dy = nowPoint - offsetY;
       if (dy == 0) return;
       //這里通過(guò)RecyclerView的scrollBy方法實(shí)現(xiàn)滾動(dòng)。
       mRecyclerView.scrollBy(0, dy);
      } else {
       int dx = nowPoint - offsetX;
       mRecyclerView.scrollBy(dx, 0);
      }
     }
    });
    mAnimator.addListener(new AnimatorListenerAdapter() {
     //動(dòng)畫結(jié)束
     @Override
     public void onAnimationEnd(Animator animation) {
      //回調(diào)監(jiān)聽(tīng)
      if (null != mOnPageChangeListener) {
       mOnPageChangeListener.onPageChange(getPageIndex());
      }
      //滾動(dòng)完成,進(jìn)行判斷是否滾到頭了或者滾到尾部了
      RecyclerView.LayoutManager layoutManager = mRecyclerView.getLayoutManager();
      //判斷是當(dāng)前l(fā)ayoutManager是否為L(zhǎng)inearLayoutManager
      // 只有LinearLayoutManager才有查找第一個(gè)和最后一個(gè)可見(jiàn)view位置的方法
      if (layoutManager instanceof LinearLayoutManager) {
       LinearLayoutManager linearManager = (LinearLayoutManager) layoutManager;
       //獲取最后一個(gè)可見(jiàn)view的位置
       lastItemPosition = linearManager.findLastVisibleItemPosition();
       //獲取第一個(gè)可見(jiàn)view的位置
       firstItemPosition = linearManager.findFirstVisibleItemPosition();
      }
      totalNum = mRecyclerView.getAdapter().getItemCount();
      if (totalNum == lastItemPosition + 1) {
       updateLayoutManger();
      }
      if (firstItemPosition == 0) {
       updateLayoutManger();
      }
     }
    });
   } else {
    mAnimator.cancel();
    mAnimator.setIntValues(startPoint, endPoint);
   }
   mAnimator.start();
   return true;
  }
 }

再看 OnScrollListener 滾動(dòng)監(jiān)聽(tīng)方法:

public abstract static class OnScrollListener {
  /**
  * Callback method to be invoked when RecyclerView's scroll state changes.
  *
  * @param recyclerView The RecyclerView whose scroll state has changed.
  * @param newState  The updated scroll state. One of {@link #SCROLL_STATE_IDLE},
  *      {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}.
  */
  public void onScrollStateChanged(RecyclerView recyclerView, int newState){}
  /**
  * Callback method to be invoked when the RecyclerView has been scrolled. This will be
  * called after the scroll has completed.
  * <p>
  * This callback will also be called if visible item range changes after a layout
  * calculation. In that case, dx and dy will be 0.
  * 滾動(dòng)完成調(diào)用
  * @param recyclerView The RecyclerView which scrolled.
  * @param dx The amount of horizontal scroll.
  * @param dy The amount of vertical scroll.
  */
  public void onScrolled(RecyclerView recyclerView, int dx, int dy){}
 }

這個(gè)監(jiān)聽(tīng)類主要有兩個(gè)方法一個(gè)是 onScrollStateChanged(RecyclerView recyclerView, int newState) 滾動(dòng)狀態(tài)發(fā)生變化調(diào)用, onScrolled(RecyclerView recyclerView, int dx, int dy) RecyclerView 發(fā)生滾動(dòng)和滾動(dòng)完成調(diào)用。有了這兩個(gè)監(jiān)聽(tīng),當(dāng)我們進(jìn)行緩慢滑動(dòng)我們就可以在 onScrollStateChanged(RecyclerView recyclerView, int newState) 中監(jiān)聽(tīng)滑動(dòng)如果結(jié)束并且超過(guò)一定距離去執(zhí)行翻頁(yè),而通過(guò) onScrolled(RecyclerView recyclerView, int dx, int dy) 方法可以記錄當(dāng)前的滑動(dòng)距離。

處理方法如下:

@Override
  public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
   //如果滑動(dòng)停止
   if (newState == RecyclerView.SCROLL_STATE_IDLE && mOrientation != ORIENTATION.NULL) {
    boolean move;
    int vX = 0, vY = 0;
    if (mOrientation == ORIENTATION.VERTICAL) {
     int absY = Math.abs(offsetY - startY);
     //如果滑動(dòng)的距離超過(guò)屏幕的一半表示需要滑動(dòng)到下一頁(yè)
     move = absY > recyclerView.getHeight() / 2;
     vY = 0;
     if (move) {
      vY = offsetY - startY < 0 ? -1000 : 1000;
     }
    } else {
     int absX = Math.abs(offsetX - startX);
     move = absX > recyclerView.getWidth() / 2;
     if (move) {
      vX = offsetX - startX < 0 ? -1000 : 1000;
     }
    }
    //調(diào)用滑動(dòng)
    mOnFlingListener.onFling(vX, vY);
   }
  }
 @Override
  public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
   //滾動(dòng)結(jié)束記錄滾動(dòng)的偏移量
   //記錄當(dāng)前滾動(dòng)到的位置
   offsetY += dy;
   offsetX += dx;
  }
 }

到這里我們要實(shí)現(xiàn)滑動(dòng)的方法和時(shí)機(jī)基本就搞定了,剩下的就是滑動(dòng)位置計(jì)算和滑動(dòng)效果實(shí)現(xiàn),滑動(dòng)位置計(jì)算就是一次滑動(dòng)一整頁(yè),這個(gè)沒(méi)什么可說(shuō)的,所以簡(jiǎn)單說(shuō)下實(shí)現(xiàn)彈性滑動(dòng)效果。

2.4 ValueAnimator 實(shí)現(xiàn)彈性滑動(dòng)效果

我們知道如果我們直接調(diào)用 scrollBy(int x, int y) 這個(gè)方法去滑動(dòng),那么是沒(méi)有緩慢滑動(dòng)的效果,看著有點(diǎn)愣,所以這里我們通過(guò) ValueAnimator 這個(gè)類來(lái)實(shí)現(xiàn)緩慢滑動(dòng)的效果,這個(gè)就很簡(jiǎn)單了,直接貼代碼:

if (mAnimator == null) {
    mAnimator = ValueAnimator.ofInt(startPoint, endPoint);
    mAnimator.setDuration(300);
    mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
     @Override
     public void onAnimationUpdate(ValueAnimator animation) {
      int nowPoint = (int) animation.getAnimatedValue();
      if (mOrientation == ORIENTATION.VERTICAL) {
       int dy = nowPoint - offsetY;
       if (dy == 0) return;
       //這里通過(guò)RecyclerView的scrollBy方法實(shí)現(xiàn)滾動(dòng)。
       mRecyclerView.scrollBy(0, dy);
      } else {
       int dx = nowPoint - offsetX;
       mRecyclerView.scrollBy(dx, 0);
      }
     }
    });

2.5 翻頁(yè)至某一頁(yè)

這里翻頁(yè)至某一頁(yè)的實(shí)現(xiàn)有了上面的基礎(chǔ)就很好實(shí)現(xiàn)了,就是直接調(diào)用 我們已經(jīng)實(shí)現(xiàn)好了的 onFling(int velocityX, int velocityY) 方法,然后把頁(yè)數(shù)傳遞過(guò)去計(jì)算一下就可以了 :

public void setPageNum(int page) {
 this.pageNum = page;
 mOnFlingListener.onFling(0, 0);
}

到這里前面說(shuō)的功能已經(jīng)全部實(shí)現(xiàn)完畢.

具體代碼細(xì)節(jié)請(qǐng)見(jiàn):

github 地址: pagerecyclerview

本地下載:點(diǎn)擊這里

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)各位Android開(kāi)發(fā)者們能帶來(lái)一定的幫助,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。

相關(guān)文章

最新評(píng)論