You can use this.
final EditText edit = (EditText)findViewById(R.id.MsgText);
final int maxLineLength = 10;
edit.addTextChangedListener(new TextWatcher() {
final Integer mark = 1;
String textBeforeEdit = null;
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
textBeforeEdit = s.toString().substring(start, start + count);
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
edit.getText().setSpan(mark, start, start + count, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
}
@Override
public void afterTextChanged(Editable s) {
String str = s.toString();
int spanStart = s.getSpanStart(mark);
int spanEnd = s.getSpanEnd(mark);
int lastNL = str.lastIndexOf('\n', spanStart);
int nextNL;
while(lastNL < spanEnd) {
nextNL = str.indexOf('\n', lastNL + 1);
if(nextNL == -1)
nextNL = str.length();
if(nextNL - lastNL > maxLineLength + 1) {
// reject the entire change
s.replace(spanStart, spanEnd, textBeforeEdit);
break;
}
lastNL = nextNL;
}
s.removeSpan(mark);
}
});
What the code does is that it watches, at each change to the text (which may equally be keyboard input or paste), whether any affected line has grown to more than maxLineLength
characters (plus one for the \n
). We don't care about lines before or after the change, so we can start counting at the last \n
immediately preceding the start of the region that was rewritten. (If the lastIndexOf
gives -1
that's fine, too.) We find the next \n
, if it's no more than maxLineLength + 1
characters past the last, that's fine and we advance until after the span (or at the end of the string).
Things get interesting when the condition is ever broken: for this reason we store an invisible mark at the beginning and at the end of the region that has been changed (relative to the start of the new text) as well as the original text that was rewritten. By replacing the new region by the old contents we effectively reject the change, so the action will be ignored.
What's good about this approach is that
- it works smoothly with insertions as well with Paste,
- it won't allow you to merge lines (using backspace) if the result would be too long,
- it never changes any text you were not meaning to change (like simply shortening or breaking the line to 10 chars would),
- it gives an expected user behaviour without flicker. You press a key that wouldn't fit – nothing happens.