Android EditText 换行和对齐问题研究

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它也不认啊。

猜你喜欢

转载自blog.csdn.net/firedancer0089/article/details/80081933
今日推荐