Подтвердить что ты не робот

Представления внутри пользовательской ViewGroup не отображаются после изменения размера

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

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

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

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

4b9b3361

Ответ 1

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

Ответ 2

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

My 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())