メモの日々


2013年10月03日(木) [長年日記]

[java] java.swing.JTextAreaの文字数を制限する

JTextAreaに入力できる文字数を制限したいが簡単にはできないみたい。文字数を制限するDocumentFilterを作ったらうまくいったようなのでメモ。

(追記)

あー、制限を超えて日本語を入れようとすると変な動作になってしまう。文字変換のときにDocumentから文字が削除されてしまうからみたいだけど、よくわからない。

(追記2)

コメントで対処方法を教えていただいたので、AttributeSetの内容をチェックするように変更してみました。これで漢字変換を行った場合でも問題なく動くようになったようです。

なお、「文字変換のときにDocumentから文字が削除されてしまう」という処理は、JTextComponent#replaceInputMethodText()内で行われているようでした。

import java.awt.BorderLayout;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DocumentFilter;
import javax.swing.text.PlainDocument;
import javax.swing.text.StyleConstants;

public class TextAreaSample {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                build();
            }
        });
    }

    static void build() {
        JFrame frame = new JFrame();
        frame.add(new JScrollPane(createTextArea()), BorderLayout.CENTER);
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.setSize(400, 300);
        frame.setVisible(true);
    }

    static JTextArea createTextArea() {
        PlainDocument document = new PlainDocument();
        document.setDocumentFilter(new LengthLimitter(10));
        JTextArea textArea = new JTextArea(document);
        return textArea;
    }

    static class LengthLimitter extends DocumentFilter {
        private final int limit;

        public LengthLimitter(int limit) {
            this.limit = limit;
        }

        @Override
        public void insertString(
                FilterBypass fb,
                int offset,
                String string,
                AttributeSet attr) throws BadLocationException {
            super.insertString(
                fb, offset, filterText(fb, string, limit, attr), attr);
        }

        @Override
        public void replace(
                FilterBypass fb,
                int offset,
                int length,
                String text,
                AttributeSet attrs) throws BadLocationException {
            super.replace(
                fb,
                offset,
                length,
                filterText(fb, text, limit + length, attrs),
                attrs);
        }

        static String filterText(
                FilterBypass fb, String text, int limit, AttributeSet attrs) {
            if (text == null) return text;
            if (attrs != null
                    && attrs.isDefined(StyleConstants.ComposedTextAttribute)) {
                return text;
            }

            int rest = limit - fb.getDocument().getLength();
            if (rest <= 0) return "";
            if (text.length() > rest) text = text.substring(0, rest);
            return text;
        }
    }
}
本日のツッコミ(全2件) [ツッコミを入れる]
java勉強中 (2013年10月30日(水) 08:15)

参考にさせていただきました。ありがとうございます。<br>日本語変換確定前はinsertStringでfilterTextを通さないと良さそうですが。<br>if ( attr != null <br> && attr.isDefined(StyleConstants.ComposedTextAttribute)){<br> super.insertString(fb, offset, str, attr);<br>} else {<br> super.insertString(fb, offset, filterText(fb, str, limit), attr);<br>}<br>参考にさせていただいた記事:d.hatena.ne.jp/Kazzz/20080121/p1

小川 (2013年10月30日(水) 14:34)

なるほど、AttributeSetをチェックする必要があるのですね。StyleConstants.ComposedTextAttributeが定義されているケースではフィルタリングをしないようにコードを変更してみました。これでおかしな動作にはならなくなったようです。<br><br>情報をありがとうございました!