레이아웃이 언제 그려 졌는지 어떻게 알 수 있습니까?


201

화면에 스크롤 가능한 비트 맵을 그리는 사용자 정의보기가 있습니다. 그것을 초기화하려면 부모 레이아웃 객체의 크기를 픽셀 단위로 전달해야합니다. 그러나 onCreate 및 onResume 함수 중에 Layout이 아직 그려지지 않았으므로 layout.getMeasuredHeight ()는 0을 반환합니다.

해결 방법으로 1 초 동안 기다린 다음 처리하는 처리기를 추가했습니다. 이것은 작동하지만 부주의하며 레이아웃이 그려지기 전에 끝나기 전에 얼마나 많은 시간을 트리밍 할 수 있는지 전혀 모른다.

내가 알고 싶은 것은 레이아웃이 그려지는 시점을 어떻게 알 수 있습니까? 이벤트 나 콜백이 있습니까?

답변:


391

레이아웃에 트리 관찰자를 추가 할 수 있습니다. 올바른 너비와 높이를 반환해야합니다. onCreate()자식 뷰의 레이아웃이 완료되기 전에 호출됩니다. 따라서 너비와 높이는 아직 계산되지 않았습니다. 높이와 너비를 얻으려면 onCreate()메소드에 다음 을 입력하십시오 .

    final LinearLayout layout = (LinearLayout) findViewById(R.id.YOUR_VIEW_ID);
    ViewTreeObserver vto = layout.getViewTreeObserver(); 
    vto.addOnGlobalLayoutListener (new OnGlobalLayoutListener() { 
        @Override 
        public void onGlobalLayout() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                    layout.getViewTreeObserver()
                            .removeOnGlobalLayoutListener(this);
                } else {
                    layout.getViewTreeObserver()
                            .removeGlobalOnLayoutListener(this);
                }
            int width  = layout.getMeasuredWidth();
            int height = layout.getMeasuredHeight(); 

        } 
    });

28
대단히 감사합니다! 그러나 왜 일반적인 문제이기 때문에이 작업을 수행하는 간단한 방법이 없는지 궁금합니다.
felixd

1
놀랄 만한. OnLayoutChangeListener를 시도했지만 작동하지 않았습니다. 그러나 OnGlobalLayoutListener가 작동했습니다. 정말 고맙습니다!
YoYoMyo

8
removeGlobalOnLayoutListener는 API 레벨 16에서 더 이상 사용되지 않습니다. 대신 removeOnGlobalLayoutListener를 사용하십시오.
tounaobun

3
내가보고있는 하나의 "gotcha"는보기가 보이지 않으면 onGlobalLayout이 호출되지만 나중에 getView가 표시되지만 onGlobalLayout ... ugh가 보이지 않는 경우입니다.
Stephen McCormick

1
이것에 대한 또 다른 빠른 해결책은을 사용 view.post(Runnable)하는 것입니다. 더 깨끗하고 같은 일을합니다.
dvdciri

74

정말 쉬운 방법은 단순히 post()레이아웃을 호출 하는 것입니다. 뷰가 이미 생성 된 후 다음 단계로 코드가 실행됩니다.

YOUR_LAYOUT.post( new Runnable() {
    @Override
    public void run() {
        int width  = YOUR_LAYOUT.getMeasuredWidth();
        int height = YOUR_LAYOUT.getMeasuredHeight(); 
    }
});

4
뷰가 이미 작성된 후 게시물이 실제로 코드를 실행할지 여부를 모르겠습니다. post는 UI 스레드에서 이전 큐가 실행 된 후에 실행되도록 RunQueue에 생성 한 Runnable을 추가하기 때문에 보장 할 수 없습니다.
HendraWD

4
반대로 post ()는 다음 UI 단계 업데이트 후에 실행되며, 뷰가 생성 된 후에 발생합니다. 따라서 뷰를 만든 후에 실행됩니다.
Elliptica 2016 년

이 코드를 여러 번 테스트했는데 UI 스레드에서만 가장 잘 작동합니다. 백그라운드 스레드에서는 때때로 작동하지 않습니다.
CoolMind

3
@HendraWijayaDjiono, 아마도이 게시물은 대답 : stackoverflow.com/questions/21937224/…
CoolMind

1
스크롤 할 필요가있는보기 (스크롤보기)에서 게시물을 사용하여 매번 첫 번째 답변을 사용할 수없는 문제가 발생하여보기가 준비되는 즉시 원하는 위치로 올바르게 스크롤 할 수있었습니다. scrollTo 메소드 호출.
Danuofr

21

더 이상 사용되지 않는 코드 및 경고를 피하려면 다음을 사용할 수 있습니다.

view.getViewTreeObserver().addOnGlobalLayoutListener(
        new ViewTreeObserver.OnGlobalLayoutListener() {
            @SuppressWarnings("deprecation")
            @Override
            public void onGlobalLayout() {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                    view.getViewTreeObserver()
                            .removeOnGlobalLayoutListener(this);
                } else {
                    view.getViewTreeObserver()
                            .removeGlobalOnLayoutListener(this);
                }
                yourFunctionHere();
            }
        });

1
이 코드를 사용하려면 'view'를 'final'로 선언해야합니다.
BeccaP

2
대신이 조건을 사용하십시오. Build.VERSION.SDK_INT <Build.VERSION_CODES.JELLY_BEAN
HendraWD

4

kotlin 확장 기능으로 개선

inline fun View.waitForLayout(crossinline yourAction: () -> Unit) {
    val vto = viewTreeObserver
    vto.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
        override fun onGlobalLayout() {
            when {
                vto.isAlive -> {
                    vto.removeOnGlobalLayoutListener(this)
                    yourAction()
                }
                else -> viewTreeObserver.removeOnGlobalLayoutListener(this)
            }
        }
    })
}

재사용 할 수있는 매우 훌륭하고 우아한 솔루션.
Ionut Negru

3

또 다른 대답은 :
에서 치수보기를 확인해보십시오 onWindowFocusChanged.


3

일반적인 방법의 대안은 뷰의 도면에 연결하는 것입니다.

OnPreDrawListener뷰를 표시 할 때 여러 번 호출되므로 뷰의 측정 된 너비 또는 높이가 특정한 반복은 없습니다. 이를 위해서는 지속적으로 확인 ( view.getMeasuredWidth() <= 0)하거나 measuredWidth0보다 큰 횟수로 제한을 설정해야합니다 .

또한 뷰가 그려지지 않을 가능성이 있으며, 이는 코드의 다른 문제를 나타낼 수 있습니다.

final View view = [ACQUIRE REFERENCE]; // Must be declared final for inner class
ViewTreeObserver viewTreeObserver = view.getViewTreeObserver();
viewTreeObserver.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
    @Override
    public boolean onPreDraw() {
        if (view.getMeasuredWidth() > 0) {     
            view.getViewTreeObserver().removeOnPreDrawListener(this);
            int width = view.getMeasuredWidth();
            int height = view.getMeasuredHeight();
            //Do something with width and height here!
        }
        return true; // Continue with the draw pass, as not to stop it
    }
});

문제는 How can you tell when a layout has been drawn?당신은 당신이 전화를하는 방법 제공하고 있습니다 OnPreDrawListenerdon't stop interrogating that view until it has provided you with a reasonable answer솔루션에 대한 반대가 명백해야한다 있도록한다.
버려진 장바구니

알아야 할 것은 언제 그려 지는지가 아니라 측정 된 때입니다. 내 코드는 실제로 요청 해야하는 질문에 대답합니다.
jwehrle

이 솔루션에 문제가 있습니까? 직면 한 문제를 해결하지 못합니까?
jwehrle

1
뷰는 배치 될 때까지 측정되지 않으므로이 대안은 과도하며 중복 검사를 수행합니다. 당신은 그 질문에 답하지 않고, 당신이 느끼는 것을 물어보아야합니다. 원래 의견은이 두 가지 점을 모두 다루었지만 코드 자체에 편집으로 제출 된 문제도 있습니다.
버려진 카트

좋은 편집입니다. 감사합니다. 내가 묻는 것은 추측입니다.이 답변을 삭제해야한다고 생각하십니까? OP의 문제가 발생했습니다. 이것은 문제를 해결했다. 나는 선의로 그것을 제공했습니다. 그게 실수 였어? StackOverflow에서 수행해서는 안되는 것입니까?
jwehrle

2

레이아웃이나 뷰에서 post 메소드를 호출하여 간단히 확인하십시오.

 view.post( new Runnable() {
     @Override
     public void run() {
        // your layout is now drawn completely , use it here.
      }
});

이것은 사실이 아니며 가능한 문제를 숨길 수 있습니다. post실행 가능한 실행 파일을 대기열에 넣습니다.보기가 측정되고 덜 그려지는 것을 보장하지는 않습니다.
Ionut Negru

@ IonutNegru는 아마이 stackoverflow.com/a/21938380
Bruno Bieri

@BrunoBieri, 뷰 측정을 변경하고 각 측정 후에 대기중인 작업이 실행되고 예상 값을 수신하는지 확인하는 비동기 기능으로이 동작을 테스트 할 수 있습니다.
Ionut Negru

1

경우 onMeasure라고보기는 측정 된 너비 / 높이를 가져옵니다. 이 후에 전화를 걸 수 있습니다 layout.getMeasuredHeight().


4
onMeasure가 언제 발생하는지 알기 위해 사용자 정의보기를 만들 필요가 없습니까?
포옹에 괴짜

예, 측정에 액세스하려면 사용자 정의보기를 사용해야합니다.
Trevor Hart
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.