Adding a line break at a particular position, only if necessary
android, android-layout, android-xml, java
Solution
@Luksprog's solution is very good and solves the problem in many cases. However, I modified the class at several points, to make it even better. These are the modifications:
- I used `onSizeChanged` instead of `onMeasure` for checking and manipulating the text, because there are problems with `onMeasure` when using `LinearLayout` with `layout_weight`.
- I considered the horizontal padding of the text by using `getPaddingLeft()` and `getPaddingRight()`
- At measuring `afterAt` I replaced `position` with `position + 1`, otherwise the resulting email address contains two `@`.
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 default `TextView`:
For all email addresses it works as I expected. The last address is not changed because the text before the `@` is already too wide, so the system breaks it before and the thereby the email address is kind of messed up anyway, so there is no need to include another line break.
Problem
In my Android layout, I have a TextView that uses half of the available width of the screen. At runtime I set the text to a (long) email address. For instance: ``` googleandroiddeveloper@gmail.com ``` If the text does not fit into one line, Android inserts a line break, which is the desired behavior. However, the position of the line break is before the first character that does not fit in the line. The result can be something like this: ``` googleandroiddeveloper@gmai l.com ``` I think, this is kind of ugly, especially in email addresses. I want the line break to appear right before the `@` character: ``` googleandroiddeveloper @gmail.com ``` Of course, i could add a `\n` in my `strings.xml`. But then the email address would use two lines in every case, even if it would fit into one line. I already thought I had found a solution in adding a ZERO WIDTH SPACE (`\u200B`) to the email address. ``` <string name="email">googleandroiddeveloper\u200B@gmail.com</string> ``` But other than with standard spaces, Android does not detect the special space character as a breakable space and consequentially does not add a line break at this point. As I am dealing with a lot of email addresses in multiple places of my application, I am searching for a solution to add a breakable and invisible space before the `@` character, so Android wraps the email address if does not fit into one line.