Составной узел JTree, позволяющий событиям проходить к объектам под ними

Я пытаюсь создать JTree, в котором некоторые узлы являются составными объектами, содержащими JLabel и JButton. Узел представляет сервер и порт, показанный JLabel, JButton будет использовать Desktop API для открытия браузера по умолчанию и перехода к URL-адресу.

Я уже прочитал следующее и внимательно следил за ними. Узел отображается так, как я этого хочу (в основном - я смогу позже сделать его лучше), но когда я пытаюсь нажать на кнопку, JTree реагирует на события, а не на кнопку.

Java Swing: добавить пользовательскую графическую кнопку к элементу JTree

http://www.java2s.com/Code/Java/Swing-JFC/TreeCellRenderer.htm

/questions/27966946/java-swing-dobavit-polzovatelskuyu-graficheskuyu-knopku-k-elementu-jtree/27966960#27966960

Мне нужно знать, как позволить событиям проходить через JTree, чтобы они обрабатывались объектом (ами) под ним - JButton или JLabel.

Вот мой TreeCellEditor:

public class UrlValidationCellEditor extends DefaultTreeCellEditor
{
    public UrlValidationCellEditor(JTree tree, DefaultTreeCellRenderer renderer)
    {
        super(tree, renderer);
    }

    @Override
    public Component getTreeCellEditorComponent(JTree tree, Object value,
            boolean isSelected, boolean expanded, boolean leaf, int row)
    {
        return renderer.getTreeCellRendererComponent(tree, value, true, expanded, leaf, row, true);
    }

    @Override
    public boolean isCellEditable(EventObject anEvent)
    {
        return true; // Or make this conditional depending on the node
    }
}

Вот TreeCellRenderer:

public class UrlValidationRenderer extends DefaultTreeCellRenderer implements TreeCellRenderer
{
    JLabel titleLabel;
    UrlGoButton goButton;

    JPanel renderer;

    DefaultTreeCellRenderer defaultRenderer = new DefaultTreeCellRenderer();

    public UrlValidationRenderer()
    {
        renderer = new JPanel(new GridLayout(1, 2));
        titleLabel = new JLabel(" ");
        titleLabel.setForeground(Color.blue);
        renderer.add(titleLabel);
        goButton = new UrlGoButton();
        renderer.add(goButton);
        renderer.setBorder(BorderFactory.createLineBorder(Color.black));
        backgroundSelectionColor = defaultRenderer
            .getBackgroundSelectionColor();
        backgroundNonSelectionColor = defaultRenderer
            .getBackgroundNonSelectionColor();
    }

    @Override
    public Component getTreeCellRendererComponent(JTree tree, Object value,
      boolean selected, boolean expanded, boolean leaf, int row,
      boolean hasFocus)
    {
        Component returnValue = null;
        if ((value != null) && (value instanceof DefaultMutableTreeNode))
        {
          Object userObject = ((DefaultMutableTreeNode) value)
              .getUserObject();
          if (userObject instanceof UrlValidation)
          {
                UrlValidation validationResult = (UrlValidation) userObject;
                titleLabel.setText(validationResult.getServer()+":"+validationResult.getPort());
                goButton.setUrl(validationResult.getUrl());

                if (selected) {
                  renderer.setBackground(backgroundSelectionColor);
                } else {
                  renderer.setBackground(backgroundNonSelectionColor);
                }
                renderer.setEnabled(tree.isEnabled());
                returnValue = renderer;
          }
        }
        if (returnValue == null)
        {
          returnValue = defaultRenderer.getTreeCellRendererComponent(tree,
              value, selected, expanded, leaf, row, hasFocus);
        }
        return returnValue;
    }

}

Буду признателен за любые идеи или предложения. Спасибо!

2 ответа

Решение

Рендереры не работают таким образом. Они используются в качестве резиновых штампов, что означает, что на самом деле существует только один экземпляр средства визуализации, который окрашивается повсеместно, как окрашивается JList. Таким образом, он не может обрабатывать ввод с помощью мыши, поскольку объекты на самом деле отсутствуют - они просто нарисованы.

Чтобы передавать события мыши нижележащим объектам, вам необходимо реализовать редактор ячеек. Иногда редактор выглядит иначе, чем средство визуализации (средства визуализации строк - это метки, редакторы - это, например, текстовые поля). Следуя этой логике, редактор должен быть реализован с использованием другого экземпляра компонента.

Теперь вы собираетесь визуализировать кнопки и использовать их для манипулирования (т.е. редактирования). Редактор тогда должен быть другим экземпляром JButton, отличным от визуализатора. Реализовать кнопку как средство визуализации легко, но сложнее всего реализовать ее в виде редактора. Вам необходимо расширить AbstractCellEditor и реализовать TreeCellEditor и ActionListener. Кнопка является полем класса редактора. В конструкторе класса редактора вы инициализируете кнопку и добавляете ее в качестве нового прослушивателя действий для кнопки. В методе getTreeCellEditorComponent вы просто возвращаете кнопку. В действии actionPerformed вы вызываете любой код, который вам нужно сделать при нажатии кнопки, а затем вызываете stopCellEditing ().

Таким образом, это работает для меня.

Я сделал SSCCE, который демонстрирует использование на String Tree

public class Start
{
    public static class ButtonCellEditor extends AbstractCellEditor implements TreeCellEditor, ActionListener, MouseListener
    {
        private JButton button;
        private JLabel label;
        private JPanel panel;
        private Object value;

        public ButtonCellEditor(){
            panel = new JPanel(new BorderLayout());
            button = new JButton("Press me!");
            button.addActionListener(this);
            label = new JLabel();
            label.addMouseListener(this);
            panel.add(button, BorderLayout.EAST);
            panel.add(label);
        }

        @Override public Object getCellEditorValue(){
            return value.toString();
        }

        @Override public void actionPerformed(ActionEvent e){
            String val = value.toString();
            System.out.println("Pressed: " + val);
            stopCellEditing();
        }

        @Override public Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row){
            this.value = value;
            label.setText(value.toString());
            return panel;
        }

        @Override public void mouseClicked(MouseEvent e){
        }

        @Override public void mousePressed(MouseEvent e){
            String val = value.toString();
            System.out.println("Clicked: " + val);
            stopCellEditing();
        }

        @Override public void mouseReleased(MouseEvent e){
        }

        @Override public void mouseEntered(MouseEvent e){
        }

        @Override public void mouseExited(MouseEvent e){
        }

    }

    public static class ButtonCellRenderer extends JPanel implements TreeCellRenderer
    {
        JButton button;
        JLabel label;

        ButtonCellRenderer(){
            super(new BorderLayout());
            button = new JButton("Press me!");
            label = new JLabel();
            add(button, BorderLayout.EAST);
            add(label);
        }

        @Override public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus){
            label.setText(value.toString());
            return this;
        }

    }

    public static void main(String[] args){
        JTree tree = new JTree();
        tree.setEditable(true);
        tree.setCellRenderer(new ButtonCellRenderer());
        tree.setCellEditor(new ButtonCellEditor());

        JFrame test = new JFrame();
        test.add(new JScrollPane(tree));
        test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        test.setSize(500, 500);
        test.setLocationRelativeTo(null);
        test.setVisible(true);
    }
}

узел должен состоять из 2 частей, метки и кнопки. Когда пользователь щелкает метку, некоторая подробная информация об узле должна появляться в другой части графического интерфейса. Когда пользователь нажимает кнопку, это должно привести к открытию окна браузера...

Не делай так. Вместо этого имейте только метку в дереве. Добавьте кнопку в тот же графический интерфейс, который отображает "подробную информацию об узле".

Другие вопросы по тегам