[컴][안드로이드] offsetLeftAndRight() 과 scrollTo() 의 차이

offsetLeftAndRight() / drawChild() / offsetTopAndBottom() / setTranslateX() 와 offsetLeftAndRight() 의 차이 / offsetLeftAndRight() 을 사용해야 하는 순간

offsetLeftAndRight() 과 setTranslateX()/scrollTo() 의 차이

ViewGroup() 을 만들고, dispatchDraw() 를 override 해서 drawChild() 로 2개의 view 를 그리도록 했다.
지금부터 얘기는 추측성 이야기 이다. 조금 더 확실한 정보를 얻기 위해서는 debugging 을 좀 더 해봐야 할 듯 하다.
둘 다 일정 부분으로 옮겨주는 것은 같다. 하지만 draw 되는 boundary 의 지정에 있어서 차이를 보이는 듯 하다.
둘 다 내부적으로 invalidate() 을 호출하기 때문에 onDraw() 를 호출하게 된다. 하지만 이 때 boundary 때문에 offsetLeftAndRight() 은 제대로 view 가 움직이는 모습을 보여주고, translateX()/scrollTo() 는 움직이면서 사라지는 모습을 보여주기도 한다.

근거

추측의 근거는 이렇다.

아래서 보시다시피 offsetLeftAndRight() 는 invalidate() 의 범위를 지정해 주는 부분을 가지고 있다. 그런데 scrollTo() 는 그런 부분이 없다.

둘 다 이후에 draw 함수를 호출하게 되는데, draw 함수 내부에서는 Animation instance 를 처리할 때 말고는 알아서 boundary 를 정하지 않는 듯이 보인다.

그렇기 때문에 animation 의 처리는 TranslateAnimation() 같은 녀석들을 이용하는 것이 바람직 한 듯 하다.


public void offsetLeftAndRight(int offset) {
 if (offset != 0) {
  updateMatrix();
  final boolean matrixIsIdentity = mTransformationInfo == null
    || mTransformationInfo.mMatrixIsIdentity;
  if (matrixIsIdentity) {
   final ViewParent p = mParent;
   if (p != null && mAttachInfo != null) {
    final Rect r = mAttachInfo.mTmpInvalRect;
    int minLeft;
    int maxRight;
    if (offset < 0) {
     minLeft = mLeft + offset;
     maxRight = mRight;
    } else {
     minLeft = mLeft;
     maxRight = mRight + offset;
    }
    r.set(0, 0, maxRight - minLeft, mBottom - mTop);
    p.invalidateChild(this, r);
   }
  } else {
   invalidate(false);
  }

  mLeft += offset;
  mRight += offset;

  if (!matrixIsIdentity) {
   mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
   invalidate(false);
  }
  invalidateParentIfNeeded();
 }
}


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()) {
                invalidate(true);
            }
        }
    }

scrollTo() 로 그렸을 때의 예

그런 이유로 2개의 view 에 각각 scroller 를 만들고 ViewGroup() 내부에서 호출을 하면, 화면에서 사라져 버린다. 아래 코드가 mHandle 이라는 녀석이 사라지는 코드이다.

   

private Scroller mScroller= new Scroller(getContext());
private Scroller mScrollerFront;
private Scroller mScrollerBehind;
private int mPrevX = 0;
private boolean mExpanded;
private int mTotalOffset = 0;


@Override
protected void onFinishInflate() {
 mHandle = findViewById(mHandleId);
 if (mHandle == null) {
  throw new IllegalArgumentException("The handle attribute is must refer to an"
    + " existing child.");
 }
 mHandle.setOnClickListener(new DrawerToggler());

 mContent = findViewById(mContentId);
 if (mContent == null) {
  throw new IllegalArgumentException("The content attribute is must refer to an" 
    + " existing child.");
 }


 mScrollerFront = new Scroller(mHandle.getContext());
 mScrollerBehind = new Scroller(mContent.getContext());

}

public void myOpen(){
 if (!mScroller.isFinished())
  return;
 
 mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST
   : TOUCH_STATE_SCROLLING;
 mScroller.startScroll(getScrollX(), 0, -100, 0, 5000);
 
 mExpanded = true;
 
 invalidate();
}

@Override
public void computeScroll() {
 
 if (mScroller.computeScrollOffset()) { 
  
  scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
  // mHandle.setTranslationX(offset);
  mHandle.scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
  
  postInvalidate();
 }
}


@Override
protected void dispatchDraw(Canvas canvas) {
 final long drawingTime = getDrawingTime();
 final View handle = mHandle;
 final boolean isVertical = mVertical;

 /**
  * order is important:
  *  order determines the depth of the layer.
  */
 drawChild(canvas, mContent, drawingTime);
 drawChild(canvas, handle, drawingTime);
 
}


@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
 
 int childLeft = 0;
 int childWidth = 0;
 int childWidthHalf;

 // behind view
 childWidth = mContent.getMeasuredWidth();
 childWidthHalf = childWidth /2 ;
 childLeft -= childWidthHalf;
 if (mContent.getVisibility() != View.GONE){
  mContent.layout(childLeft, 0,
   childLeft + childWidth, mContent.getMeasuredHeight());
 }
 
 // front view
 childLeft += childWidthHalf;
 childWidth = mHandle.getMeasuredWidth();
 mHandle.layout(childLeft, 0,
  childLeft + childWidth, mHandle.getMeasuredHeight());

}


 


실제 실행 화면은 아래와 같다. 보기처럼 하얀색 아이콘이 움직였으나 그려지지 않는다.(정확히 얘기하면, 녹색부분을 벗어난 부분은 그려지지 않는다.) 예전에 있던 녹색 바탕만 남아있다.

image

하지만 이것을 offsetLeftAndRight() 로 처리하면 보인다. 코드는 귀찮아서 생략한다. (^^;)

댓글 1개:

  1. 좋은글 감사합니다 많은 도음 되었습니다.

    답글삭제