最近开发中遇到了这样一个小需求:将 EditText
中的文字输出,并在自动折行的位置用换行符 \n
替换。
输入:
DevMcryYu
超爱 Android 的啦~超爱 Android 的啦~超爱 Android 的啦~
DevMcryYu 超爱 Android 的啦~超爱 Android 的啦~超爱 Android 的啦~超爱 Android 的啦~
屏幕上自动换行后:
期望输出:
DevMcryYu
超爱 Android 的啦~超爱 Android 的啦~超
爱 Android 的啦~
DevMcryYu 超爱 Android 的啦~超爱
Android 的啦~超爱 Android 的啦~超爱
Android 的啦~
看起来不太难的样子嘛
- 在输入监听里加判断逻辑?同时处理不同位置插入时的逻辑?No No No 太复杂了,不想做。
- 计算字符的宽度并累加,超出视图宽度时插入换行?
啪,字体换掉了,宽度一变换行位置都不一样了:
哦~要把不同字体的差异算进去啊,太麻烦了,不想做。
- 使用
Paint.breakText()
对字符串进行截取?emmmmmm,好像是条路子。不过还有没有更简单点的?几行代码就能搞定的? - 如果能获取到每个字符所在的行数就好了,这样一来判断相邻字符是否在同一行,如果不在同一行的话插入一个
\n
不就好了嘛~,别说,还真的有办法。
直接上代码:
editText.text?.toString()?.let { text ->
editText.layout?.let { layout ->
val builder = StringBuilder(text)
var addOffset = 0
for (i in 0 until text.lastIndex) {
val lineOfText = layout.getLineForOffset(i)
val lineOfNextText = layout.getLineForOffset(i + 1)
if (lineOfText != lineOfNextText) {
if (text[i].toString() != "\n") {
builder.insert(i + 1 + addOffset++, "\n")
}
}
}
builder.toString()
}
}
这里使用到了 android.text.Layout
中的 getLineForOffset(int offset)
方法,实现很好理解:
/**
* Get the line number on which the specified text offset appears.
* If you ask for a position before 0, you get 0; if you ask for a position
* beyond the end of the text, you get the last line.
*/
public int getLineForOffset(int offset) {
int high = getLineCount(), low = -1, guess;
while (high - low > 1) {
guess = (high + low) / 2;
if (getLineStart(guess) > offset)
high = guess;
else
low = guess;
}
if (low < 0) {
return 0;
} else {
return low;
}
}
总结
作为一名开发者,更多时候应该深入挖掘源码,最次也要做到和各个 Api 混个脸熟,这样等到需要的时候才不至于像个无头苍蝇一般,才可以很快速的找到解决方案, android.text.Layout
还有着很多方法,后续我也会花时间浏览一遍。