Android TextView 中英文数字排版的一种简单优化办法

现象

  原生的TextView 在内容是中英文数字混合的情况下,换行后会有留白,像这样:

  可以看到文本右侧的空白,原因是TextView换行后依然使用了原内容的最大宽度(我设置了TextView的maxWidth)
在这里插入图片描述

一种简单优化办法

  既然问题是宽度引起的,那么在onMeasure里手动计算下TextView的最大宽度。

//自定义View继承自AppCompatTextView
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        Layout layout = getLayout();
        if (layout == null || layout.getLineCount() <= 1) {
            // 只有一行无需处理
            return;
        }
        // 最大宽度
        int maxWidth = 0;
        int totalHeight = 0;
        for (int i = layout.getLineCount() - 1; i >= 0; --i) {
            int lineStart = layout.getLineStart(i);
            int lineEnd = layout.getLineEnd(i);
            // 获取每行文字的宽度
            float lineWidth = layout.getPaint().measureText(getText(), lineStart, lineEnd);
            // 获取TextView最大宽度
            maxWidth = Math.max(maxWidth, Math.round(lineWidth));
            // 获取每行的高度和行间距
            totalHeight += layout.getLineBottom(i) - layout.getLineTop(i);
        }
        // 避免宽度超出父视图的限制
        int desiredWidth = Math.min(maxWidth + getPaddingLeft() + getPaddingRight(), MeasureSpec.getSize(widthMeasureSpec));
        int desiredHeight = totalHeight + getPaddingTop() + getPaddingBottom();
        // 设置测量结果 明确告诉系统这个视图的宽度和高度是多少。系统会根据这些尺寸进行布局。
        setMeasuredDimension(desiredWidth, desiredHeight);
    }

  显示如下

特殊情况,当文本有样式SpannableString的时候,宽度会有问题:

其原因是有样式时,还需要加上左边缘样式的宽度,否则会导致宽度测量不准确

// 获取行内缩进
int paragraphLeft = getLayout().getParagraphLeft(i);
// 获取每行文字的宽度
float lineWidth = layout.getPaint().measureText(getText(), lineStart, lineEnd) + paragraphLeft;

最后附上这种简单优化的代码

/**
 * @author r3562939
 * @date 2024/12/5
 */
public class SimpleLayoutTextView extends AppCompatTextView {

    public SimpleLayoutTextView(Context context) {
        super(context);
    }

    public SimpleLayoutTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public SimpleLayoutTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        Layout layout = getLayout();
        if (layout == null || layout.getLineCount() <= 1) {
            // 只有一行无需处理
            return;
        }
        int maxWidth = 0;
        int totalHeight = 0;
        // 输出最大宽度
        for (int i = layout.getLineCount() - 1; i >= 0; --i) {
            int lineStart = layout.getLineStart(i);
            int lineEnd = layout.getLineEnd(i);
            // 获取行内缩进
            int paragraphLeft = getLayout().getParagraphLeft(i);
            // 获取每行文字的宽度
            float lineWidth = layout.getPaint().measureText(getText(), lineStart, lineEnd) + paragraphLeft;
            // 获取TextView最大宽度
            maxWidth = Math.max(maxWidth, Math.round(lineWidth));
            // 获取每行的高度和行间距
            totalHeight += layout.getLineBottom(i) - layout.getLineTop(i);
        }
        // 避免宽度超出父视图的限制
        int desiredWidth = Math.min(maxWidth + getPaddingLeft() + getPaddingRight(), MeasureSpec.getSize(widthMeasureSpec));
        int desiredHeight = totalHeight + getPaddingTop() + getPaddingBottom();
        // 设置测量结果 明确告诉系统这个视图的宽度和高度是多少。系统会根据这些尺寸进行布局。
        setMeasuredDimension(desiredWidth, desiredHeight);
    }
}
注:图片中文本引用的样式来自于Conversations 一个优秀的Xmpp Android客户端
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值