Как вы можете определить, когда был составлен макет?

У меня есть пользовательский вид, который рисует прокручиваемое растровое изображение на экране. Чтобы инициализировать его, мне нужно передать размер в пикселях родительского объекта макета. Но во время функций onCreate и onResume макет еще не нарисован, и поэтому layout.getMeasuredHeight() возвращает 0.

В качестве обходного пути я добавил обработчик, чтобы подождать одну секунду, а затем измерить. Это работает, но его неряшливо, и я понятия не имею, насколько я могу сократить время до того, как я закончу до того, как макет нарисован.

Что я хочу знать, как определить, когда макет нарисован? Есть ли событие или обратный вызов?

Ответ 1

Вы можете добавить дерево наблюдателя к макету. Это должно вернуть правильную ширину и высоту. 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(); 

        } 
    });

Ответ 2

Самый простой способ - просто называть post() на вашем макете. Это запустит ваш код следующим шагом после того, как ваше представление уже создано.

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

Ответ 3

Чтобы избежать устаревшего кода и предупреждений, вы можете использовать:

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

Ответ 4

Другой ответ:
Попробуйте проверить размеры View в onWindowFocusChanged.

Ответ 5

Альтернативой обычным методам является подключение к чертежу вида.

OnPreDrawListener вызывается много раз при отображении представления, поэтому не существует конкретной итерации, где у вашего представления есть допустимая измеренная ширина или высота. Это требует, чтобы вы постоянно view.getMeasuredWidth() <= 0 (view.getMeasuredWidth() <= 0) или устанавливали ограничение на количество проверок для measuredWidth ширины больше нуля.

Существует также вероятность того, что представление никогда не будет отображено, что может указывать на другие проблемы с вашим кодом.

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

Ответ 6

Просто проверьте это, вызвав метод post на вашем макете или представлении

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

Ответ 7

Когда onMeasure, представление получает измеренную ширину/высоту. После этого вы можете вызвать layout.getMeasuredHeight().

Ответ 8

Лучше с функциями расширения 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)
            }
        }
    })
}