關於JTextField限制輸入字符長度的問題,因為沒提供現成的api,所以我們得自己動手,來實現這個功能,網上也有很多這樣的資料,大多是在JTextField的Document的insertString方法中動手腳,比較當前文本框的字符長度和最大長度,如果輸入不涉及中文,該方法也堪稱完美了。
細心的童鞋可能會發現,在swing里輸入中文,有的是下方彈出一個小方框,顯示當前輸入的字母;有的是直接將這些字母顯示在了文本輸入框里,打完一個字詞時,將這些字母清除掉,再將中文顯示上去。這兩種不同的顯示方式取決於System.getProperty("java.awt.im.style")的值是不是"below-the-spot",如果是,輸入框下方就顯示一個小窗口(如果為null好像也是這樣),這種情況下,上面的解決辦法也沒有問題;如果不是,即字母顯示在文本輸入框中,那么在輸入字符接近或達到最大長度時,會出現吃掉前面的字符的情況。
此問題的原因在於,在輸入中文時,將鍵入的字母(暫且稱之為“臨時字母”吧)打印在了輸入框內,假如是j,按第二個字母時(假如是i),會把之前的臨時字母(j)刪除,再用insertString插入ji,再輸入n時,把ji刪除,再插入jin,JTextField的公共方法是無法區分臨時字母的,如果插入ji時達到了長度上限,插入失敗,再輸入n,刪除ji,此時ji並未插入,所以就刪除了左邊的字符,此時就出現了吃掉左邊字符的問題。
接下來說解決辦法:
重寫Document里的方法
public void insertString(int offs, String str, AttributeSet a) throws BadLocationException { if (a == null) { int allowCount = maxLength - getLength(); if (allowCount > 0) { if (allowCount < str.length()) str = str.substring(0, allowCount); } else return; } super.insertString(offs, str, a); }
經調試跟蹤,插入臨時字母時AttributeSet a都不是null,而輸入英文或中文上屏時,a都為空,所以臨時字母能夠正常插入,刪除時,就不會拿正常輸入的字符當替死鬼了。
到此,問題已經基本解決了,只是到了長度上限,還能敲入臨時字母,雖然切換輸出法、失去焦點啥的,臨時字母會自動被刪除(所以說基本沒什么問題了,除非你不是根據鼠標鍵盤操作去getText,要不getText是不會獲取到臨時字母的),但如果有點完美主義的話,心里還是有點不舒服,為啥就不能讓它在達到最大長度時,連臨時字母也不能輸入了?
因為臨時字母的刪除再插入,在insertString、remove等處已經發生了,又不方便跟正常刪除插入進行區分,所以往上找,找到了輸入法事件處理的代碼,在此處就能比較根本地解決問題了
private int composedLen; @Override protected void processInputMethodEvent(InputMethodEvent e) { if (e.getID() == InputMethodEvent.INPUT_METHOD_TEXT_CHANGED) { if (e.getCommittedCharacterCount() == 0) { AttributedCharacterIterator aci = e.getText(); if (getDocument().getLength() - composedLen >= maxLength) { e.consume(); composedLen = 0; } else composedLen = aci != null ? aci.getEndIndex() - aci.getBeginIndex() : 0; } else composedLen = 0; } super.processInputMethodEvent(e); }
在JTextField里重寫processInputMethodEvent方法,並定義一個composedLen變量,用來記錄文本中的臨時字母的長度,文本長度減去臨時字母的長度,就得到了真正輸入的字符的長度了,如果這個長度大於等於上限,就消耗掉這個事件(e.consume()),讓它失效。介紹下里面的方法,e.getCommittedCharacterCount()得到的是提交的字符數,也就是中文上屏時,中文的字符個數,aci.getEndIndex() - aci.getBeginIndex()得到的是文本的長度,可能是上屏的中文,也可能是臨時字母。
如果你對我用insertString里的AttributeSet a是否為null作為判斷依據有所質疑的話(JDK升級后,這規則還有效嗎?),也可以把composedLen傳到insertString里,作為判斷依據。
OK,大功告成了,雖然沒寫多少內容,但找出問題和解決問題的過程,還是花了我一些時間的,有點啰嗦,就不寫了