1. 程式人生 > >Android EditText 換行和對齊問題研究

Android EditText 換行和對齊問題研究

1.原生方法換行Android textview換行屬性有BreakStrategy和hyphenationFrequencyandroid:breakStrategyBreak strategy (control over paragraph layout).Must be one of the following constant values.
ConstantValueDescription
balanced2Line breaking strategy balances line lengths.
high_quality1Line breaking uses high-quality strategy, including hyphenation.
simple0Line breaking uses simple strategy.
對於TextView預設值是high_quality,對於EditText預設值是simple連字元android:hyphenationFrequencyFrequency of automatic hyphenation.Must be one of the following constant values.
ConstantValueDescription
full2
none0Standard amount of hyphenation, useful for running text and for screens with limited space for text.
normal1Less 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_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會很多的。

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