关于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,大功告成了,虽然没写多少内容,但找出问题和解决问题的过程,还是花了我一些时间的,有点啰嗦,就不写了

posted on 2012-11-30 11:31  trytocatch  阅读(9036)  评论(0编辑  收藏  举报