Android 에서 RecyclerView 가 페이지 를 나 누 어 스크롤 하 는 방법 에 대한 상세 한 설명

수요 분석
최근 회사 프로젝트 가 하나의 수 요 를 실현 하려 면 다음 과 같은 기능 을 만족 시 켜 야 한다.
      1)list 목록 을 표시 합 니 다.item 수량 이 고정 되 지 않 습 니 다.
      2)페이지 넘 기기 기능 을 실현 하여 한 번 에 한 페이지 씩 넘 기기.
      3)한 페이지 로 넘 기 는 기능 을 실현 한다.
다음은 RecyclerView 를 통 해 이 수 요 를 실현 하 는 과정(효과 도 는 다음 과 같다)을 소개 한다.

기능 실현
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)방법 으로 이 루어 진 것 이다.그러나 소스 코드 를 간단하게 보 니 RecyclerView 는 이 방법 을 지원 하지 않 는 다.

@Override
 public void scrollTo(int x, int y) {
  Log.w(TAG, "RecyclerView does not support scrolling to an absolute position. "
    + "Use scrollToPosition instead");
 }
그래서 여기 서 우 리 는scrollTo(int x, int y)를 사용 하여 미끄럼 을 실현 하 는 것 을 선택한다.
2.3 OnFling Listener 와 OnScroll Listener 가 미끄럼 시 기 를 호출 합 니 다.
위 에서 우 리 는scrollBy(int x, int y) 을 사용 하여 미끄럼 을 실현 하기 로 결정 했다.그러면 지금 우 리 는scrollBy(int x, int y) 이 방법의 호출 시 기 를 확정 해 야 한다.우 리 는 우리 가 RecyclerView 를 미 끄 러 질 때 보통 두 가지 상황 으로 나 뉜 다 는 것 을 알 고 있다.하 나 는 손가락 이 화면 위 에서 천천히 미 끄 러 지 는 것(Scroll)이 고 다른 하 나 는 빠 른 속도 로 미 끄 러 지 는 것(onFling)이다.한 번 의 조 사 를 통 해RecyclerView 에 이 두 가지 상태의 감청 이 있 는 것 을 발 견 했 습 니 다.그러면 이 두 가지 상태의 방법 정 의 를 살 펴 보 겠 습 니 다.먼저 보 겠 습 니 다scrollBy(int x, int y) .
참고: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);
 }
방법 에 대한 설명 도 잘 쓰 여 있 습 니 다.이 방법 이 호출 되 고 트 루 로 돌아 갈 때 시스템 은 미끄럼 을 처리 하지 않 고 미끄럼 을 우리 자신 에 게 맡 깁 니 다.그래서 우 리 는 이 방법 을 감청 할 수 있 습 니 다.우리 가 빠 른 미끄럼 을 실행 할 때 이 방법 에서 미 끄 러 질 거 리 를 계산 하고 실행onFling(int velocityX, int velocityY) 을 통 해 미끄럼 을 실현 한 다음 에 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){}
 }
이 감청 류 는 주로 두 가지 방법 이 있 는데 하 나 는scrollBy(int x, int y)스크롤 상태 에 변화 호출 이 발생 하고onScrollStateChanged(RecyclerView recyclerView, int newState) 스크롤 과 스크롤 이 발생 하여 호출 이 완료 되 는 것 이다.이 두 개의 감청 이 있 습 니 다.우리 가 천천히 미 끄 러 지면onScrolled(RecyclerView recyclerView, int dx, int dy) RecyclerView 에서 감청 미끄럼 이 끝나 고 일정한 거 리 를 초과 하여 페이지 를 넘 길 수 있 습 니 다.onScrollStateChanged(RecyclerView recyclerView, int newState) 방법 을 통 해 현재 의 미끄럼 거 리 를 기록 할 수 있 습 니 다.
처리 방법 은 다음 과 같다.

@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 ? -1000 : 1000;
     }
    } else {
     int absX = Math.abs(offsetX - startX);
     move = absX > recyclerView.getWidth() / 2;
     if (move) {
      vX = offsetX - startX < 0 ? -1000 : 1000;
     }
    }
    //    
    mOnFlingListener.onFling(vX, vY);
   }
  }
 @Override
  public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
   //            
   //          
   offsetY += dy;
   offsetX += dx;
  }
 }
여기까지 우리 가 미끄럼 을 실현 하 는 방법 과 시 기 는 기본적으로 해결 되 었 다.나머지 는 미끄럼 위치 계산 과 미끄럼 효과 실현 이다.미끄럼 위치 계산 은 한 번 에 한 페이지 씩 미 끄 러 지 는 것 이다.이것 은 할 말 이 없 기 때문에 간단하게 말 해서 탄성 미끄럼 효 과 를 실현 한다.
2.4 ValueAnimator 신축성 슬라이딩 효과 구현
우 리 는 만약 에 우리 가 직접onScrolled(RecyclerView recyclerView, int dx, int dy) 이라는 방법 으로 미 끄 러 지 는 것 을 알 고 있다.그러면 천천히 미 끄 러 지 는 효과 가 없고 약간 어리둥절 해 보인다.그래서 여기 서 우 리 는 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 어느 페이지 로 넘 기기
여기 서 한 페이지 로 넘 기 는 실현 은 위의 기초 가 있 으 면 잘 이 루어 진다.바로 우리 가 이미 실현 한 scrollBy(int x, int y)방법 을 직접 호출 한 다음 에 페이지 수 를 전달 하여 계산 하면 된다.

public void setPageNum(int page) {
 this.pageNum = page;
 mOnFlingListener.onFling(0, 0);
}
여기까지 앞에서 말 한 기능 은 이미 모두 실현 되 었 다.
구체 적 인 코드 세부 사항 은 다음 을 보십시오.
github 주소:pagerecyclerview
로 컬 다운로드:여 기 를 클릭 하 세 요.
총결산
이상 은 이 글 의 전체 내용 입 니 다.본 논문 의 내용 이 여러분 의 안 드 로 이 드 개발 자 들 에 게 어느 정도 도움 이 되 기 를 바 랍 니 다.궁금 한 점 이 있 으 시 면 댓 글 을 남 겨 주 셔 서 저희 에 대한 지지 에 감 사 드 립 니 다.

좋은 웹페이지 즐겨찾기