onMeasure ()를 재정의하는 권위있는 방법?


88

onMeasure ()를 재정의하는 올바른 방법은 무엇입니까? 다양한 접근 방식을 보았습니다. 예를 들어, Professional Android Development 는 MeasureSpec을 사용하여 차원을 계산 한 다음 setMeasuredDimension ()에 대한 호출로 끝납니다. 예를 들면 :

@Override 
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
this.setMeasuredDimension(parentWidth/2, parentHeight);
}

반면 에이 게시물 에 따르면 "올바른"방법은 MeasureSpec을 사용하고 setMeasuredDimensions () 를 호출 한 다음 setLayoutParams ()를 호출 한 다음 super.onMeasure () 를 호출하는 것입니다. 예를 들면 :

@Override 
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
this.setMeasuredDimension(parentWidth/2, parentHeight);
this.setLayoutParams(new *ParentLayoutType*.LayoutParams(parentWidth/2,parentHeight));
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

그렇다면 올바른 방법은 무엇입니까? 어느 쪽도 100 % 효과가 없었습니다.

나는 정말로 내가 묻는 것은 onMeasure (), 레이아웃, 자식 뷰의 크기 등을 설명하는 자습서를 아는 사람이 있습니까?


15
답변이 유용하거나 정확하다고 생각하십니까? 왜로 표시 대답은?
superjos

답변:


68

다른 솔루션은 포괄적이지 않습니다. 어떤 경우에는 작동 할 수 있으며 시작하기에 좋은 곳이지만 작동이 보장되지 않을 수 있습니다.

onMeasure가 호출되면 크기를 변경할 수있는 권한이있을 수도 있고 없을 수도 있습니다. onMeasure ( widthMeasureSpec, heightMeasureSpec)에 전달되는 값 에는 자식보기에서 수행 할 수있는 작업에 대한 정보가 포함됩니다. 현재 세 가지 값이 있습니다.

  1. MeasureSpec.UNSPECIFIED -원하는만큼 커질 수 있습니다.
  2. MeasureSpec.AT_MOST-원하는만큼 크게 (사양 크기까지), 이것은 parentWidth귀하의 예입니다.
  3. MeasureSpec.EXACTLY- 선택의 여지가 없다. 부모가 선택했습니다.

이는 Android가 각 항목에 적합한 크기를 찾기 위해 여러 번 통과 할 수 있도록 하기위한 것입니다. 자세한 내용 은 여기 를 참조하세요.

이러한 규칙을 따르지 않으면 접근 방식이 제대로 작동하지 않을 수 있습니다.

예를 들어 크기 변경이 허용되는지 확인하려면 다음을 수행 할 수 있습니다.

final int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
boolean resizeWidth = widthSpecMode != MeasureSpec.EXACTLY;
boolean resizeHeight = heightSpecMode != MeasureSpec.EXACTLY;

이 정보를 사용하면 코드에서와 같이 값을 수정할 수 있는지 여부를 알 수 있습니다. 또는 다른 일을해야하는 경우. 원하는 크기를 확인하는 빠르고 쉬운 방법은 다음 방법 중 하나를 사용하는 것입니다.

int resolveSizeAndState (int 크기, int measureSpec, int childMeasuredState)

int resolveSize (int size, int measureSpec)

첫 번째는 Honeycomb에서만 사용할 수 있지만 두 번째는 모든 버전에서 사용할 수 있습니다.

참고 : resizeWidth또는 resizeHeight항상 거짓 임을 알 수 있습니다 . 내가 요청한 경우이 사실을 알았습니다 MATCH_PARENT. WRAP_CONTENT부모 레이아웃 을 요청한 다음 UNSPECIFIED 단계에서 크기를 요청 하여이 문제를 해결할 수있었습니다 Integer.MAX_VALUE. 이렇게하면 부모가 onMeasure를 통과 할 때 허용하는 최대 크기가 제공됩니다.


39

문서는이 문제에 대한 권한입니다 : http://developer.android.com/guide/topics/ui/how-android-draws.htmlhttp://developer.android.com/guide/topics/ui/custom -components.html

요약하면 : 재정의 된 onMeasure메서드 의 끝에서 setMeasuredDimension.

을 (를) 호출 super.onMeasure한 후 호출 하면 안됩니다 setMeasuredDimension. 설정 한 내용이 지워집니다. 어떤 상황에서는를 super.onMeasure먼저 호출 한 다음을 호출하여 결과를 수정할 수 setMeasuredDimension있습니다.

호출하지 마십시오 setLayoutParams에서 onMeasure. 레이아웃은 측정 후 두 번째 단계에서 발생합니다.


내 경험에 따르면 super.onMeasure를 호출하지 않고 onMeasure를 재정의하면 OnLayout이 자식 뷰에서 호출되지 않습니다.
윌리엄 Jockusch

2

나는 그것이 당신이 무시하는 부모에 달려 있다고 생각합니다.

예를 들어 FrameLayout과 같은 ViewGroup을 확장하는 경우 크기를 측정 할 때 아래와 같이 호출해야합니다.

super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
                MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));

ViewGroup이 나머지 작업을 수행하기를 원할 수 있기 때문입니다 (자식보기에서 일부 작업 수행).

View를 확장하는 경우 (예 : ImageView) this.setMeasuredDimension(width, height); . 부모 클래스는 일반적으로했던 것과 같은 작업을 수행하기 때문입니다.

한마디로, 부모 클래스가 무료로 제공하는 일부 기능을 원한다면 호출해야합니다 super.onMeasure()(보통 MeasureSpec.EXACTLY 모드 측정 스펙 통과). 그렇지 않으면 호출 this.setMeasuredDimension(width, height);로 충분합니다.


1

문제를 해결 한 방법은 다음과 같습니다.

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

        ....

        setMeasuredDimension( measuredWidth, measuredHeight );

        widthMeasureSpec = MeasureSpec.makeMeasureSpec( measuredWidth, MeasureSpec.EXACTLY );
        heightMeasureSpec = MeasureSpec.makeMeasureSpec( measuredHeight, MeasureSpec.EXACTLY);

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

}

또한 ViewPager 구성 요소가 필요했습니다.


3
이것은 적절한 해결책이 아닙니다. super.onMeasure을 사용하여 설정된 치수를 지 웁니다 setMeasuredDimension.
tomrozb

@tomrozb이 아니에요 humptydevelopers.com/2013/05/... 당신은 안드로이드 소스를 확인하실 수 있습니다
Yuli Reiri

@YuliReiri : 완벽한 솔루션!
샤얀 타바타 배에

0

내부에서보기 크기를 변경하는 경우 onMeasure 하면 필요한 모든 것이 setMeasuredDimension호출됩니다. 외부에서 크기를 변경 onMeasure하려면을 호출해야합니다 setLayoutParams. 예를 들어 텍스트가 변경 될 때 텍스트보기의 크기를 변경합니다.


여전히 setMinWidth () 및 setMaxWidth ()를 호출하고 표준 onMeasure가 처리하도록 할 수 있습니다. 사용자 정의 View 클래스 내부에서 setLayoutParams를 호출하지 않는 것이 좋습니다. 실제로 ViewGroups 또는 활동에서 하위보기 동작을 재정의하는 데 사용됩니다.
Dorrin


-1

setLayoutParams 및 측정 값 재계 산은 일반적으로 파생 클래스의 onMeasure에서 수행되므로 자식 뷰의 크기를 올바르게 조정하는 해결 방법이라고 생각합니다.

그러나 이것은 거의 올바르게 작동하지 않습니다 (어떤 이유로 든 ...), measureChildren을 더 잘 호출하거나 (ViewGroup을 파생시킬 때) 필요할 때 비슷한 것을 시도하십시오.


-5

이 코드 조각을 onMeasure ()의 예로 사용할 수 있습니다.

public class MyLayerLayout extends RelativeLayout {

    public MyLayerLayout(Context context) {
        super(context);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
        int parentHeight = MeasureSpec.getSize(heightMeasureSpec);

        int currentChildCount = getChildCount();
        for (int i = 0; i < currentChildCount; i++) {
            View currentChild = getChildAt(i);

            //code to find information

            int widthPercent = currentChildInfo.getWidth();
            int heightPercent = currentChildInfo.getHeight();

//considering we will pass height & width as percentage

            int myWidth = (int) Math.round(parentWidth * (widthPercent / 100.0));
            int myHeight = (int) Math.round(parentHeight * (heightPercent / 100.0));

//Considering we need to set horizontal & vertical position of the view in parent

            AlignmentTraitValue vAlign = currentChildInfo.getVerticalLocation() != null ? currentChildlayerInfo.getVerticalLocation() : currentChildAlignmentTraitValue.TOP;
            AlignmentTraitValue hAlign = currentChildInfo.getHorizontalLocation() != null ? currentChildlayerInfo.getHorizontalLocation() : currentChildAlignmentTraitValue.LEFT;
            int topPadding = 0;
            int leftPadding = 0;

            if (vAlign.equals(currentChildAlignmentTraitValue.CENTER)) {
                topPadding = (parentHeight - myHeight) / 2;
            } else if (vAlign.equals(currentChildAlignmentTraitValue.BOTTOM)) {
                topPadding = parentHeight - myHeight;
            }

            if (hAlign.equals(currentChildAlignmentTraitValue.CENTER)) {
                leftPadding = (parentWidth - myWidth) / 2;
            } else if (hAlign.equals(currentChildAlignmentTraitValue.RIGHT)) {
                leftPadding = parentWidth - myWidth;
            }
            LayoutParams myLayoutParams = new LayoutParams(myWidth, myHeight);
            currentChildLayoutParams.setMargins(leftPadding, topPadding, 0, 0);
            currentChild.setLayoutParams(myLayoutParams);
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
}

6
이 코드는 근본적으로 잘못되었습니다. 첫째, 레이아웃 모드를 고려하지 않습니다 (Gmmace의 답변 참조). 그것은 나쁘지만 마지막에 super.onMeasure ()를 호출하여 설정 한 값을 재정의하고 (satur9nine의 답변 참조) onMeasure () 재정의 목적을 전혀 무효화하기 때문에 그 효과를 보지 못할 것입니다.
spaaarky21
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.