Android EditText 換行和對齊問題研究
阿新 • • 發佈:2019-01-23
1.原生方法換行Android textview換行屬性有BreakStrategy和hyphenationFrequencyandroid:breakStrategyBreak strategy (control over paragraph layout).Must be one of the following constant values.
對於TextView預設值是high_quality,對於EditText預設值是simple連字元android:hyphenationFrequencyFrequency of automatic hyphenation.Must be one of the following constant values.
當breakStrategy的值不是simple時改策略的normal和full有效。這兩個屬性是Android6.0引入的,實現在frameworks/minikin/libs/minikin目錄下,java通過jni呼叫so庫。但是按照icu的規範,英文單詞是儘可能的不中斷的,標點符號不會在行尾或者行首,所以即使設定high_quality和full的值,視覺上看起來也沒啥大變化。對齊android:justificationMode 有兩個值,JUSTIFICATION_MODE_INTER_WORDint JUSTIFICATION_MODE_INTER_WORDValue for justification mode indicating the text is justified by stretching word spacing.Constant Value: 1 (0x00000001)JUSTIFICATION_MODE_NONEint JUSTIFICATION_MODE_NONEValue 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會很多的。
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. |
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. |
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它也不認啊。