Android 에서 Scroller 의 스크롤 원 리 를 깊이 이해 하 다.

View 의 부 드 러 운 스크롤 효과
View 의 부 드 러 운 스크롤 효 과 를 실현 하 는 것 은 무엇 입 니까?간단 한 예 를 들 어 하나의 View 는 우리 가 지정 한 시간 내 에 한 위치 에서 다른 위치 로 굴 러 갑 니 다.우 리 는 Scroller 류 를 이용 하여 등 속 스크롤 을 실현 할 수 있 습 니 다.먼저 가속 한 후에 속 도 를 줄 일 수 있 고 먼저 속 도 를 줄 인 후에 속 도 를 올 릴 수 있 습 니 다.순간 적 인 이동 효과 가 아 닙 니 다.그래서 Scroller 는 우리 가 많은 미끄럼 효 과 를 실현 하 는 데 도움 을 줄 수 있다.
먼저 Scroller 의 용법 을 살 펴 보 겠 습 니 다.기본적으로'3 부작'으로 요약 할 수 있 습 니 다.
1.Scroller 대상 을 만 듭 니 다.보통 View 의 구조 기 에서 만 듭 니 다.

public ScrollViewGroup(Context context) {
  this(context, null);
}

public ScrollViewGroup(Context context, AttributeSet attrs) {
  this(context, attrs, 0);
}

public ScrollViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  mScroller = new Scroller(context);
}
2.View 의 coptute Scroll()방법 을 다시 쓰 면 다음 코드 는 기본적으로 변 하지 않 습 니 다.

@Override
public void computeScroll() {
  super.computeScroll();
  if (mScroller.computeScrollOffset()) {
    scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
    postInvalidate();
  }
}
3.startScroll()방법 을 호출 합 니 다.startX 와 startY 는 스크롤 을 시작 하 는 좌표 점 이 고 dx 와 dy 는 대응 하 는 오프셋 입 니 다.

mScroller.startScroll (int startX, int startY, int dx, int dy);
invalidate();
위의 세 단 계 는 바로 Scroller 의 기본 적 인 용법 이다.
그럼 다음 임 무 는 Scroller 의 스크롤 원 리 를 분석 하 는 것 입 니 다.
그 전에 우 리 는 또 한 가지 해 야 할 일이 있다.그것 은 바로 scrollTo()scrollBy() 의 원 리 를 밝 히 는 것 이다.scrollTo()scrollBy() 의 차 이 는 중복 서술 하지 않 겠 습 니 다.모 르 는 것 은 스스로 구 글 이나 바 이 두 를 할 수 있 습 니 다.
아래 에 scrollTo() 의 소스 코드 를 붙 입 니 다.

public void scrollTo(int x, int y) {
  if (mScrollX != x || mScrollY != y) {
    int oldX = mScrollX;
    int oldY = mScrollY;
    mScrollX = x;
    mScrollY = y;
    invalidateParentCaches();
    onScrollChanged(mScrollX, mScrollY, oldX, oldY);
    if (!awakenScrollBars()) {
      postInvalidateOnAnimation();
    }
  }
}
mScrollXmScrollY 을 설정 한 후 onScrollChanged(mScrollX, mScrollY, oldX, oldY);  을 호출 하면 View 가 다시 그 려 집 니 다.이렇게 해서 미끄럼 효과 에 이 르 렀 다.
다음은 scrollBy() 을 살 펴 보 겠 습 니 다.  :

public void scrollBy(int x, int y) {
  scrollTo(mScrollX + x, mScrollY + y);
}
이렇게 짧 은 코드 는 모두 가 알 고 있 을 것 이 라 고 믿 습 니 다.원래 scrollBy() 내 부 는 scrollTo() 을 호출 했 습 니 다.그러나 scrollTo() /scrollBy() 의 스크롤 은 모두 순간 에 완 성 된 것 입 니 다.어떻게 해야만 부 드 러 운 스크롤 을 실현 할 수 있 습 니까?
여러분 들 은 이런 생각 을 가지 고 있 는 지 모 르 겠 습 니 다.만약 에 우리 가 구 를 오프셋 을 몇 개의 작은 오프셋 으로 나 누 면 당연히 이 분량 이 커 야 합 니 다.그리고 scrollTo()/scrollBy() 으로 매번 작은 오프셋 을 굴 립 니 다.일정한 시간 안에 부 드 럽 게 구 르 는 것 이 되 지 않 습 니까?맞 아,Scroller 는 바로 이 원 리 를 빌려 부 드 러 운 스크롤 을 실현 하 는 거 야.
다음은 소스 코드 를 살 펴 보 겠 습 니 다!
'3 부작'중 1 부 에 따 르 면 먼저 Scroller 의 구조 기 를 살 펴 보 자.

public Scroller(Context context, Interpolator interpolator, boolean flywheel) {
  mFinished = true;
  if (interpolator == null) {
    mInterpolator = new ViscousFluidInterpolator();
  } else {
    mInterpolator = interpolator;
  }
  mPpi = context.getResources().getDisplayMetrics().density * 160.0f;
  mDeceleration = computeDeceleration(ViewConfiguration.getScrollFriction());
  mFlywheel = flywheel;

  mPhysicalCoeff = computeDeceleration(0.84f); // look and feel tuning
}
구조 기 에서 주로 플러그 인 을 지정 하 는데 플러그 인 이 지정 되 지 않 으 면 기본 ViscousFluidInterpolator 을 사용 합 니 다.
Scroller 의 startScroll() 을 다시 보 겠 습 니 다.

public void startScroll(int startX, int startY, int dx, int dy, int duration) {
  mMode = SCROLL_MODE;
  mFinished = false;
  mDuration = duration;
  mStartTime = AnimationUtils.currentAnimationTimeMillis();
  mStartX = startX;
  mStartY = startY;
  mFinalX = startX + dx;
  mFinalY = startY + dy;
  mDeltaX = dx;
  mDeltaY = dy;
  mDurationReciprocal = 1.0f / (float) mDuration;
}
startScroll() 에서 스크롤 을 시작 하지 않 고 변수의 초기 값 을 설정 한 것 을 발 견 했 습 니 다.그러면 도대체 무엇이 View 를 스크롤 하기 시 작 했 습 니까?우 리 는 반드시 목 표를 startScroll() 의 다음 문장 invalidate(); 에 집중 해 야 한다.우 리 는 이렇게 이해 할 수 있다.먼저 startScroll() 에 초기 값 을 설정 한 다음 에 invalidate(); 을 사용 하여 View 를 다시 그립 니 다.여기 서 또 하나의 중요 한 점 이 있 습 니 다.draw() 에서 computeScroll() 이라는 방법 을 사용 할 것 입 니 다!
소스 코드 가 너무 길 어서 여기에 붙 이지 않 겠 습 니 다.보고 싶 은 동 화 는 뷰 클래스 에서 boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) 을 찾 아 보면 알 수 있 습 니 다.ViewGroup.drawChild() 방법 을 통 해 서브 뷰 의 draw() 방법 을 호출 할 것 이다.그리고 View 류 에 있 는 computeScroll() 은 빈 방법 이 므 로 우리 가 실현 해 야 한다.

/**
 * Called by a parent to request that a child update its values for mScrollX
 * and mScrollY if necessary. This will typically be done if the child is
 * animating a scroll using a {@link android.widget.Scroller Scroller}
 * object.
 */
public void computeScroll() {
}
위의'3 부작'의 2 부 에서 우 리 는 이미 computeScroll() 을 실현 했다.  。먼저 computeScrollOffset() 을 판 단 했 습 니 다.관련 소스 코드 를 살 펴 보 겠 습 니 다.

/**
 * Call this when you want to know the new location. If it returns true,
 * the animation is not yet finished.
 */ 
public boolean computeScrollOffset() {
  if (mFinished) {
    return false;
  }

  int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);

  if (timePassed < mDuration) {
    switch (mMode) {
    case SCROLL_MODE:
      final float x = mInterpolator.getInterpolation(timePassed * mDurationReciprocal);
      mCurrX = mStartX + Math.round(x * mDeltaX);
      mCurrY = mStartY + Math.round(x * mDeltaY);
      break;
    case FLING_MODE:
      final float t = (float) timePassed / mDuration;
      final int index = (int) (NB_SAMPLES * t);
      float distanceCoef = 1.f;
      float velocityCoef = 0.f;
      if (index < NB_SAMPLES) {
        final float t_inf = (float) index / NB_SAMPLES;
        final float t_sup = (float) (index + 1) / NB_SAMPLES;
        final float d_inf = SPLINE_POSITION[index];
        final float d_sup = SPLINE_POSITION[index + 1];
        velocityCoef = (d_sup - d_inf) / (t_sup - t_inf);
        distanceCoef = d_inf + (t - t_inf) * velocityCoef;
      }

      mCurrVelocity = velocityCoef * mDistance / mDuration * 1000.0f;
      
      mCurrX = mStartX + Math.round(distanceCoef * (mFinalX - mStartX));
      // Pin to mMinX <= mCurrX <= mMaxX
      mCurrX = Math.min(mCurrX, mMaxX);
      mCurrX = Math.max(mCurrX, mMinX);
      
      mCurrY = mStartY + Math.round(distanceCoef * (mFinalY - mStartY));
      // Pin to mMinY <= mCurrY <= mMaxY
      mCurrY = Math.min(mCurrY, mMaxY);
      mCurrY = Math.max(mCurrY, mMinY);

      if (mCurrX == mFinalX && mCurrY == mFinalY) {
        mFinished = true;
      }

      break;
    }
  }
  else {
    mCurrX = mFinalX;
    mCurrY = mFinalY;
    mFinished = true;
  }
  return true;
}
이 방법의 반환 값 은 중요 합 니 다.true 로 돌아 가면 Scroller 의 미끄럼 이 끝나 지 않 았 음 을 설명 합 니 다.false 로 돌아 가면 Scroller 의 미끄럼 이 끝 났 음 을 설명 합 니 다.다시 내부 코드 를 보십시오.먼저 미 끄 러 진 시간 을 계 산 했 습 니 다.미 끄 러 진 시간 이 전체 미 끄 러 진 시간 보다 적 으 면 미 끄 러 진 시간 이 끝나 지 않 았 음 을 설명 합 니 다.그렇지 않 으 면 미끄럼 이 끝났다 는 뜻 이 고 표시 mFinished = true;  을 설정 합 니 다.미끄럼 이 끝나 지 않 았 을 때 두 개의 mode 로 나 뉘 었 지만 이 두 mode 는 차이 가 많 지 않 은 일 을 했다.대체적으로 아까 의 시간 에 따라 timePassed 와 플러그 인 에 따라 이 시간 에 굴 러 가 는 거 리 를 계산 한 것 이다.mCurrXmCurrY 이다.바로 위의'3 부작'중 2 부의 mScroller.getCurrX()  ,mScroller.getCurrY() 의 값 이다.
그 다음 에 2 부 곡 에서 scrollTo() 방법 으로 지정 점(즉 위의 mCurrX,mCurrY)으로 스크롤 합 니 다.이후 postInvalidate(); 을 호출 하여 View 를 다시 그리고 computeScroll() 을 다시 호출 하여 View 가 지 정 된 위치 로 굴 러 갈 때 까지 순환 시 켰 습 니 다.이로써 Scroller 스크롤 이 끝 났 습 니 다.
사실 Scroller 의 원 리 는 비교적 통속 적 이 고 이해 하기 쉽다.오늘 의 Scroller 해석 을 그림 으로 끝 내 겠 습 니 다.

총결산
자,안 드 로 이 드 에서 Scroller 의 스크롤 원리 에 대한 내용 은 여기까지 입 니 다.질문 이 있 으 면 아래 에 메 시 지 를 남 겨 주세요.이 글 의 내용 이 여러분 의 안 드 로 이 드 개발 에 도움 이 되 기 를 바 랍 니 다.

좋은 웹페이지 즐겨찾기