Adding a line break in a specific place, only if necessary

In my Android layout, I have a TextView that uses half the available screen width. At runtime, I set the text to a long email address. For example:

googleandroiddeveloper@gmail.com 

If the text does not fit on one line, Android inserts a line break, which is the desired behavior. However, the line break position is before the first character that does not match the line. The result could be something like this:

 googleandroiddeveloper@gmai l.com 

I think this is disgusting, especially in email addresses. I want the line break to appear right before the @ character:

 googleandroiddeveloper @gmail.com 

Of course, I could add \n to my strings.xml . But then the email address will use two lines in each case, even if it fits on one line.

I already thought I found a solution when adding ZERO WIDTH SPACE ( \u200B ) to an email address.

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

But, in addition to standard spaces, Android does not detect a special space character as blurry space and, therefore, does not add a line break at this moment.

Since I am dealing with a large number of email addresses in several places of my application, I am looking for a solution to add discontinuous and invisible space before the @ symbol, so that Android wraps the email address if it does not fit on one line.

+7
source share
2 answers

@Luksprog's solution is very good and solves the problem in many cases. However, I changed the class at several points to make it even better. These are the modifications:

  • I used onSizeChanged instead of onMeasure to check and process the text, because when using LinearLayout with layout_weight there are problems with onMeasure .
  • I looked at horizontal text padding with getPaddingLeft() and getPaddingRight()
  • When measuring afterAt I replaced position with position + 1 , otherwise the received email address contains two @ .

the code:

 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); } } } } } 

Here is a screenshot of EmailTextView compared to the default TextView :

EmailTextView

For all email addresses, it works as I expected. The last address does not change, because the text before @ already too wide, so the system breaks it earlier and, thus, the email address is kind of messed up, so there is no need to include another line break.

+5
source

You can take a look at the custom TextView class below (although probably not very efficient), which should insert (with very few tests) the desired line break in some cases:

 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); } } } } } 
+2
source

All Articles