Android 在RecyclerView中包含ImageSpans的TextView=布局错误

Android 在RecyclerView中包含ImageSpans的TextView=布局错误,android,android-layout,textview,spannablestring,imagespan,Android,Android Layout,Textview,Spannablestring,Imagespan,我正在TextView中插入ImageSpans,这是RecyclerView的一部分。大多数情况下,它工作正常,但当你上下滚动时,文本视图的高度有时是错误的,没有明显的原因-有太多的垂直空白 在特定设备上的这个布局中,包含一行文本的TextView的正确高度是48,错误高度是80(我从HierarchyViewer获得了这些值)。有趣的是,包含一行文本(包括ImageSpan)的TextView的正确高度是80。因此,当TextView被RecyclerView回收时,它有时会保留表示其旧内容

我正在TextView中插入ImageSpans,这是RecyclerView的一部分。大多数情况下,它工作正常,但当你上下滚动时,文本视图的高度有时是错误的,没有明显的原因-有太多的垂直空白

在特定设备上的这个布局中,包含一行文本的TextView的正确高度是48,错误高度是80(我从HierarchyViewer获得了这些值)。有趣的是,包含一行文本(包括ImageSpan)的TextView的正确高度是80。因此,当TextView被RecyclerView回收时,它有时会保留表示其旧内容的高度(包括一个ImageSpan)

此屏幕截图显示“Anore”和“Halal blahs”文本视图为80像素高,这是错误的。“Hello”文本视图在48像素处是正确的

在调查这一点时,我打开了DDMS并运行了“Dump UI hierarchy”(转储UI层次结构),发生了一些有趣的事情:TextView动态地自我校正:

这是非常有说服力的证据,表明问题在于文本视图在更新文本后没有得到正确的布局,因此我尝试了各种方法在文本视图及其父视图上调用forceLayout()和invalidate(),但都没有帮助

我试着用ListView替换RecyclerView,但没有成功。我试着让所有的图片都使用相同的绘图,但运气不好

我运行了HierarchyViewer,但它没有像UI Automator那样“修复”布局。甚至当我按下“无效布局”和“请求布局”按钮时也不行

我没有做任何花哨的事情来设置图像跨度:

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本身的错误。

我猜这里的问题不在于
TextView
的度量,而是TextView容器的度量(一些
ViewGroup
-可能是
线性布局
?)

如果此
ViewGroup
的布局参数设置为高度的
WRAP\u CONTENT
,则由于某种原因,不会再次按时计算高度。要解决此问题,请尝试以下操作:

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?
int heightMeasureSpec = MeasureSpec.makeMeasureSpec(calculatedHeight + topPadding + bottomPadding, MeasureSpec.EXACTLY);
如果您知道单元格的确切高度,也可以使用它-简单地将我计算heightMeasureSpec的行替换为以下内容:

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?
int heightMeasureSpec = MeasureSpec.makeMeasureSpec(calculatedHeight + topPadding + bottomPadding, MeasureSpec.EXACTLY);

希望这有帮助。

有点棘手

setTextSize(someDifferentSize);
setTextSize(normalSize);

似乎这是一种线条高度问题,发生在图像跨度大于它时。如果我有时间,我会深入研究这个问题。

文本视图嵌套在什么地方?是否有对此视图组的引用?已编辑的问题以包括列表项布局。我尝试在父级上无效并强制布局,但没有任何效果。您可以暂时删除
线性布局
,然后查看结果。您可以子类化
TextView
,重写一些有趣的方法(例如,
onMeasure()
),并尝试找出在您认为应该调用的时候没有调用的内容。您可以尝试采用
BufferType
setText()
变量,但不需要这样做。顺便说一句,
android:layout\u-toRightOf
用于
RelativeLayout
的直接子对象,而不是
LinearLayout
的子对象。在
RelativeLayout
内部有一个
LinearLayout
。这几乎是不需要的。为了简洁起见,我发布的布局是实际布局的精简版本。我只发布了TextView的直系父母。在实际布局中还有其他视图使得这个层次结构成为必要。实际上,问题在于TextView本身的度量,因为它的高度是wrap_content,在运行时高度是80,而它应该是48。这是根据HierarchyViewer得出的结论。可能是因为TextViev在内容未被更改的情况下并没有对自身进行测量,而测量是在onBindViewHolder之前进行的。所以当TextView被反弹时,它的内容在那一刻并没有改变,测量结果被破坏。