Как выделить текст в JTextPane с помощью setFocusable (false)?

Итак, у меня есть JTextPane, который в основном служит консолью. У меня это в центре поля JFrame и JTextField в южном поле. JTextField возьмет текст, который у него есть, и добавит его к JTextPane, когда пользователь нажмет Enter. Чтобы сделать JTextPane недоступным для редактирования пользователем, мне пришлось setFocusable(false), потому что использование setEditable(false) остановило появление любого текста на JTextPane. Но хотя я не хочу, чтобы пользователь редактировал панель, я все же хотел бы, чтобы пользователь мог выделять текст на панели, но я не могу найти способ сделать это.

Ниже приведен пример, демонстрирующий, что я имею в виду

Образец

package resources;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.text.AttributeSet;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;

public class SampeTextPane extends JFrame
{
    public SampeTextPane()
    {
        setPreferredSize(new Dimension(350, 200));
        setDefaultCloseOperation(EXIT_ON_CLOSE);

        JTextPane display = new JTextPane();
        display.setBorder(new EmptyBorder(5, 5, 5, 5));
        display.setMargin(new Insets(5, 5, 5, 5));
        display.setFocusable(false);
        appendToPane(display, "Example", Color.BLUE);

        JTextField field = new JTextField();
        field.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                appendToPane(display, field.getText(), Color.BLACK);
                field.setText("");
            }
        });

        add(display, BorderLayout.CENTER);
        add(field, BorderLayout.SOUTH);

        pack();
        setVisible(true);
    }

    private void appendToPane(JTextPane pane, String text, Color color)
    {
        StyleContext sc = StyleContext.getDefaultStyleContext();
        AttributeSet aset = sc.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.Foreground, color);

        aset = sc.addAttribute(aset, StyleConstants.Alignment, StyleConstants.ALIGN_JUSTIFIED);

        int len = pane.getDocument().getLength();
        pane.setCaretPosition(len);
        pane.setCharacterAttributes(aset, false);
        pane.replaceSelection(text + "\n");
    }

    public static void main(String[] args)
    {
        new SampeTextPane();
    }
}

Заранее благодарю за любую помощь.


person Ryan    schedule 30.09.2016    source источник


Ответы (2)


использование setEditable(false) остановило появление любого текста в JTextPane.

Вы можете сделать JTextPane нередактируемым, но вы не можете обновить текст через текстовую панель.

Вместо этого вы можете обновить текст через Document:

//int len = pane.getDocument().getLength();
//pane.setCaretPosition(len);
//pane.setCharacterAttributes(aset, false);
//pane.replaceSelection(text + "\n");

try
{
    StyledDocument doc = pane.getStyledDocument();
    doc.insertString(doc.getLength(), text, aset);
}
catch(BadLocationException e) { System.out.println(e); }
person camickr    schedule 30.09.2016

Другой вариант, хотя и более запутанный, - использовать логический флаг, чтобы разрешить или запретить изменения в документе, что предложил StanislavL здесь .

В вашей ситуации это может выглядеть примерно так:

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.DocumentFilter;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;

@SuppressWarnings("serial")
public class SampeTextPane extends JFrame {
    private boolean isApi = false;

    public SampeTextPane() {
        setPreferredSize(new Dimension(350, 200));
        setDefaultCloseOperation(EXIT_ON_CLOSE);

        JTextPane display = new JTextPane();

        ((DefaultStyledDocument) display.getDocument()).setDocumentFilter(new DocFilter());

        display.setBorder(new EmptyBorder(5, 5, 5, 5));
        display.setMargin(new Insets(5, 5, 5, 5));
        // !! display.setFocusable(false);
        appendToPane(display, "Example", Color.BLUE);

        JTextField field = new JTextField();
        field.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                appendToPane(display, field.getText(), Color.BLACK);
                field.setText("");
            }
        });

        add(display, BorderLayout.CENTER);
        add(field, BorderLayout.SOUTH);

        pack();
        setVisible(true);
    }

    private void appendToPane(JTextPane pane, String text, Color color) {
        isApi = true;

        StyleContext sc = StyleContext.getDefaultStyleContext();
        AttributeSet aset = sc.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.Foreground,
                color);

        aset = sc.addAttribute(aset, StyleConstants.Alignment, StyleConstants.ALIGN_JUSTIFIED);
        int len = pane.getDocument().getLength();

        String selection = pane.getSelectedText();
        if (selection == null) {
            pane.setCaretPosition(len);
            text += "\n";
        }        
        pane.setCharacterAttributes(aset, false);
        pane.replaceSelection(text);

        isApi = false;
    }

    private class DocFilter extends DocumentFilter {
        @Override
        public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr)
                throws BadLocationException {
            if (isApi) {
                super.insertString(fb, offset, string, attr);
            }
        }

        @Override
        public void remove(FilterBypass fb, int offset, int length) throws BadLocationException {
            if (isApi) {
                super.remove(fb, offset, length);
            }
        }

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

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new SampeTextPane());
    }

}
person Hovercraft Full Of Eels    schedule 30.09.2016
comment
Интересный способ сделать это, и, насколько я вижу, он делает примерно то же самое, но немного превышает мои знания Java. Спасибо хоть! - person Ryan; 30.09.2016
comment
@Ryan: да, предложение Camickr намного чище, и я рад, что вы согласились с ним. Я создавал свой код до того, как он опубликовал свой правильный ответ, и поэтому я ненавидел не публиковать свои усилия. Конечно, я проголосовал за его ответ. - person Hovercraft Full Of Eels; 30.09.2016