1.原生方法
换行
Android textview换行属性有BreakStrategy和hyphenationFrequency
android:breakStrategy
Break strategy (control over paragraph layout).
Must be one of the following constant values.
Constant | Value | Description |
balanced | 2 | Line breaking strategy balances line lengths. |
high_quality | 1 | Line breaking uses high-quality strategy, including hyphenation. |
simple | 0 | Line breaking uses simple strategy. |
对于TextView默认值是high_quality,对于EditText默认值是simple
连字符
android:hyphenationFrequency
Frequency of automatic hyphenation.
Must be one of the following constant values.
Constant | Value | Description |
full | 2 | |
none | 0 | Standard amount of hyphenation, useful for running text and for screens with limited space for text. |
normal | 1 | Less frequent hyphenation, useful for informal use cases, such as chat messages. |
当breakStrategy的值不是simple时改策略的normal和full有效。
这两个属性是Android6.0引入的,实现在frameworks/minikin/libs/minikin目录下,java通过jni调用so库。但是按照icu的规范,
英文单词是尽可能的不中断的
,标点符号不会在行尾或者行首,所以即使设置high_quality和full的值,视觉上看起来也没啥大变化。
对齐
android:justificationMode
有两个值,
JUSTIFICATION_MODE_INTER_WORD
added in
API level 26
int JUSTIFICATION_MODE_INTER_WORD
Value for justification mode indicating the text is justified by stretching word spacing.
Constant Value: 1 (0x00000001)
JUSTIFICATION_MODE_NONE
added in
API level 26
int JUSTIFICATION_MODE_NONE
Value for justification mode indicating no justification.
Constant Value: 0 (0x00000000)
一个是不启用,另一个是启用
原理就是设置Paint的setWordSpacing方法调整单词的间距填满整行
/**
* Set the paint's word-spacing for text. The default value is 0.
* The value is in pixels (note the units are not the same as for
* letter-spacing).
*
*
@param wordSpacing
set the paint's word-spacing for drawing text.
*
@hide
*/
public void
setWordSpacing(
float
wordSpacing) {
nSetWordSpacing
(
mNativePaint
, wordSpacing);
}
这个也是网上大部分justify textview开源项目的思路,具体代码可见android.text.Layout的代码
注意的是这个属性不仅是Android8.0才引入的,而且对于EditText来说是无效的,因为EditText和TextView使用的是不同的Layout,EditText使用的是DynamicLayout,而TextView使用的是StaticLayout,当然可以通过反射的方式强制EditText开启justify模式,但是据我试验在引入斜体等会导致字体宽度变化的因素情况下layout会出现奇怪的现象,例如设置完justify模式后设置部分内容为斜体,layout会马上混乱。
2.其他方法
1.justify
我在网上找到的大部分方法都是justify的思路,例如
都要override OnDraw,通过调整间距使布局整齐。但是这种方法忽视了TextView中的其它逻辑,Android源码中的TextView一个文件就有8000+行代码,android.text包下的文件基本都是为TextView服务的,对于文本不变的TextVIew来说还可行,对于EditText来说行不通。
2.更改源码
有更改源码的能力的话,例如手机厂商,更改StaticLayout的代码即可。
3.反射的方式改源码
textview和android.text包下的hide类和方法非常多,从4.x到8.0变化也很多,例如linebreak的方法在5.0之前是java代码,之后就是jni了,而且jni的方法也一直在变,StaticLayout初期还是通过new来创建实例,后来就新加了Builder静态内部类,反射方式修改个人觉得不大可行。
4.把TextView源码整个挪动到app来
这个方法我试验过了,挪动的是4.4的代码,因为这个版本line break的逻辑在java代码中(比较好改),但是hide类和方法太多了,通过反射和删除的方式代码能编译调通,line break效果也有,但是可想而知的是bug会很多的。
4.4版本删除代码在StaticLayout.java
// From the Unicode Line Breaking Algorithm (at least approximately)
//delete by lgy
// boolean isLineBreak = isSpaceOrTab ||
// // / is class SY and - is class HY, except when followed by a digit
// ((c == CHAR_SLASH || c == CHAR_HYPHEN) &&
// (j + 1 >= spanEnd || !Character.isDigit(chs[j + 1 - paraStart]))) ||
// // Ideographs are class ID: breakpoints when adjacent, except for NS
// // (non-starters), which can be broken after but not before
// (c >= CHAR_FIRST_CJK && isIdeographic(c, true) &&
// j + 1 < spanEnd && isIdeographic(chs[j + 1 - paraStart], false));
//
// if (isLineBreak) {
// okWidth = w;
// ok = j + 1;
//
// if (fitTop < okTop)
// okTop = fitTop;
// if (fitAscent < okAscent)
// okAscent = fitAscent;
// if (fitDescent > okDescent)
// okDescent = fitDescent;
// if (fitBottom > okBottom)
// okBottom = fitBottom;
// }
最致命的是framework源码中不少地方是指定了类的,例如Span中有参数会指定是android.text.Layout,挪过来的Layout是无法使用的。输入法有相关参数是指定android.text.TextView的,挪过来的TextView它也不认啊。