WedX - журнал о программировании и компьютерных науках

Java Swing StyledDocument получает дерево элементов/стиль SelectedText, например полужирный, курсив

Привет, у меня есть несколько вопросов/проблем.

Мне нужно создать небольшую программу для редактирования текста. (Выбранный) текст должен быть стилем. Жирный, курсив, подчеркнутый, выравнивание по центру справа-слева. Это прекрасно работает. Я использовал определенные действия StyleEditorKit.

Моя проблема в том, что эти действия запускаются с помощью кнопок на панели jtoolbar и jmenuitems в jmenu/jmenubar.

Таким образом, есть два элемента click для выделения текста жирным шрифтом, два элемента для выделения текста курсивом и так далее. Если щелкнуть один элемент (например, кнопку на панели инструментов), элемент jmenui также должен быть выбран/активирован. Но как я могу это осознать?

Моя идея состоит в том, чтобы проверить выделенный текст (реализован CaretListener). Если текст выделен жирным шрифтом => установите кнопку и пункт меню активными. Но как я могу получить, если selectedText выделен жирным/курсивом и т. д.?

Я думаю, что для этого есть дерево StyledDocument с листьями. Но как я могу получить это дерево? как я могу получить листья?

Это мои первые шаги:

jTextPane1.addCaretListener(new CaretListener() {

    @Override
    public void caretUpdate(CaretEvent e) {
        Highlight[] h = jTextPane1.getHighlighter().getHighlights();
        for(int i = 0; i < h.length; i++) {
            System.out.println(h[i].getStartOffset());
            System.out.println(h[i].getEndOffset());
            String selectedText = jTextPane1.getSelectedText();


            StyledDocument styleddoc = (StyledDocument) jTextPane1.getDocument();

            System.out.println(styleddoc);

        }

    }
});

Но я получаю только javax.swing.text.DefaultStyledDocument@5098cb76

Как я могу перебрать дерево и получить элементы листьев/жирным или курсивом?

Спасибо


Ответы:


1

как я могу получить, если selectedText выделен жирным шрифтом/курсивом и т. д.?

В вашем CaretListener вы можете использовать:

AttributeSet attributes = jTextPane1.getCharacterAttributes();
System.out.println( attributes.containsAttribute(StyleConstants.Bold, Boolean.TRUE) );

Если щелкнуть один элемент (например, кнопку на панели инструментов), элемент jmenui также должен быть выбран/активирован.

Вы должны использовать Action:

Action bold = new StyledEditorKit.BoldAction();
JButton boldButton = new JButton( bold );
JCheckBoxMenuItem boldMenuItem = new JCheckBoxMenuItem( bold );

Когда вы устанавливаете состояние Action, состояние всех компонентов, использующих Action, изменяется. Например, вы можете сделать Action выбранным или активным.

11.05.2016
  • Спасибо. С атрибутами работает. Но если я выберу весь текст, он вернет false... если я выберу все без последнего символа, он вернет true... Я использовал BoldAction. Нужно ли устанавливать кнопку в положение «выбрано» или «включено»? 11.05.2016
  • System.out.println(attributes.containsAttribute(StyleConstants.Italic, Boolean.TRUE)); он возвращает false каждый раз... 11.05.2016
  • But if i select my whole text it returns false... — я думаю, вы можете перебирать каждый отдельный символ выделенного текста. Все символы должны быть жирными. Ну, если он не включен, то вы не сможете нажать кнопку. Вы не делаете кнопку включенной или выбранной. Вы меняете Действие. 11.05.2016
  • Он печатает правильное значение для меня. Опубликуйте свой SSCCE, демонстрирующий проблему. То есть создайте простое приложение с JFrame и JTextPane, кнопкой Bold и CaretListener. Когда курсор перемещается по отдельным символам, вывод будет меняться. 11.05.2016
  • @TorbenG, it returns false if the caret ends up after the last character. Верно, потому что курсор предназначен для следующего символа, а не для предыдущего символа. Как я уже несколько раз предлагал, теперь нужно проверять каждый символ в выделенном тексте по отдельности! Поэтому вам нужно получить информацию из документа. Сначала вы получаете Элемент для конкретного персонажа, затем вы получаете Атрибуты от Элемента. 11.05.2016

  • 2

    мой код сейчас выглядит так

    public void jToolBarInitButtons() {
    
        jTextPane1.addCaretListener(new CaretListener() {
    
        @Override
        public void caretUpdate(CaretEvent e) {
    
            StyledDocument styleddoc = (StyledDocument) jTextPane1.getDocument();
    
                AttributeSet attributes = jTextPane1.getCharacterAttributes();
                System.out.println("bold " +  attributes.containsAttribute(StyleConstants.Bold, Boolean.TRUE));
                System.out.println("italic " + attributes.containsAttribute(StyleConstants.Italic, Boolean.TRUE));
    
        }
    
    11.05.2016
  • Это не SSCCE. Я не могу скомпилировать этот код. Здесь нет рамки, текстового поля или кнопки. Поэтому я не могу проверить это, чтобы увидеть, есть ли у меня проблемы или нет. 11.05.2016

  • 3

    Таким образом, есть два элемента click для выделения текста жирным шрифтом, два элемента для выделения текста курсивом и так далее. Если щелкнуть один элемент (например, кнопку на панели инструментов), элемент jmenui также должен быть выбран/активирован. Но как я могу это осознать?

    Создайте пункт меню и кнопку, передав один и тот же Действие к их конструкторам.

    Похоже, вы намереваетесь показать полужирным шрифтом состояние кнопки и пункта меню, поэтому вы, вероятно, захотите создать JCheckBoxMenuItem и JToggleButton. При создании действия установите его SELECTED_KEY в ненулевое значение, поэтому кнопки будут поддерживать его в актуальном состоянии:

    action.putValue(Action.SELECTED_KEY, false);
    

    Из документации по действию:

    SELECTED_KEY

    Компоненты, которые учитывают это свойство, используют значение только в том случае, если оно не равно null. Например, если вы установите Action, который имеет нулевое значение для SELECTED_KEY на JToggleButton, JToggleButton никоим образом не обновит свое выбранное состояние. Точно так же каждый раз, когда изменяется выбранное состояние JToggleButton, оно будет возвращать значение Action только в том случае, если Action имеет значение, отличное от null, для SELECTED_KEY. Компоненты, которые учитывают это свойство, синхронизируют свое выбранное состояние с этим свойством. Когда один и тот же Action используется с несколькими компонентами, все компоненты синхронизируют свое выбранное состояние с этим свойством.

    Ваш CaretListener должен соответствовать camickr.

    11.05.2016
  • Это мой код, но, например. BoldAction не имеет метода putValue.. Действие boldAction = new StyledEditorKit.BoldAction(); Действие italicAction = новый StyledEditorKit.ItalicAction(); Действие underlineAction = новый StyledEditorKit.UnderlineAction(); 11.05.2016
  • @TorbenG Вам нужно будет вызывать putValue(Action.SELECTED_KEY, false) для каждого из этих действий, если вы собираетесь использовать каждое из них как в кнопке, так и в пункте меню. 11.05.2016
  • может быть, я слишком глуп, но у меня это не работает... boldAction.putValue(Action.SELECTED_KEY, false); 11.05.2016
  • Вы передали один и тот же жирный шрифт как в конструкторе вашей «жирной» кнопки, так и в конструкторе вашего «жирного» пункта меню? 11.05.2016
  • JCheckBoxMenuItem jCheckBoxBold = новый JCheckBoxMenuItem(boldAction); buttonBold = новая кнопка JButton (полужирное действие); 11.05.2016
  • @TorbenG JButton не имеет постоянного выбранного состояния. Вместо этого используйте JToggleButton. 11.05.2016

  • 4

    Извиняюсь. Это весь код:

    Обновление 2: вот код

    public class TestFrame extends javax.swing.JFrame {
    
        /**
         * Creates new form TestFrame
         */
        public TestFrame() {
            initComponents();
            initButtons();
        }
    
        Action boldAction = new StyledEditorKit.BoldAction();    
        Action italicAction = new StyledEditorKit.ItalicAction();
        Action underlineAction = new StyledEditorKit.UnderlineAction();
    
        public void initButtons() {
            JButton boldButton = new JButton(boldAction);
            boldButton.setText("bold");
    
            JButton italicButton = new JButton(italicAction);
            boldButton.setText("italic");
    
            JButton underlineButton = new JButton(underlineAction);
            boldButton.setText("underline");
    
            jToolBar1.add(boldButton);
            jToolBar1.add(italicButton);
            jToolBar1.add(underlineButton);
    
            jTextPane1.addCaretListener(new CaretListener() {
    
            @Override
            public void caretUpdate(CaretEvent e) {
                Highlighter.Highlight[] h = jTextPane1.getHighlighter().getHighlights();
                for(int i = 0; i < h.length; i++) {
                    System.out.println("length " +  h.length);
                    System.out.println(h[i].getStartOffset());
                    System.out.println(h[i].getEndOffset());
                    String selectedText = jTextPane1.getSelectedText();
    
    
                    StyledDocument styleddoc = (StyledDocument) jTextPane1.getDocument();
    
                    AttributeSet attributes = jTextPane1.getCharacterAttributes();
                    System.out.println("Bold " + attributes.containsAttribute(StyleConstants.Bold, Boolean.TRUE));
                    System.out.println("Italic " + attributes.containsAttribute("Italic " + StyleConstants.Italic, Boolean.TRUE));
                    System.out.println("Underline " + attributes.containsAttribute(StyleConstants.Underline, Boolean.TRUE));
                }
    
            }
        });
        }
    
        /**
         * This method is called from within the constructor to initialize the form.
         * WARNING: Do NOT modify this code. The content of this method is always
         * regenerated by the Form Editor.
         */
        @SuppressWarnings("unchecked")
        // <editor-fold defaultstate="collapsed" desc="Generated Code">                          
        private void initComponents() {
    
            jScrollPane1 = new javax.swing.JScrollPane();
            jTextPane1 = new javax.swing.JTextPane();
            jToolBar1 = new javax.swing.JToolBar();
    
            setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
    
            jScrollPane1.setViewportView(jTextPane1);
    
            jToolBar1.setRollover(true);
    
            javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
            getContentPane().setLayout(layout);
            layout.setHorizontalGroup(
                layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                    .addContainerGap(116, Short.MAX_VALUE)
                    .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 269, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addGap(75, 75, 75))
                .addGroup(layout.createSequentialGroup()
                    .addContainerGap()
                    .addComponent(jToolBar1, javax.swing.GroupLayout.PREFERRED_SIZE, 192, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
            );
            layout.setVerticalGroup(
                layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addGroup(layout.createSequentialGroup()
                    .addGap(23, 23, 23)
                    .addComponent(jToolBar1, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addGap(14, 14, 14)
                    .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 154, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addContainerGap(89, Short.MAX_VALUE))
            );
    
            pack();
        }// </editor-fold>                        
    
        /**
         * @param args the command line arguments
         */
        public static void main(String args[]) {
            /* Set the Nimbus look and feel */
            //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
            /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
             * For details see https://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 
             */
            try {
                for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                    if ("Nimbus".equals(info.getName())) {
                        javax.swing.UIManager.setLookAndFeel(info.getClassName());
                        break;
                    }
                }
            } catch (ClassNotFoundException ex) {
                java.util.logging.Logger.getLogger(TestFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
            } catch (InstantiationException ex) {
                java.util.logging.Logger.getLogger(TestFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
            } catch (IllegalAccessException ex) {
                java.util.logging.Logger.getLogger(TestFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
            } catch (javax.swing.UnsupportedLookAndFeelException ex) {
                java.util.logging.Logger.getLogger(TestFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
            }
            //</editor-fold>
    
            /* Create and display the form */
            java.awt.EventQueue.invokeLater(new Runnable() {
                public void run() {
                    new TestFrame().setVisible(true);
                }
            });
        }
    
        // Variables declaration - do not modify                     
        private javax.swing.JScrollPane jScrollPane1;
        private javax.swing.JTextPane jTextPane1;
        private javax.swing.JToolBar jToolBar1;
        // End of variables declaration                   
    }
    
    11.05.2016
  • Что вы ожидаете? Код, который я вам дал, смотрит только на конечную позицию каретки. Поэтому, если выделение заканчивается полужирным текстом, вы получаете значение true, в противном случае — значение false. Я предложил вам создать метод для проверки каждого символа в выделенном тексте. Как только вы найдете символ, выделенный жирным шрифтом, вы вернете false. Если вы дойдете до конца строки, вы вернете true. 11.05.2016
  • моя проблема в том, что он возвращает false, если каретка заканчивается после последнего символа. Если у меня есть жирный привет, я выбрал все и остановился после «о», он возвращает ложь ... неправда. Но все, весь привет жирный. 11.05.2016
  • Новые материалы

    Я хотел выучить язык программирования MVC4, но не мог выучить его раньше, потому что это выглядит сложно…
    Просто начните и учитесь самостоятельно Я хотел выучить язык программирования MVC4, но не мог выучить его раньше, потому что он кажется мне сложным, и я бросил его. Это в основном инструмент..

    Лицензии с открытым исходным кодом: руководство для разработчиков и создателей
    В динамичном мире разработки программного обеспечения открытый исходный код стал мощной парадигмой, способствующей сотрудничеству, инновациям и прогрессу, движимому сообществом. В основе..

    Объяснение документов 02: BERT
    BERT представил двухступенчатую структуру обучения: предварительное обучение и тонкая настройка. Во время предварительного обучения модель обучается на неразмеченных данных с помощью..

    Как проанализировать работу вашего классификатора?
    Не всегда просто знать, какие показатели использовать С развитием глубокого обучения все больше и больше людей учатся обучать свой первый классификатор. Но как только вы закончите..

    Работа с цепями Маркова, часть 4 (Машинное обучение)
    Нелинейные цепи Маркова с агрегатором и их приложения (arXiv) Автор : Бар Лайт Аннотация: Изучаются свойства подкласса случайных процессов, называемых дискретными нелинейными цепями Маркова..

    Crazy Laravel Livewire упростил мне создание электронной коммерции (панель администратора и API) [Часть 3]
    Как вы сегодня, ребята? В этой части мы создадим CRUD для данных о продукте. Думаю, в этой части я не буду слишком много делиться теорией, но чаще буду делиться своим кодом. Потому что..

    Использование машинного обучения и Python для классификации 1000 сезонов новичков MLB Hitter
    Чему может научиться машина, глядя на сезоны новичков 1000 игроков MLB? Это то, что исследует это приложение. В этом процессе мы будем использовать неконтролируемое обучение, чтобы..


    Для любых предложений по сайту: [email protected]