[컴][안드로이드] scroll 구현시 computeScroll()와 scrollTo() 의 관계


mScroller.startScroll(getScrollX(), 0, delta, 0, Math.abs(delta) * 2);
…
@Override 
public void computeScroll() {
  
  Log.d(TAG, "getCurrX() = " + mScroller.getCurrX());
  Log.d(TAG, "getCurrY() = " + mScroller.getCurrY());

  if (mScroller.computeScrollOffset()) {  
    
    if (mScroller.isFinished())
    {
      Log.d("namh", "mScroller is finished");

      if(mState == SIDEBAR_OPEN){
        mState = SIDEBAR_CLOSE;
      }else{
        mState = SIDEBAR_OPEN;
      }

    
        // make a layout
        requestLayout();
        invalidate();
    
    }
    else
    {
        /**
         * 
         * 
         * Unless, {@link #scrollTo} is not called here,
         * The View works like the one big page, so that
         * the fling works just like the non-elastic drag.
         *  
         */
        Log.d(TAG, "X = "+mScroller.getCurrX());
    
        // invalidate() is not needed after this function.
        // because the view will be invalidated in the {@link #scrollTo}
        scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
        invalidate();
    }
  } 
}


기본적으로 이 녀석은 빈 함수 이기 때문에 override 할 때 굳이 super.computeScroll() 을 호출할 필요는 없다.

이 코드를 보면서 이게 어떻게 동작하는지 분석하고 있었다. 그런데, 처음에 scrollTo(x, y) 가 뜻하는 바가 "x, y 지점으로 scroll 해라" 이기 때문에, scroll 이 끝나고 난 후에 computeScroll() 이 더 이상 호출되지 않을 것이라고 생각했다.

근데 이 녀석(computeScroll())은 일단 ViewGroup 에서 scrolling 을 하고 있을 때 호출되는 녀석이 아니라 draw() 할 때 호출되는 녀석이다.

scrollTo() 를 통해서 특정지점까지 움직이라고 하고, 특정지점에 도착한 모습을 그리면서 draw() 가 호출되는데 그러면서 computeScroll() 이 호출된다.

그렇기 때문에 위의 코드에서 computeScroll() 에서 scrollTo() 를 호출하면, scrollTo() 가 끝나는 시점에 computeScroll() 이 한 번 더 호출된다. 참고로, computeScroll() 은 scrollTo() 와 관련해서 호출되는 invalidate() 에 의해서 화면이 다시 그려지면서 호출되는 듯 하다.(추측)

그래서 이 때 다시 scrollTo() 를 호출해서 좀 더 그리고 다시 computeScroll() 로 돌아오고, 이런 식으로 scroll 이 목적지에 다다를 때까지(mScroller.computeScrollOffset() == false) loop 를 돈다고 보면 되겠다.

이 때 잠시 오해를 한 부분이
mScroller.startScroll();
를 통해 설정해 놓은 목표지점이 scrollTo() 로 인해 바뀌어 버린다고 생각했는데, Scroll() 과 View() 는 다른 object 여서 그럴 일은 없으니까 오해하지 말자.


image



scrollTo() 이후에 invalidate()

위의 같은 코드를 가지고 emulator 에서는 큰 문제가 없었는데, duration 을 길게 잡고 폰에서 테스트를 할 때는 mScroller.getCurrX() 와 getScrollX() 값이 같은 경우가 발생했다. 두값이 같으면 scrollTo() 는 그냥 return 하게 되어있기 때문에 화면이 다시 그려지지 않게 된다. 그래서 scroll 이 되지않고 drag 한채로 정지한 화면이 된다.

확실히 언제 getCurrX() 와 getScrollX() 가 같아지는지에 대해서는 잘 모르겠지만, animation 의 끝부분에 갈수록 그렇게 되는 듯 하다. (추측컨데, 값이 int 여서 float 이라면 같은 값이 아닌 경우에도 같은 값이 나오는 경우가 발생하지 않을까 생각한다.)

그런데 문제는 mScroller.computeScrollOffset()이 true 이기 때문에 아직 animation 이 끝나지 않은 상태인데도 getCurrX() 와 getScrollX() 값이 같아서 scrollTo() 가 아무런 동작도 하지 않아 화면이 그려지다 만 상태로 보이는 듯 하다.

이런 이유로 scrollTo() 에서 invalidate() 를 호출하지 않는 경우에 대비해서 scrollTo() 이후에 명시적으로 invalidate() 를 호출해 주는 것이 정상적인 작동에 도움을 준다.


See Also

  1. http://i5on9i.blogspot.kr/2012/12/scroller-getcurrx-getcurry.html

댓글 없음:

댓글 쓰기