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


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?당신은 당신이 전화를하는 방법 제공하고 있습니다 OnPreDrawListener및 don'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.