on 측정 사용자 정의보기 설명


316

사용자 지정 구성 요소를 시도했습니다. View클래스를 확장 하고 onDraw재정의 된 메소드로 그리기를 수행했습니다 . 대체해야하는 이유는 무엇 onMeasure입니까? 내가하지 않으면 모든 것이 옳은 것으로 나타났습니다. 누군가 설명해 주시겠습니까? 내 onMeasure방법을 어떻게 작성해야 합니까? 몇 가지 자습서를 보았지만 각 자습서는 서로 약간 다릅니다. 때때로 그들은 super.onMeasure마지막에 전화 를하고 때로는 사용 setMeasuredDimension하고 호출하지 않았습니다. 차이점은 어디에 있습니까?

결국 나는 정확히 동일한 구성 요소를 여러 개 사용하고 싶습니다. 해당 구성 요소를 XML파일에 추가 했지만 그 구성 요소의 크기를 모릅니다. 나는 (내가에 설정된 크기로해야하는 이유 나중에 그 위치와 크기를 설정할 onMeasure의 경우 onDraw내가 그것을 그릴 때뿐만 아니라, 노력) 사용자 정의 컴포넌트 클래스. 정확히 언제해야합니까?

답변:


735

onMeasure()부모가 제공하는 레이아웃 제약 조건에 따라 사용자 정의보기가 얼마나 큰지 Android에 알릴 수있는 기회입니다. 또한 레이아웃 제약 조건이 무엇인지 배울 수있는 사용자 정의보기의 기회이기도합니다 ( match_parent상황과 상황 에 따라 다르게 행동하려는 wrap_content경우). 이러한 제약 조건은 MeasureSpec메서드에 전달 된 값 으로 패키지됩니다 . 모드 값의 대략적인 상관 관계는 다음과 같습니다.

  • 정확하게layout_width또는 layout_height값이 특정 값으로 설정 되었음을 의미합니다 . 당신은 아마이 크기로 볼 수 있습니다. match_parent크기를 부모 뷰로 정확하게 설정하기 위해 사용될 때 트리거 될 수도 있습니다 (프레임 워크에 따라 레이아웃에 따라 다름).
  • AT_MOST는 일반적으로 layout_width또는 layout_height값이 설정 match_parent되었거나 wrap_content최대 크기가 필요한 위치 (프레임 워크에 따라 레이아웃에 따라 다름)를 의미하며 부모 차원의 크기는 값입니다. 이 크기보다 크지 않아야합니다.
  • UNSPECIFIED 전형적 수단 layout_width또는 layout_height값으로 설정된 wrap_content제한없이. 당신은 당신이 원하는 어떤 크기가 될 수 있습니다. 일부 레이아웃은이 콜백을 사용하여 원하는 크기를 파악하기 전에 두 번째 측정 요청에서 실제로 다시 전달할 사양을 결정합니다.

존재 계약 onMeasure()IS setMeasuredDimension() 해야 당신이보기 싶습니다 크기에 끝에서 호출 될 수있다. 이 메소드는의 기본 구현을 포함하여 모든 프레임 워크 구현에서 View호출 super되므로 사용 사례에 맞는 경우 대신 호출하는 것이 안전합니다 .

프레임 워크가 기본 구현을 적용하기 때문에이 방법을 재정의 할 필요는 없지만 뷰 공간이 컨텐츠보다 작거나 그렇지 않은 경우보기 공간이 작은 경우 클리핑이 나타날 수 있습니다. wrap_content프레임 워크가 얼마나 큰지 알지 못하므로 양방향으로 사용자 정의보기를 사용 하면보기가 전혀 표시되지 않을 수 있습니다!

일반적으로 View기존 위젯이 아닌 재정의 하는 경우 다음과 같이 단순하더라도 구현을 제공하는 것이 좋습니다.

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

    int desiredWidth = 100;
    int desiredHeight = 100;

    int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    int widthSize = MeasureSpec.getSize(widthMeasureSpec);
    int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    int heightSize = MeasureSpec.getSize(heightMeasureSpec);

    int width;
    int height;

    //Measure Width
    if (widthMode == MeasureSpec.EXACTLY) {
        //Must be this size
        width = widthSize;
    } else if (widthMode == MeasureSpec.AT_MOST) {
        //Can't be bigger than...
        width = Math.min(desiredWidth, widthSize);
    } else {
        //Be whatever you want
        width = desiredWidth;
    }

    //Measure Height
    if (heightMode == MeasureSpec.EXACTLY) {
        //Must be this size
        height = heightSize;
    } else if (heightMode == MeasureSpec.AT_MOST) {
        //Can't be bigger than...
        height = Math.min(desiredHeight, heightSize);
    } else {
        //Be whatever you want
        height = desiredHeight;
    }

    //MUST CALL THIS
    setMeasuredDimension(width, height);
}

도움이 되길 바랍니다.


1
안녕하세요 @Devunwired 내가 지금까지 읽은 최고의 설명입니다. 귀하의 설명은 내가 가지고있는 많은 질문에 대답하고 의심을 지우지 만 여전히 하나는 남아 있습니다. LayoutParams 제약 조건에 대한 각 프로브마다 각 자식에게 자신의 제약 조건에 따라 자체 측정하도록 요청합니까?
파라오

47
이 코드는 ViewGroup 하위 클래스의 onMeasure를 재정의하는 경우 수행되지 않습니다. 하위 조회수가 표시되지 않으며 모두 크기가 0x0입니다. 사용자 정의 ViewGroup의 onMeasure를 재정의해야하는 경우 widthMode, widthSize, heightMode 및 heightSize를 변경하고 MeasureSpec.makeMeasureSpec을 사용하여 measureSpecs로 다시 컴파일하고 결과 정수를 super.onMeasure로 전달하십시오.
Alexey

1
환상적인 답변. Google 문서에 따라 패딩을 처리하는 것은 View의 책임입니다.
jonstaff

4
복잡한 c ** p를 사용하면 Android를 사용하기 어려운 레이아웃 시스템으로 만들 수 있습니다. 그들은 단지 getParent (). get *** ()을 가질 수있었습니다.
Oliver Dixon

2
View클래스에 resolveSizeAndStateand 라는 도우미 메소드가있어 resolveSize'if'절의 기능을 수행해야합니다. 특히 IF를 자주 작성 해야하는 경우 유용합니다.
stan0

5

실제로 값은 포장 용기에 따라 다르므로 답은 완전하지 않습니다. 상대 또는 선형 레이아웃의 경우 값은 다음과 같이 동작합니다.

  • 정확히 match_parent는 정확히 + 부모의 크기입니다
  • AT_MOST wrap_content 결과 AT_MOST MeasureSpec
  • 지정 되지 않음

가로 스크롤보기의 경우 코드가 작동합니다.


57
여기에 일부 답변이 불완전하다고 생각되면 부분 답변을 제공하지 말고 추가하십시오.
Michaël

1
이것을 레이아웃 작동 방식에 연결하는 데 도움이되지만, 내 경우에는 onMeasure가 사용자 정의보기로 세 번 호출됩니다. 문제의 뷰는 wrap_content 높이와 가중치 폭 (너비 = 0, 무게 = 1)을 가졌습니다. 첫 번째 통화는 불특정 / 불특정, 두 번째 통화는 AT_MOST / 정확하고 세 번째 통화는 정확히 / 정확했습니다.
윌리엄 T. 청둥 오리

0

측정 값을 변경할 필요가없는 경우이를 무시할 필요가 없습니다.

Devunwired 코드 (여기서 선택되고 가장 투표가 많은 답변)는 SDK 구현이 이미 수행 한 것과 거의 동일합니다 (2009 년 이후로 확인했습니다).

onMeasure 방법은 여기에서 확인할 수 있습니다 .

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
            getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}

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;
}

동일한 코드로 대체 할 SDK 코드를 재정의하는 것은 의미가 없습니다.

"기본 onMeasure ()는 항상 100x100의 크기를 설정합니다"라고 주장하는 이 공식 문서 는 잘못되었습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.