亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么在Android應用中利用RecyclerView實現一個分頁滾動功能

發布時間:2020-12-04 15:33:48 來源:億速云 閱讀:322 作者:Leah 欄目:移動開發

怎么在Android應用中利用RecyclerView實現一個分頁滾動功能?針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。

一、需求分析

最近公司項目要實現一個需求要滿足以下功能:

      1)顯示一個 list 列表, item 數量不固定。

      2)實現翻頁功能,一次翻一頁。

      3)實現翻至某一頁功能。

二、功能實現

2.1 OnTouchListener 記錄當前開始滑動位置

要實現翻頁滑動首先我們要確定是向前翻頁還是向后翻頁,這里通過記錄開始翻頁前當前的位置和滑動后的位置比較即可得知,下面選擇手指觸摸按下時滑動的位置為當前開始滑動位置:

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

好了,當我們確定了滑動方向,下面要考慮的就是如何實現滑動?

2.2 scrollTo(int x, int y) 和 scrollBy(int x, int y) 實現滑動

滑動我們最容易想到的方法就是 scrollTo(int x, int y)scrollBy(int x, int y) 這兩個方法, scrollTo(int x, int y) 是將當前 View 的內容滑動至某一位置, scrollBy(int x, int y) 是將當前 View 內容相對于當前位置滑動一定的距離,其實 scrollBy(int x, int y) 內部是調用了 scrollTo(int x, int y) 方法實現的 一開始想用 scrollTo(int x, int y) 去實現,但是簡單看了下源碼發現, RecyclerView 不支持這個方法:

@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) 去實現滑動。

2.3 OnFlingListener 和 OnScrollListener 調用滑動時機

上面我們決定使用 scrollBy(int x, int y) 去實現滑動,那么現在我們就要確定 scrollBy(int x, int y) 這個方法的調用時機,我們知道當我們滑動 RecyclerView 的時候一般分為兩種情況,一種是手指在屏幕上面緩慢滑動(Scroll),另一種是飛速滑動(onFling),經過一番查閱,發現 RecyclerView 中有這兩種狀態的監聽,那么我們一起看一下這兩種狀態的方法定義,先看 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);
 }

方法的注釋寫的也很清楚,當這個方法被調用并且返回 true 的時候系統就不處理滑動了,而是將滑動交給我們自己處理。所以我們可以監聽這個方法,當我們執行快速滑動的時候在這個方法里面計算要滑動的距離并執行 scrollBy(int x, int y) 實現滑動,然后直接返回 true,表示滑動我們自己處理了,不需要系統處理。

處理代碼如下:

 //當前滑動距離
 private int offsetY = 0;
 private int offsetX = 0;
 //按下屏幕點
 private int startY = 0;
 private int startX = 0;
 //最后一個可見 view 位置
 private int lastItemPosition = -1;
 //第一個可見view的位置
 private int firstItemPosition = -2;
 //總 itemView 數量
 private int totalNum;
@Override
  public boolean onFling(int velocityX, int velocityY) {
   if (mOrientation == ORIENTATION.NULL) {
    return false;
   }
   //獲取開始滾動時所在頁面的index
   int page = getStartPageIndex();
   //記錄滾動開始和結束的位置
   int endPoint = 0;
   int startPoint = 0;
   //如果是垂直方向
   if (mOrientation == ORIENTATION.VERTICAL) {
    //開始滾動位置,當前開始執行 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;
    }
    //更具不同的速度判斷需要滾動的方向
    //一次滾動一個 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();
   }
   //使用動畫處理滾動
   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;
       //這里通過RecyclerView的scrollBy方法實現滾動。
       mRecyclerView.scrollBy(0, dy);
      } else {
       int dx = nowPoint - offsetX;
       mRecyclerView.scrollBy(dx, 0);
      }
     }
    });
    mAnimator.addListener(new AnimatorListenerAdapter() {
     //動畫結束
     @Override
     public void onAnimationEnd(Animator animation) {
      //回調監聽
      if (null != mOnPageChangeListener) {
       mOnPageChangeListener.onPageChange(getPageIndex());
      }
      //滾動完成,進行判斷是否滾到頭了或者滾到尾部了
      RecyclerView.LayoutManager layoutManager = mRecyclerView.getLayoutManager();
      //判斷是當前layoutManager是否為LinearLayoutManager
      // 只有LinearLayoutManager才有查找第一個和最后一個可見view位置的方法
      if (layoutManager instanceof LinearLayoutManager) {
       LinearLayoutManager linearManager = (LinearLayoutManager) layoutManager;
       //獲取最后一個可見view的位置
       lastItemPosition = linearManager.findLastVisibleItemPosition();
       //獲取第一個可見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 滾動監聽方法:

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.
  * 滾動完成調用
  * @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){}
 }

這個監聽類主要有兩個方法一個是 onScrollStateChanged(RecyclerView recyclerView, int newState) 滾動狀態發生變化調用, onScrolled(RecyclerView recyclerView, int dx, int dy) RecyclerView 發生滾動和滾動完成調用。有了這兩個監聽,當我們進行緩慢滑動我們就可以在 onScrollStateChanged(RecyclerView recyclerView, int newState) 中監聽滑動如果結束并且超過一定距離去執行翻頁,而通過 onScrolled(RecyclerView recyclerView, int dx, int dy) 方法可以記錄當前的滑動距離。

處理方法如下:

@Override
  public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
   //如果滑動停止
   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);
     //如果滑動的距離超過屏幕的一半表示需要滑動到下一頁
     move = absY > recyclerView.getHeight() / 2;
     vY = 0;
     if (move) {
      vY = offsetY - startY < 0 &#63; -1000 : 1000;
     }
    } else {
     int absX = Math.abs(offsetX - startX);
     move = absX > recyclerView.getWidth() / 2;
     if (move) {
      vX = offsetX - startX < 0 &#63; -1000 : 1000;
     }
    }
    //調用滑動
    mOnFlingListener.onFling(vX, vY);
   }
  }
 @Override
  public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
   //滾動結束記錄滾動的偏移量
   //記錄當前滾動到的位置
   offsetY += dy;
   offsetX += dx;
  }
 }

到這里我們要實現滑動的方法和時機基本就搞定了,剩下的就是滑動位置計算和滑動效果實現,滑動位置計算就是一次滑動一整頁,這個沒什么可說的,所以簡單說下實現彈性滑動效果。

2.4 ValueAnimator 實現彈性滑動效果

我們知道如果我們直接調用 scrollBy(int x, int y) 這個方法去滑動,那么是沒有緩慢滑動的效果,看著有點愣,所以這里我們通過 ValueAnimator 這個類來實現緩慢滑動的效果,這個就很簡單了,直接貼代碼:

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;
       //這里通過RecyclerView的scrollBy方法實現滾動。
       mRecyclerView.scrollBy(0, dy);
      } else {
       int dx = nowPoint - offsetX;
       mRecyclerView.scrollBy(dx, 0);
      }
     }
    });

2.5 翻頁至某一頁

這里翻頁至某一頁的實現有了上面的基礎就很好實現了,就是直接調用 我們已經實現好了的 onFling(int velocityX, int velocityY) 方法,然后把頁數傳遞過去計算一下就可以了 :

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

關于怎么在Android應用中利用RecyclerView實現一個分頁滾動功能問題的解答就分享到這里了,希望以上內容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注億速云行業資訊頻道了解更多相關知識。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

施甸县| 临邑县| 宜兰市| 大新县| 扬中市| 罗定市| 日照市| 本溪| 鄄城县| 兰坪| 潜江市| 名山县| 乳山市| 富蕴县| 阿克陶县| 汉源县| 合水县| 安溪县| 漯河市| 侯马市| 兰坪| 抚顺市| 卢湾区| 广河县| 南澳县| 双流县| 庆安县| 天全县| 溧水县| 景泰县| 巴里| 道孚县| 绿春县| 东方市| 金寨县| 永修县| 青海省| 周口市| 邹城市| 深泽县| 威海市|