Отмените выбор узла из JTree, когда щелкните в любом месте за пределами дерева - PullRequest
7 голосов
/ 27 марта 2019

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

Можно ли вызвать дерево, чтобы очистить выделение, когда я нажимаю на любое место вне дерева? Очистив выделение, я смогу снова изменить цвет фона, но я не знаю, как или где использовать метод clearSelection() дерева, когда я щелкаю по дереву.

Вот код, который я использую:

Пример:

import javax.swing.*;
import javax.swing.tree.*;
import java.awt.*;

public class JTreeSelectDeselect {

    public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);

        JPanel panel = new JPanel(new BorderLayout());
        JTree tree = new JTree();
        tree.setCellRenderer(new DeselectTreeCellRenderer());

        panel.add(tree, BorderLayout.LINE_START);
        panel.add(new JScrollPane(new JTextArea(10, 30)));
        frame.add(panel);

        frame.pack();
        frame.setVisible(true);
    }
}

class DeselectTreeCellRenderer extends DefaultTreeCellRenderer {

    @Override
    public Color getBackgroundSelectionColor() {
        return new Color(86, 92, 160);
    }

    @Override
    public Color getBackground() {
        return (null);
    }

    @Override
    public Color getBackgroundNonSelectionColor() {
        return new Color(23, 27, 36);
    }

    @Override
    public Component getTreeCellRendererComponent(JTree tree, Object value,
            boolean sel, boolean exp, boolean leaf, int row, boolean hasFocus) {
        super.getTreeCellRendererComponent(tree, value, sel, exp, leaf, row, hasFocus);

        setForeground(new Color(225, 225, 221, 255));
        setOpaque(false);

        return this;
    }
}

Здесь я показываю, как я создаю узлы и добавляю их в дерево, используя модель дерева, и как я настраиваю свой пользовательский TreeCellRenderer.

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

Есть ли способ отменить выбор узла, когда я щелкаю вне дерева?

И если кто-то знает, есть ли способ изменить некоторые листья с помощью флажков TreeCellRenderer? Чтобы у некоторых детей были метки, а у других - флажки. Потому что, когда я пытаюсь добавить флажки, он говорит (как я и ожидал), что флажки не являются DefaultMutableTreeNode объектами, и я не могу добавить их в древовидную модель.

Ответы [ 3 ]

4 голосов
/ 01 апреля 2019

Прежде всего вам не нужно создавать подкласс DefaultTreeCellRenderer, если вы просто хотите изменить некоторые цвета.Вы можете создать новый, установить цвета по своему желанию и установить его на дерево.В приведенном ниже примере кода я сделал это в getDefaultTreeCellRenderer ().

Ваша панель содержит два элемента - дерево и текстовую область.Чтобы достичь того, что вам нужно, я добавил слушателя мыши и слушателя фокуса к дереву:

  • Прослушиватель мыши - при использовании mouseClicked () запускается как при щелчке внутри дерева, так и за его пределами (но не при этом).в TextArea, для этого у нас есть слушатель фокуса). Чтобы проверить, щелкнули ли вы в границах ячейки, мы используем tree.getRowForLocation (e.getX (), e.getY ()), и если он возвращает -1, это означает, что мы щелкнули вне любой ячейки, чтобы мы могли очиститьвыделение
  • Слушатель фокуса - когда вы потеряете фокус от JTree и нажмете на текстовую область, это сработает, и мы просто очистим выделение

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

import javax.swing.*;
import javax.swing.tree.DefaultTreeCellRenderer;
import java.awt.*;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

public class JTreeSelectDeselect {

    public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);

        JPanel panel = new JPanel(new BorderLayout());
        JTree tree = new JTree();

        tree.setCellRenderer(getDefaultTreeCellRenderer());
        tree.addMouseListener(getMouseListener(tree));
        tree.addFocusListener(getFocusListener(tree));

        panel.add(tree, BorderLayout.LINE_START);
        panel.add(new JScrollPane(new JTextArea(10, 30)));
        frame.add(panel);

        frame.pack();
        frame.setVisible(true);
    }

    private static DefaultTreeCellRenderer getDefaultTreeCellRenderer() {
        DefaultTreeCellRenderer defaultTreeCellRenderer = new DefaultTreeCellRenderer();
        defaultTreeCellRenderer.setBackgroundSelectionColor(new Color(86, 92, 160));
        defaultTreeCellRenderer.setBackgroundNonSelectionColor(new Color(135, 151, 53));
        defaultTreeCellRenderer.setBackground(new Color(225, 225, 221, 255));
        defaultTreeCellRenderer.setForeground(new Color(225, 225, 221, 255));
        return defaultTreeCellRenderer;
    }

    private static FocusListener getFocusListener(final JTree tree) {
        return new FocusListener() {
            @Override
            public void focusGained(FocusEvent e) {

            }

            @Override
            public void focusLost(FocusEvent e) {
                System.out.println("focus lost");
                tree.clearSelection();
            }
        };
    }

    private static MouseListener getMouseListener(final JTree tree) {
        return new MouseListener() {
            @Override
            public void mouseClicked(MouseEvent e) {
                System.out.println("mouse clicked");
                if(tree.getRowForLocation(e.getX(),e.getY()) == -1) {
                    System.out.println("clicked outside a specific cell");
                    tree.clearSelection();
                }
            }

            @Override
            public void mousePressed(MouseEvent e) {

            }

            @Override
            public void mouseReleased(MouseEvent e) {

            }

            @Override
            public void mouseEntered(MouseEvent e) {

            }

            @Override
            public void mouseExited(MouseEvent e) {

            }
        };
    }
}
2 голосов
/ 03 апреля 2019

Класс Robot в пакете Java AWT используется для генерации собственных событий системного ввода для целей автоматизации тестирования, самостоятельных демонстраций и других приложений, где необходимо управление мышью и клавиатурой.Основная цель Robot - облегчить автоматизированное тестирование реализаций платформы Java.Проще говоря, класс обеспечивает управление мышью и клавиатурой.В приведенном ниже фрагменте «Робот» был использован для захвата события и очистки выбора из дерева.

import java.awt.AWTException;
import java.awt.Robot;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.tree.TreePath;

public class TreeDeselectionTest {

  JTree createdTreeInstance = new JTree();

  TreePath pathSelectionInstance;

  Robot robotInstance;

  public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        new TreeDeselectionTest().createTreeAndCaptureEvents();
      }
    });
  }

  public void createTreeAndCaptureEvents() {
    try {
      robotInstance = new Robot();
    } catch (AWTException exceptionInstance) {
      exceptionInstance.printStackTrace();
    }
    createdTreeInstance.setShowsRootHandles(false);
    createdTreeInstance.addMouseListener(new MouseAdapter() {
      @Override
      public void mousePressed(MouseEvent eventMousePressInstance) {
        if (robotInstance != null && pathSelectionInstance != null && eventMousePressInstance.getButton() == MouseEvent.BUTTON1) {
          createdTreeInstance.clearSelection();
          robotInstance.mousePress(InputEvent.BUTTON1_MASK);
          robotInstance.mouseRelease(InputEvent.BUTTON1_MASK);
        }
        pathSelectionInstance = createdTreeInstance.getSelectionPath();
      }
    });
    JFrame frameConetnds = new JFrame();
    frameConetnds.setContentPane(new JScrollPane(createdTreeInstance));
    frameConetnds.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frameConetnds.setSize(400, 400);
    frameConetnds.setLocationRelativeTo(null);
    frameConetnds.setVisible(true);
  }
}
2 голосов
/ 02 апреля 2019

Если вы хотите отменить выбор всех выбранных узлов в дереве при щелчке мышью где-то еще, вам нужно получать уведомления, когда пользователь щелкает где-то еще.Чтобы получить его, вы можете использовать глобальный прослушиватель событий (Toolkit.getDefaultToolkit().addAWTEventListener()).

Если вы хотите отобразить некоторые флажки в своем дереве, вам нужен специальный средство визуализации ячеек, которое для условия возвращает флажок.Также вам нужен контейнер данных для хранения информации о том, выбран ли узел, и, наконец, вам нужна подпрограмма для обновления вашей модели данных при щелчке по узлу.Последнее, что обычно предоставляют редакторы ячеек (подробнее об этом в статье о редакторах и визуализаторах ), но в вашем случае это проще сделать с помощью глобального прослушивателя мыши, который мы установили ранее.

Конечно, мой пример содержит немного "магии свинга", поэтому, когда вы чего-то не понимаете, не стесняйтесь спрашивать меня;)

import java.awt.AWTEvent;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Toolkit;
import java.awt.event.AWTEventListener;
import java.awt.event.MouseEvent;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;

/**
 * <code>JTreeDeselected</code>.
 */
public class JTreeSelectDeselect {

    private final JTree tree = new JTree();

    // model to hold nodes that must be presented as check boxes 
    // and whether the check boxes are selected
    private final Map<Object, Boolean> checkMap = new HashMap<>();

    // listener as method reference
    private AWTEventListener awtListener = this::mouseClicked;

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new JTreeSelectDeselect()::start);
    }

    private void start() {
        checkMap.put("football", true);
        checkMap.put("soccer", false);
        checkMap.put("violet", true);
        checkMap.put("yellow", false);
        checkMap.put("pizza", true);
        checkMap.put("ravioli", false);
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);

        JPanel panel = new JPanel(new BorderLayout());
        // JTree tree = new JTree();
        tree.setCellRenderer(new DeselectTreeCellRenderer(checkMap));
        panel.add(new JScrollPane(tree), BorderLayout.LINE_START);
        panel.add(new JScrollPane(new JTextArea(10, 30)));
        frame.add(panel);
        // register global listener
        Toolkit.getDefaultToolkit().addAWTEventListener(awtListener, AWTEvent.MOUSE_EVENT_MASK);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    private void mouseClicked(AWTEvent evt) {
        if (evt instanceof MouseEvent) {
            MouseEvent me = (MouseEvent) evt;
            if (me.getID() == MouseEvent.MOUSE_PRESSED) {
                if (me.getComponent().equals(tree)) {
                    TreePath path = tree.getPathForLocation(me.getX(), me.getY());
                    if (path == null) {
                        tree.clearSelection();
                    } else {
                        // update check box value
                        String pathString = Objects.toString(path.getLastPathComponent(), "");
                        Boolean val = checkMap.get(pathString);
                        if (val != null) {
                            checkMap.put(pathString, !val);
                            ((DefaultTreeModel) tree.getModel()).valueForPathChanged(path, pathString);
                        }
                    }
                } else {
                    tree.clearSelection();
                }
            }
        }
    }
}

class DeselectTreeCellRenderer extends DefaultTreeCellRenderer {

    private final JCheckBox checkBox = new JCheckBox();

    private final Map<Object, Boolean> checkMap;

    public DeselectTreeCellRenderer(Map<Object, Boolean> checkMap) {
        this.checkMap = checkMap;
    }

    @Override
    public Color getBackgroundSelectionColor() {
        return new Color(86, 92, 160);
    }

    @Override
    public Color getBackground() {
        return (null);
    }

    @Override
    public Color getBackgroundNonSelectionColor() {
        return new Color(23, 27, 36);
    }

    @Override
    public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean exp, boolean leaf, int row,
            boolean hasFocus) {
        super.getTreeCellRendererComponent(tree, value, sel, exp, leaf, row, hasFocus);

        setForeground(new Color(225, 225, 221, 255));
        setOpaque(false);

        // if our "check model" contains an entry for the node,
        // present the node as check box
        if (checkMap.containsKey(Objects.toString(value))) {
            checkBox.setOpaque(true);
            checkBox.setSelected(Boolean.TRUE == checkMap.get(Objects.toString(value)));
            checkBox.setBackground(sel ? getBackgroundSelectionColor() : getBackgroundNonSelectionColor());
            checkBox.setForeground(getForeground());
            checkBox.setFont(getFont());
            checkBox.setBorder(getBorder());
            checkBox.setText(getText());
            return checkBox;
        }
        return this;
    }
}
...