#android #resize #rendering #custom-view #viewgroup
#Android #изменение размера #визуализация #пользовательский вид #viewgroup
Вопрос:
Я столкнулся с проблемой, которая поставила меня в тупик, и я надеялся, что кто-нибудь сможет дать мне несколько советов.
Я работаю над приложением, которое использует пользовательскую ViewGroup (фактически FrameLayout, который содержит RelativeLayout) для отображения календаря событий. События внутри календаря представлены в виде представлений, размер которых соответствует продолжительности события и размеру содержащего представления.
Я сталкиваюсь с проблемой, которая возникает при изменении размера содержащего FrameLayout. Текущая реализация удаляет все представления, представляющие события, и пытается добавить новые и рассчитать их размеры на основе текущего размера FrameLayout. Эта работа запускается с помощью метода просмотра onSizeChanged (), который переопределяется в FrameLayout.
При изменении размера представления выполняется этот код и представления обновляются, однако ни одно из них фактически не отображается на экране… представления, содержащиеся в FrameLayout, просто не видны. Если я загружаю представление в инструмент hierarchyviewer, они являются частью дерева представлений и отображаются в обзоре в тех позициях, в которых они должны находиться, но они не отображаются. (Обратите внимание, что представления видны при первоначальном рендеринге FrameLayout … они исчезают только после изменения размера.)
Похоже, что порядок событий во время изменения размера следующий:
onMeasure()
onMeasure()
onSizeChanged()
onLayout()
Вызов requestLayout () после сброса представлений (внутри onSizeChanged()), похоже, не имеет никакого эффекта. Однако, если я вызываю некоторую задержку перед вызовом requestLayout (), представления становятся видимыми. Я могу вызвать эту задержку, запустив поток и перейдя в спящий режим, или создав фиктивную кнопку, которая просто вызывает requestLayout () и нажимает ее после изменения размера самостоятельно, или даже этот уродливый хак, размещенный в конце onSizeChanged ():
post(new Runnable() {
public void run() {
requestLayout();
}
});
Когда я использую этот хак, содержащиеся в нем представления видны, а порядок событий следующий:
onMeasure()
onMeasure()
onSizeChanged()
onLayout()
onMeasure()
onMeasure()
onLayout()
Таким образом, похоже, что принудительное выполнение второго прохода измерения (после изменения дерева представлений) делает содержащиеся в нем представления видимыми так, как они должны быть. Почему задержка вызова requestLayout () приводит к этому, для меня загадка.
Кто-нибудь может предоставить какие-либо указания относительно того, что я делаю неправильно?
Я понимаю, что это несколько сложно понять, не взглянув на некоторый код, поэтому я создал небольшой образец приложения, который демонстрирует мою проблему, и сделал его доступным на Github:
https://github.com/MichaelSims/ViewGroupResizeTest
Упомянутый выше взлом зафиксирован в отдельной ветке:
https://github.com/MichaelSims/ViewGroupResizeTest/tree/post-runnable-hack
Если я могу предоставить какую-либо дополнительную информацию, пожалуйста, дайте мне знать и заранее спасибо.
Ответ №1:
Это не взлом, это то, как это должно работать. onSizeChanged() вызывается как часть прохода layout, и вы не можете / не должны запрашивать Layout() во время прохода layout. Правильно размещать requestLayout() в очереди событий, чтобы указать, что вы изменили иерархию представлений во время прохода макета.
Комментарии:
1. Фантастика! Большое спасибо, что нашли время ответить.
2. Почему onMeasure () следует за onLayout () вместо onDraw (), когда он отправляет requestLayout() в очередь событий?
3. черт возьми, я искал это объяснение часами. Имейте в виду эту функцию, если вы разрабатываете пользовательский макет, который работает аналогично ListView с помощью addViewInLayout — вам нужно опубликовать событие requestLayout через Runnalbe, чтобы обновить measure после добавления всех представлений
Ответ №2:
Я сталкиваюсь с той же проблемой.
Мой onMeasure () был чем-то вроде этого:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
...
//calculate new width and height here
...
setMeasuredDimension(newMeasureSpecWidth, newMeasureSpecHeight);
}
Моя ошибка заключалась в том, что я вызывал super.onMeasure () в первой строке onMeasure (), поэтому внутренние дочерние элементы моей ViewGroup вычислялись на основе размера, который должен был измениться.
Итак, мое исправление делало что-то вроде этого:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
...
//calculate new width and height here
...
int newMeasureSpecWidth = MeasureSpec.makeMeasureSpec(newWidth, MeasureSpec.EXACTLY);
int newMeasureSpecHeight = MeasureSpec.makeMeasureSpec(newHeight, MeasureSpec.EXACTLY);
super.onMeasure(newMeasureSpecWidth, newMeasureSpecHeight);
}
Итак, я устанавливал новый размер для моей ViewGroup, используя вызов super.onMeasure (), а затем он также передает новый размер своим дочерним элементам.
Помните: контракт при переопределении onMeasure() заключается в том, что вы должны вызвать setMeasuredDimension() (и вы можете достичь этого, вызвав сам метод или super.onMeasure())