3

Я вставляю ImageSpans внутри TextViews, которые являются частью RecyclerView. В большинстве случаев он работает нормально, но при прокрутке вверх и вниз иногда высота TextView ошибочна без видимой причины - слишком много вертикальных пробелов.TextView с ImageSpans внутри RecyclerView = неправильный макет

В этом макете на определенном устройстве правильная высота для TextView с 1 ​​строкой текста составляет 48, а неправильная высота - 80 (я получил эти значения из HierarchyViewer). Интересно, что правильная высота для TextView с 1 ​​строкой текста, включая ImageSpan, равна 80. То, что, похоже, происходит, так как TextView получает переработку RecyclerView, иногда он сохраняет высоту, представляющую ее старый контент (который включает в себя ImageSpan).

На этом скриншоте показаны текстовые объекты «Anore» и «Halal blahs» размером 80 пикселей, что неверно. Текстовый текст «Hello» корректен при 48 пикселях.

Wrong height (80)

При исследовании этого я открыл DDMS и побежал «дамп иерархии UI» и что-то интересное произошло: высота TextView исправил себя на лету:

Correct height (48)

Это довольно убедительные доказательства того, что проблема заключается в том, что TextView не корректно раскладывается после обновления текста, поэтому я пробовал различные способы вызова forceLayout() и invalidate() в TextView и его родительских представлениях, но это не помогло.

Я попытался заменить RecyclerView на ListView, не повезло. Я попробовал, чтобы все ImageSpans использовали один и тот же drawable, не повезло.

Я запускал HierarchyViewer, но он не «исправил» макеты, как это сделал UI Automator. Даже когда я нажал кнопки «invalidate layout» и «request layout».

Я не делаю ничего фантазии, чтобы установить ImageSpans:

SpannableStringBuilder stringBuilder = new SpannableStringBuilder(rawText); 
for(int i = inlineImages.size() - 1; i >= 0; i--) { 
    InlineImage img = inlineImages.get(i); 
    stringBuilder.insert(img.idx, "x"); 
    ImageSpan span = new ImageSpan(context, img.drawable); 
    stringBuilder.setSpan(span, img.idx, img.idx + 1, 0); 
} 

holder.mText.setText(stringBuilder); 

// none of this helps 
holder.mText.forceLayout(); 
holder.mText.requestLayout(); 
holder.mText.invalidate(); 

Вот соответствующая часть макета для каждого элемента списка:

<?xml version="1.0" encoding="utf-8"?> 
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
       android:layout_width="match_parent" 
       android:layout_height="match_parent"> 

    <LinearLayout 
     android:id="@+id/speech_bubble" 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content" 
     android:orientation="vertical" 
     android:background="@drawable/speech_bubble_right" 
     android:layout_marginLeft="40dp" 
     > 

     <TextView 
      android:id="@+id/text" 
      android:layout_width="match_parent" 
      android:layout_height="wrap_content" 
      android:textAppearance="?android:attr/textAppearanceMedium" 
      android:layout_toRightOf="@id/timestamp" 
      /> 

    </LinearLayout> 

</RelativeLayout> 

Я попытался недействительности и принуждать расположение на родитель TextView (LinearLayout), но он ничего не сделал.

Я попытался отредактировать макет вниз буквально следующее:

<?xml version="1.0" encoding="utf-8"?> 
<TextView 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    android:id="@+id/text" 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" 
    android:textAppearance="?android:attr/textAppearanceMedium" 
    /> 

.. и проблема все еще происходит. Это определенно вина самого TextView.

+0

Что входит в TextView? У вас есть ссылка на эту ViewGroup? –

+0

Отредактированный вопрос для включения макета списка. Я попытался сделать недействительным и вытеснить макет родителя, но ничего не сделал. – foo64

+0

Вы можете временно удалить «LinearLayout» и посмотреть, каковы ваши результаты. Вы можете подклассифицировать «TextView», переопределить некоторые интересные методы (например, 'onMeasure()') и попытаться выяснить, что не вызвано, когда вы думаете, что это нужно. Вы можете попробовать вариант 'setText()', который принимает 'BufferType', хотя это не нужно. BTW, 'android: layout_toRightOf' для непосредственных детей« RelativeLayout », а не для детей« LinearLayout ». – CommonsWare

ответ

1

Я предполагаю, что проблема здесь не в измерении TextView, а в измерении контейнера TextView (некоторые ViewGroup - возможно, LinearLayout?).

Если этот параметр ViewGroup установлен на высоту, то по какой-то причине высота не рассчитывается снова по времени.Чтобы решить эту проблему, попробуйте следующее:

int widthMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY); 
int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); 
holder.mContainer.measure(widthMeasureSpec, heightMeasureSpec); // perhaps a simple call to requestLayout or forceLayout will be enough? 

Если вы знаете точную высоту ячейки должен быть, вы можете использовать это, а - просто заменить строку, где я вычислить heightMeasureSpec следующим:

int heightMeasureSpec = MeasureSpec.makeMeasureSpec(calculatedHeight + topPadding + bottomPadding, MeasureSpec.EXACTLY); 

Надеюсь, это поможет.

+0

На самом деле проблема заключается в измерении самого TextView, потому что его высота - wrap_content и во время выполнения высота равна 80, когда должно быть 48. Это соответствует иерархии. – foo64

+0

Возможно, это потому, что TextViev не измерил себя, если контент не изменяется, а измерение выполняется до OnBindViewHolder. Поэтому, когда TextView отскакивает, это содержимое не изменяется на тот момент, и измерение нарушено. –

+0

У меня такая же проблема в listview, но это не работает –

0

Есть немного сложнее

setTextSize(someDifferentSize); 
setTextSize(normalSize); 

Похоже, это какая-то проблема высоты линии, произошло, когда диапазон изображения выше, чем это. Я буду копаться в этом, если у меня будет время.

Смежные вопросы