Android TextView:将文本拆分为等长两行并自适应宽度的实现问题
解决TextView平衡换行后宽度无法收缩的问题
我之前做类似需求的时候也踩过这个坑!android:breakStrategy="balanced"确实能帮我们把文本拆分成尽量等长的两行,但wrap_content的测量逻辑有点“死板”——它会先按照文本不换行的宽度计算默认尺寸,之后才会应用换行策略,所以最终TextView的宽度还是会保留原单行文本的宽度,没法自动收缩到换行后单行的实际宽度。
要解决这个问题,我们需要自定义TextView,重写它的测量逻辑,让它在平衡换行后重新计算实际需要的宽度。
自定义BalancedWrapTextView
这里提供一个Kotlin版本的自定义TextView,你可以直接复制使用(Java版本逻辑完全一致,转译即可):
class BalancedWrapTextView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = android.R.attr.textViewStyle ) : AppCompatTextView(context, attrs, defStyleAttr) { override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { // 先让系统完成初始测量,应用换行策略 super.onMeasure(widthMeasureSpec, heightMeasureSpec) // 仅当宽度设置为wrap_content(AT_MOST模式)时才处理 if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.AT_MOST) { val layout = layout ?: return // 遍历所有行,找到最宽的那一行的宽度(平衡换行后两行宽度几乎一致) var maxLineWidth = 0f for (i in 0 until layout.lineCount) { val lineWidth = layout.getLineWidth(i) if (lineWidth > maxLineWidth) { maxLineWidth = lineWidth } } // 加上左右padding,转换为实际需要的宽度值 val desiredWidth = (maxLineWidth + paddingLeft + paddingRight).toInt() // 重新生成测量规格,强制TextView使用计算出的宽度 val newWidthSpec = MeasureSpec.makeMeasureSpec(desiredWidth, MeasureSpec.EXACTLY) // 再次调用测量,应用新的宽度 super.onMeasure(newWidthSpec, heightMeasureSpec) } } }
布局中使用
在你的XML布局里替换原来的TextView,同时记得设置maxLines="2"(确保文本只会拆成两行)和breakStrategy="balanced":
<com.your.package.name.BalancedWrapTextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:breakStrategy="balanced" android:maxLines="2" android:text="这里是你需要拆分的长文本内容" />
注意事项
- 必须设置
android:maxLines="2",否则balanced策略可能会根据文本长度拆分成更多行,影响最终宽度计算 - 如果文本本身很短不需要换行,这个自定义View会自动 fallback 到正常的
wrap_content行为 - 如果你使用的是Java项目,只需要把上述Kotlin代码转译为Java即可,核心逻辑(重新测量宽度)保持不变
内容的提问来源于stack exchange,提问作者JJAlexander
相关产品推荐
相关产品推荐

