Представления внутри пользовательской группы ViewGroup не отображаются после изменения размера - PullRequest
20 голосов
/ 02 мая 2011

Я столкнулся с проблемой, которая поставила меня в тупик, и я надеялся, что кто-то может дать мне несколько советов.

Я работаю над приложением, которое использует пользовательскую ViewGroup (на самом деле FrameLayout, которая содержит RelativeLayout) для визуализации календаря событий. События в календаре представлены в виде представлений, размер которых зависит от продолжительности события и размера содержащего представления.

Я столкнулся с проблемой, возникающей при изменении размера содержащего FrameLayout. Текущая реализация удаляет все представления, которые представляют события, и пытается добавить новые и рассчитать их размеры на основе текущего размера FrameLayout. Эта работа запускается с помощью метода onSizeChanged () View, который переопределяется в FrameLayout.

Когда размер представления изменяется, этот код выполняется и представления обновляются, однако ни один из них фактически не отображается на экране ... представления, содержащиеся в FrameLayout, просто не видны. Если я загружаю представление в инструмент просмотра иерархии, они являются частью дерева представлений и отображаются в обзоре в тех позициях, в которых они должны быть, но они не отображаются. (Обратите внимание, что виды видны на начальном рендере 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

Если я могу предоставить какую-либо дополнительную информацию, пожалуйста, дайте мне знать и заранее спасибо.

Ответы [ 2 ]

25 голосов
/ 02 мая 2011

Это не хак, а то, как это должно работать.onSizeChanged () вызывается как часть этапа макета, и вы не можете / не должны запрашиватьLayout () во время этапа макета.Правильно опубликовать requestLayout () в очереди событий, чтобы указать, что вы изменили иерархию просмотра во время передачи макета.

0 голосов
/ 06 апреля 2016

Я столкнулся с той же проблемой.

Мой 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 ())

...