只在必要时才在特定位置添加换行符

在我的Android布局,我有一个TextView,使用一半的可用宽度的屏幕。 在运行时,我将文本设置为(长)电子邮件地址。 例如:

googleandroiddeveloper@gmail.com 

如果文本不符合一行,Android将插入换行符,这是所需的行为。 但是,换行符的位置在第一个不符合该行的字符之前。 结果可能是这样的:

 googleandroiddeveloper@gmai l.com 

我认为,这是一种丑陋,特别是在电子邮件地址。 我希望换行符出现在@字符之前:

 googleandroiddeveloper @gmail.com 

当然,我可以在我的strings.xml添加一个\n 。 但是,那么电子邮件地址在每一种情况下都会使用两行,即使它适合一行。

我以为我已经find了在电子邮件地址中添加一个零宽度空间( \u200B )的解决scheme。

 <string name="email">googleandroiddeveloper\u200B@gmail.com</string> 

但除了使用标准空格,Android不会将特殊空格字符检测为易碎空间,因此此时不会添加换行符。

由于我在应用程序的多个地方处理了许多电子邮件地址,因此我正在寻找一种解决scheme,在@字符之前添加易碎和不可见的空间,所以如果不适合一行,则Android会封装电子邮件地址。

Solutions Collecting From Web of "只在必要时才在特定位置添加换行符"

@ Luksprog的解决scheme非常好,在很多情况下解决了这个问题。 不过,我在几个地方修改了课程,使其更好。 这些是修改:

  • 我使用onSizeChanged而不是onMeasure来检查和操作文本,因为在使用LinearLayoutlayout_weight时, onMeasure存在问题。
  • 我通过使用getPaddingLeft()getPaddingRight()来考虑文本的水平填充
  • 在测量afterAt我用position + 1replaceposition ,否则产生的电子邮件地址包含两个@

码:

 public class EmailTextView extends TextView { public EmailTextView(Context context, AttributeSet attrs) { super(context, attrs); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); // the width the text can use, that is the total width of the view minus // the padding int availableWidth = w - getPaddingLeft() - getPaddingRight(); String text = getText().toString(); if (text.contains("\n@")) { // the text already contains a line break before @ return; } // get the position of @ in the string int position = -1; for (int i = 0; i < text.length(); i++) { if (text.charAt(i) == '@') { position = i; break; } } if (position > 0) { final Paint pnt = getPaint(); // measure width before the @ and after the @ String beforeAt = text.subSequence(0, position).toString(); String afterAt = text.subSequence(position + 1, text.length()) .toString(); final float beforeAtSize = pnt.measureText(beforeAt); final float afterAtSize = pnt.measureText(afterAt); final float atSize = pnt.measureText("@"); if (beforeAtSize > availableWidth) { // the text before the @ is bigger than the width // so Android will break it return; } else { if ((beforeAtSize + afterAtSize + atSize) <= availableWidth) { // the entire text is smaller than the available width return; } else { // insert the line break before the @ setText(beforeAt + "\n@" + afterAt); } } } } } 

这里是EmailTextView与默认TextView的截图:

EmailTextView

对于所有电子邮件地址,它按照我的预期工作 最后一个地址没有改变,因为@之前的文本已经太宽了,所以系统之前就把它打破了,因此电子邮件地址总是搞砸了,所以不需要再包含换行符。

您可以看看下面的自定义TextView类(尽pipe可能不是非常高效):在某些情况下,谁应该插入(假设在很less的testing中)所需的换行符:

 public static class NewLineText extends TextView { private static final String CHALLANGE_TEXT = "\n@"; public NewLineText(Context context, AttributeSet attrs) { super(context, attrs); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); String text = getText().toString(); if (text.contains(CHALLANGE_TEXT)) { return; } int position = -1; for (int i = 0; i < text.length(); i++) { if (text.charAt(i) == '@') { position = i; break; } } if (position > 0) { final Paint pnt = getPaint(); String beforeAt = text.subSequence(0, position).toString(); String afterAt = text.subSequence(position, text.length()) .toString(); final float beforeAtSize = pnt.measureText(beforeAt); final float afterAtSize = pnt.measureText(afterAt); final float atSize = pnt.measureText("@"); if (beforeAtSize > getMeasuredWidth()) { // return ?! the text before the @ is bigger than the width // so Android will break it return; } else { if ((beforeAtSize + afterAtSize + atSize) <= getMeasuredWidth()) { return; } else { setText(beforeAt + CHALLANGE_TEXT + afterAt); } } } } }