如何实现自定义形状的RecyclerView,使其Item环绕ImageView排列?
RecyclerView列表项文字环绕内部ImageView的可行方案
下面是3种不同成本的实现方案,可根据项目兼容需求选择:
方案1:原生API实现(性能最优)
- Android 12(API 31)及以上版本,TextView原生支持图文环绕能力,无需自定义View,直接通过
inlineImage系列属性设置要环绕的图片、对齐方式、边距即可,列表项布局仅需一个TextView控件,滑动性能最好,适合不需要兼容低版本系统的场景。 - 若需要兼容API 31以下版本,可通过自定义TextView实现,核心逻辑是提前计算图片占位区域,再通过
StaticLayout计算文字排版位置,跳过图片区域绘制文字即可,核心代码示例如下:
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); // 计算图片占位区域 mImageRect.set(mPaddingLeft, mPaddingTop, mPaddingLeft + mDrawableWidth + mDrawableMargin, mPaddingTop + mDrawableHeight + mDrawableMargin); // 初始化文字排版实例 mTextLayout = new StaticLayout(mText, mPaint, getMeasuredWidth() - getPaddingLeft() - getPaddingRight(), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); // 计算控件最终高度 int finalHeight = Math.max(mImageRect.bottom, mTextLayout.getHeight() + getPaddingTop() + getPaddingBottom()); setMeasuredDimension(getMeasuredWidth(), finalHeight); } @Override protected void onDraw(Canvas canvas) { // 先绘制目标图片 mDrawable.setBounds(mImageRect); mDrawable.draw(canvas); // 逐行绘制文字,图片覆盖的行仅在右侧绘制 int lineCount = mTextLayout.getLineCount(); float currentY = getPaddingTop(); for (int i = 0; i < lineCount; i++) { float lineTop = mTextLayout.getLineTop(i); float lineBottom = mTextLayout.getLineBottom(i); int textStart = mTextLayout.getLineStart(i); int textEnd = mTextLayout.getLineEnd(i); float baseLineY = currentY + mTextLayout.getLineBaseline(i); if (lineBottom < mImageRect.top || lineTop > mImageRect.bottom) { canvas.drawText(mText, textStart, textEnd, getPaddingLeft(), baseLineY, mPaint); } else { canvas.drawText(mText, textStart, textEnd, mImageRect.right + mDrawableMargin, baseLineY, mPaint); } currentY += mTextLayout.getLineHeight(); } }
方案2:通用布局模拟实现(无自定义逻辑)
如果不想编写自定义View代码,可以用ConstraintLayout或者FlexboxLayout作为列表项根布局模拟效果:
- 先计算单行文高,得出图片高度对应需要缩进的行数N = 图片高度 / 单行文高
- 前N行文本控件的宽度设置为「父布局宽度 - 图片宽度 - 边距」,靠右侧排列,图片固定在左侧对应位置
- N行之后的文本控件宽度设置为占满父布局宽度,即可实现和原生环绕一致的视觉效果
方案3:第三方封装控件实现(集成成本最低)
可直接使用开源社区已经封装好的图文环绕TextView控件,通常只需要和普通TextView一样设置文本内容,再额外配置要环绕的图片资源、位置、边距参数即可,不需要自行处理排版逻辑,适合快速迭代的项目。
列表滑动性能优化注意项
- 自定义View实现时,不要在
onDraw方法中创建StaticLayout、Paint等对象,要提前初始化或者缓存,避免频繁GC导致卡顿 - 如果图片是网络加载的,需要在图片加载完成后主动触发对应列表项的重绘,避免文字和图片位置重叠
- 当不同列表项的图片大小、文本长度差异较大时,给RecyclerView设置
setHasStableIds(true)避免布局刷新时出现闪烁
内容的提问来源于stack exchange,提问作者yaromchikV
相关产品推荐
相关产品推荐

