Android 보기 의 그리 기 프로 세 스(위)View 측정

13606 단어 Android보기View
종합 하여 서술 하 다
View 의 그리 기 프로 세 스 는 measure,layot,draw 프로 세 스 로 나 눌 수 있 습 니 다.measure 는 View 의 측정 과정 을 나타 내 고 View 의 너비 와 높이 를 측정 하 는 데 사용 합 니 다.layot 는 View 가 부모 용기 의 위 치 를 확인 하 는 데 사 용 됩 니 다.draw 는 View 를 화면 에 그립 니 다.다음은 View 의 Measure 과정 을 살 펴 보 겠 습 니 다.
측정 과정
View 의 그리 기 프로 세 스 는 ViewRoot 의 permTraversals 방법 에서 시 작 된 것 으로 ViewRoot 는 ViewRootImpl 류 에 대응 합 니 다.ViewRoot 는 performTraversals 에서 performMeasure 방법 을 사용 하여 루트 View 를 측정 합 니 다.performance Measure 방법 에 서 는 View 의 measure 방법 을 호출 합 니 다.View 의 measure 방법 에 대해 서 는 final 유형 입 니 다.즉,이 measure 방법 은 이불 류 에 다시 쓸 수 없습니다.하지만 measure 방법 에서 onMeasure 방법 을 호출 했다.그래서 View 의 하위 클래스 는 onMeasure 방법 을 다시 써 서 각자 의 Measure 과정 을 실현 할 수 있다.여기 서 는 주로 onMeasure 방법 을 분석 하 는 것 이다.
MeasureSpec
MeasureSpec 는 View 클래스 의 정적 내부 클래스 입 니 다.하나의 MeasureSpec 는 부모 레이아웃 이 하위 레이아웃 에 전달 하 는 레이아웃 요 구 를 패키지 합 니 다.모든 MeasureSpec 은 높이 나 너비 의 요 구 를 대표 합 니 다.모든 MesureSpec 는 specSize 와 specMode 로 구성 되 어 있 으 며 32 비트 의 int 값 을 대표 합 니 다.그 중에서 높 은 2 비트 는 specSize 를 대표 하고 낮은 30 비트 는 specMode 를 대표 합 니 다.
Measure Spec 의 측정 모델 은 세 가지 가 있 습 니 다.다음은 이 세 가지 측정 모델 을 소개 합 니 다.
UNSPECIFIED
부모 용 기 는 하위 View 에 대해 아무런 제한 이 없습니다.하위 View 는 모든 크기 일 수 있 습 니 다.
EXACTLY
부모 용 기 는 하위 View 크기 로 구체 적 인 값 을 지정 합 니 다.View 의 최종 크기 는 specSize 입 니 다.대응 View 속성 matchparent 와 구체 적 인 값.
AT_MOST
하위 뷰 의 크기 는 최대 specSize,즉 하위 뷰 의 크기 는 specSize 를 초과 할 수 없습니다.View 속성 에 대응 하 는 wrapcontent.
MeasureSpec 에 서 는 specSize 와 specMode 를 통 해 MakeMeasureSpec 방법 으로 MeasureSpec 을 만 들 수 있 으 며,getMode 와 getSize 를 통 해 MeasureSpec 의 specMode 와 specSize 를 얻 을 수 있다.
View 의 측정 과정
위 에서 언급 한 바 와 같이 View 의 Measure 과정 은 measure 방법 으로 이 루어 지고 measure 방법 은 onMeasure 방법 을 호출 하여 View 의 Measure 과정 을 완성 한다.그럼 onMeasure 방법 을 살 펴 보 겠 습 니 다.

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
      getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
View 의 onMeasure 방법 에 서 는 set Measured Dimension 방법 만 호출 되 었 을 뿐,set Measured Dimension 방법 은 View 의 높 고 넓 은 측정 치 를 설정 하 는 역할 을 합 니 다.View 측정 후 너비 와 높 은 값 은 getDefaultSize 방법 으로 얻 을 수 있 습 니 다.다음은 이 getDefault Size 방법 입 니 다.

public static int getDefaultSize(int size, int measureSpec) {
  int result = size;
  int specMode = MeasureSpec.getMode(measureSpec);
  int specSize = MeasureSpec.getSize(measureSpec);

  switch (specMode) {
  case MeasureSpec.UNSPECIFIED:
    result = size;
    break;
  case MeasureSpec.AT_MOST:
  case MeasureSpec.EXACTLY:
    result = specSize;
    break;
  }
  return result;
}
MeasureSpec 에 대한 ATMOST 와 EXACTLY 모드 에서 바로 되 돌아 오 는 것 은 MeasureSpec 의 specSize 입 니 다.즉,이 specSize 는 View 가 측정 한 크기 입 니 다.UNSPECIFIED 모드 에서 View 의 측정 값 은 getDefaultSize 방법의 첫 번 째 매개 변수 size 입 니 다.이 size 에 대응 하 는 너비 와 높이 는 getSuggested Minimum Width 와 getSuggested Minimum Height 두 가지 방법 으로 얻 을 수 있 습 니 다.다음은 이 두 가지 방법 을 살 펴 보 겠 습 니 다.

protected int getSuggestedMinimumHeight() {
  return (mBackground == null) ? mMinHeight : max(mMinHeight, mBackground.getMinimumHeight());

}

protected int getSuggestedMinimumWidth() {
  return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
}

여기 서 View 너비 와 높 은 수치 가 View 에 존재 하 는 배경 에 따라 설정 되 어 있 는 지 볼 수 있 습 니 다.여기 서 View 의 너비 로 설명 하 겠 습 니 다.View 에 배경 이 없 으 면 View 의 너비 인 mMinWidth 입 니 다.mMinWidth 는 XML 레이아웃 파일 에 minWidth 속성 을 설정 할 수 있 습 니 다.기본 값 은 0 입 니 다.View 의 set Minimum Width()방법 을 호출 하여 값 을 부여 할 수도 있 습 니 다.View 에 배경 이 존재 한다 면 View 자체 의 최소 너비 인 mMinWidth 와 View 배경의 최소 너비 중 최대 치 를 가 져 옵 니 다.
ViewGroup 의 측정 과정
ViewGroup 의 Measure 과정 에 대해 ViewGroup 은 Measure 자체 의 크기 를 처리 하고 하위 View 를 옮 겨 다 니 며 그들의 measure 방법 을 호출 한 다음 에 각 하위 요 소 를 재 귀적 으로 Measure 과정 을 수행 해 야 합 니 다.ViewGroup 에 서 는 onMeasure 방법 을 다시 쓰 지 않 았 습 니 다.ViewGroup 은 추상 적 인 유형 이기 때문에 구체 적 인 ViewGroup 의 onMeasure 방법 에서 이 루어 지 는 과정 이 다 릅 니 다.그러나 뷰 그룹 에 서 는 하위 뷰 를 측정 하 는 measurement Children 방법 을 제공 했다.다음은 이 measure Children 방법 을 살 펴 보 겠 습 니 다.

protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
  final int size = mChildrenCount;
  final View[] children = mChildren;
  for (int i = 0; i < size; ++i) {
    final View child = children[i];
    if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
      measureChild(child, widthMeasureSpec, heightMeasureSpec);
    }
  }
}
뷰 그룹의 모든 하위 뷰 를 가 져 옵 니 다.그리고 View Group 중성자 View 를 옮 겨 다 니 며 measure Child 방법 으로 하위 View 에 대한 측정 을 완성 합 니 다.다음은 measure Child 방법 을 살 펴 보 겠 습 니 다.

protected void measureChild(View child, int parentWidthMeasureSpec,
    int parentHeightMeasureSpec) {
  final LayoutParams lp = child.getLayoutParams();

  final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
      mPaddingLeft + mPaddingRight, lp.width);
  final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
      mPaddingTop + mPaddingBottom, lp.height);

  child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
이 코드 에서 getChild MeasureSpec 방법 을 통 해 하위 View 너비 와 높 은 MeasureSpec 를 얻 을 수 있 습 니 다.그리고 서브 뷰 의 measure 방법 을 호출 하여 뷰 를 측정 하기 시작 했다.getChild MeasureSpec 방법 을 통 해 View 의 MeasureSpec 를 어떻게 얻 었 는 지 살 펴 보 자.

public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
  int specMode = MeasureSpec.getMode(spec);
  int specSize = MeasureSpec.getSize(spec);

  int size = Math.max(0, specSize - padding);

  int resultSize = 0;
  int resultMode = 0;

  switch (specMode) {
  // Parent has imposed an exact size on us
  case MeasureSpec.EXACTLY:
    if (childDimension >= 0) {
      resultSize = childDimension;
      resultMode = MeasureSpec.EXACTLY;
    } else if (childDimension == LayoutParams.MATCH_PARENT) {
      // Child wants to be our size. So be it.
      resultSize = size;
      resultMode = MeasureSpec.EXACTLY;
    } else if (childDimension == LayoutParams.WRAP_CONTENT) {
      // Child wants to determine its own size. It can't be
      // bigger than us.
      resultSize = size;
      resultMode = MeasureSpec.AT_MOST;
    }
    break;

  // Parent has imposed a maximum size on us
  case MeasureSpec.AT_MOST:
    if (childDimension >= 0) {
      // Child wants a specific size... so be it
      resultSize = childDimension;
      resultMode = MeasureSpec.EXACTLY;
    } else if (childDimension == LayoutParams.MATCH_PARENT) {
      // Child wants to be our size, but our size is not fixed.
      // Constrain child to not be bigger than us.
      resultSize = size;
      resultMode = MeasureSpec.AT_MOST;
    } else if (childDimension == LayoutParams.WRAP_CONTENT) {
      // Child wants to determine its own size. It can't be
      // bigger than us.
      resultSize = size;
      resultMode = MeasureSpec.AT_MOST;
    }
    break;

  // Parent asked to see how big we want to be
  case MeasureSpec.UNSPECIFIED:
    if (childDimension >= 0) {
      // Child wants a specific size... let him have it
      resultSize = childDimension;
      resultMode = MeasureSpec.EXACTLY;
    } else if (childDimension == LayoutParams.MATCH_PARENT) {
      // Child wants to be our size... find out how big it should
      // be
      resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
      resultMode = MeasureSpec.UNSPECIFIED;
    } else if (childDimension == LayoutParams.WRAP_CONTENT) {
      // Child wants to determine its own size.... find out how
      // big it should be
      resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
      resultMode = MeasureSpec.UNSPECIFIED;
    }
    break;
  }
  return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}

이 코드 에서 MeasureSpec 에 대한 획득 은 주로 부모 용기 의 MeasureSpec 와 View 자체 의 LayoutParams 에 의 해 이 루어 집 니 다.다음은 표 한 장 을 통 해 그들 간 의 대응 관 계 를 살 펴 보 자.
  
       여기까지 getChild Measure Spec 방법 을 통 해 하위 View 의 Measure Spec 를 얻 은 후 View 의 Measure 방법 을 호출 하여 View 를 측정 하기 시작 했다.
방금 말 한 바 와 같이 View Group 에 대해 추상 적 인 유형 으로 View 의 onMeasure 방법 을 다시 쓰 지 않 았 습 니 다.그러나 구체 적 인 View Group 에 도착 할 때,예 를 들 어 FrameLayout,LinearLayout,RelativeLayout 등 은 onMeasure 방법 을 다시 써 서 자신 과 하위 View 의 Measure 과정 을 완성 한다.다음은 FrameLayout 를 예 로 들 어 Measure 과정 을 살 펴 보 겠 습 니 다.FrameLayout 에서 도 Measure 과정 이 간단 한 편 입 니 다.다음은 FrameLayout 의 onMeasure 방법 을 살 펴 보 겠 습 니 다.

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  int count = getChildCount();

  final boolean measureMatchParentChildren =
      MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
      MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
  mMatchParentChildren.clear();

  int maxHeight = 0;
  int maxWidth = 0;
  int childState = 0;

  for (int i = 0; i < count; i++) {
    final View child = getChildAt(i);
    if (mMeasureAllChildren || child.getVisibility() != GONE) {
      measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
      final LayoutParams lp = (LayoutParams) child.getLayoutParams();
      maxWidth = Math.max(maxWidth,
          child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
      maxHeight = Math.max(maxHeight,
          child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
      childState = combineMeasuredStates(childState, child.getMeasuredState());
      if (measureMatchParentChildren) {
        if (lp.width == LayoutParams.MATCH_PARENT ||
            lp.height == LayoutParams.MATCH_PARENT) {
          mMatchParentChildren.add(child);
        }
      }
    }
  }

  // Account for padding too
  maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();
  maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();

  // Check against our minimum height and width
  maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
  maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());

  // Check against our foreground's minimum height and width
  final Drawable drawable = getForeground();
  if (drawable != null) {
    maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
    maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
  }

  setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
      resolveSizeAndState(maxHeight, heightMeasureSpec,
          childState << MEASURED_HEIGHT_STATE_SHIFT));

  count = mMatchParentChildren.size();
  if (count > 1) {
    for (int i = 0; i < count; i++) {
      final View child = mMatchParentChildren.get(i);
      final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

      final int childWidthMeasureSpec;
      if (lp.width == LayoutParams.MATCH_PARENT) {
        final int width = Math.max(0, getMeasuredWidth()
            - getPaddingLeftWithForeground() - getPaddingRightWithForeground()
            - lp.leftMargin - lp.rightMargin);
        childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
            width, MeasureSpec.EXACTLY);
      } else {
        childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
            getPaddingLeftWithForeground() + getPaddingRightWithForeground() +
            lp.leftMargin + lp.rightMargin,
            lp.width);
      }

      final int childHeightMeasureSpec;
      if (lp.height == LayoutParams.MATCH_PARENT) {
        final int height = Math.max(0, getMeasuredHeight()
            - getPaddingTopWithForeground() - getPaddingBottomWithForeground()
            - lp.topMargin - lp.bottomMargin);
        childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
            height, MeasureSpec.EXACTLY);
      } else {
        childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
            getPaddingTopWithForeground() + getPaddingBottomWithForeground() +
            lp.topMargin + lp.bottomMargin,
            lp.height);
      }

      child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    }
  }
}


이 부분 코드 에서 도 논리 가 간단 해서 주로 두 가지 일 을 완성 했다.먼저 FrameLayout 는 자신의 측정 과정 을 완성 한 다음 에 서브 View 를 옮 겨 다 니 며 View 의 measure 방법 을 실행 하여 View 의 Measure 과정 을 완성 합 니 다.여기 서 코드 는 비교적 간단 해서 상세 한 설명 을 하지 않 습 니 다.
총결산
마지막 으로 View 와 View Group 의 Measure 과정 을 정리 하 겠 습 니 다.View 에 대해 서 는 Measure 가 간단 합 니 다.View 의 높이 와 너비 의 측정 치 를 얻 은 후에 높 고 넓 게 설정 합 니 다.한편,ViewGroup 의 경우 자신의 Measure 과정 을 완성 하 는 것 외 에 서브 View 를 옮 겨 다 니 며 서브 View 의 측정 과정 을 완성 해 야 한다.

좋은 웹페이지 즐겨찾기