Пересылка событий mouseMoved () на узлы JTree? - PullRequest
2 голосов
/ 17 января 2012

Если я помещаю JCheckBox вне JTree, он воспроизводит анимацию, когда я наводю на него курсор. Когда я помещаю тот же JCheckbox в узел JTree, он больше не получает никаких событий mouseMoved () и анимация не воспроизводится. Я попытался переслать эти события из JTree в JCheckBox, но ничего не появляется.

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

Есть идеи?

РЕДАКТИРОВАТЬ : Вот отдельный тестовый пример. Обратите внимание, что создание кликабельного JCheckBox выходит за рамки этого вопроса (я уже делал это в своем приложении с использованием TreeCellEditor).

import java.awt.BorderLayout;
import java.awt.Component;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeCellRenderer;

public class HoverBug
{
    public static class TreeRenderer implements TreeCellRenderer
    {
        private final JCheckBox checkbox = new JCheckBox();

        @Override
        public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected,
            boolean expanded, boolean leaf, int row, boolean hasFocus)
        {
            DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
            checkbox.setSelected((Boolean) node.getUserObject());
            return checkbox;
        }
    }

    public static void main(String[] args)
    {
        JCheckBox checkbox = new JCheckBox("See... this works!");

        DefaultMutableTreeNode root = new DefaultMutableTreeNode(Boolean.TRUE);
        DefaultMutableTreeNode child1 = new DefaultMutableTreeNode(Boolean.FALSE);
        DefaultMutableTreeNode child2 = new DefaultMutableTreeNode(Boolean.FALSE);
        root.add(child1);
        root.add(child2);
        DefaultTreeModel model = new DefaultTreeModel(root);

        JTree tree = new JTree(model);
        tree.setCellRenderer(new TreeRenderer());

        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.getContentPane().setLayout(new BorderLayout());
        frame.getContentPane().add(checkbox, BorderLayout.NORTH);
        frame.getContentPane().add(tree, BorderLayout.CENTER);
        frame.setSize(800, 600);
        frame.setVisible(true);
    }

Ответы [ 2 ]

1 голос
/ 19 января 2012

Я не думаю, что есть решение по двум причинам:

  1. Классы пользовательского интерфейса JCheckBox (например, BasicCheckBoxUI) вызывают repaint () всякий раз, когда вид изменяется, но вы не увидите эти измененияесли вы не попросите JTree перерисовать узел, используя DefaultTreeModel.nodeChanged (TreeNode) .
  2. Даже если вы прослушиваете те же события модели, что и класс пользовательского интерфейса, и запускаете DefaultTreeModel.nodeChanged(TreeNode) после repaint (), некоторые L & F (в частности Insubstantial ) проверяют, находится ли JCheckBox внутри CellRendererPane, и отключают анимацию в таком случае (возможно, по соображениям производительности).

В целом, дизайн JTree делает его непрактичным для анимации узлов.

Для чего стоит, вот моя попытка перенаправить события на базовый узел (он глючит, но вы поняли идею):

[...]
    MouseAdapterImpl listener = new MouseAdapterImpl(tree);
    tree.addMouseListener(listener);
    tree.addMouseMotionListener(listener);
    tree.addMouseWheelListener(listener);
[...]
private class MouseAdapterImpl implements MouseListener, MouseWheelListener, MouseMotionListener
{
    private final JTree tree;
    private int lastRow = -1;

    public MouseAdapterImpl(JTree tree)
    {
        this.tree = tree;
    }

    /**
     * Returns the mouse position relative to the JTree row.
     * <p/>
     * @param e the mouse event
     */
    private void forwardEvent(MouseEvent e)
    {
        int row = tree.getRowForLocation(e.getX(), e.getY());
        Rectangle bounds;
        Point point;
        if (row == -1)
        {
            bounds = null;
            point = null;
        }
        else
        {
            bounds = tree.getRowBounds(row);
            point = new Point(e.getX() - bounds.x, e.getY() - bounds.y);
        }
        if (lastRow != row)
        {
            if (lastRow != -1)
            {
                Rectangle lastBounds = tree.getRowBounds(lastRow);
                if (lastBounds != null)
                {
                    Point lastPoint = new Point(e.getX() - lastBounds.x, e.getY() - lastBounds.y);
                    dispatchEvent(new MouseEvent(checkbox, MouseEvent.MOUSE_EXITED,
                        System.currentTimeMillis(), 0, lastPoint.x, lastPoint.y, 0, false, 0), lastRow);
                }
            }
            if (row != -1)
            {
                dispatchEvent(new MouseEvent(checkbox, MouseEvent.MOUSE_ENTERED,
                    System.currentTimeMillis(), 0, point.x, point.y, 0, false, 0), row);
            }
        }
        lastRow = row;
        if (row == -1)
            return;
        dispatchEvent(new MouseEvent(checkbox, e.getID(),
            System.currentTimeMillis(), e.getModifiers(), point.x, point.y, e.getClickCount(),
            e.isPopupTrigger(), e.getButton()), row);
    }

    private void dispatchEvent(MouseEvent e, int row)
    {
        checkbox.dispatchEvent(e);
        TreePath pathForLocation = tree.getPathForRow(row);
        if (pathForLocation == null)
            return;
        Object lastPathComponent = pathForLocation.getLastPathComponent();
        if (lastPathComponent instanceof DefaultMutableTreeNode)
        {
            DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
            model.nodeChanged((DefaultMutableTreeNode) lastPathComponent);
        }
    }

    @Override
    public void mouseEntered(MouseEvent e)
    {
        forwardEvent(e);
    }

    @Override
    public void mouseMoved(MouseEvent e)
    {
        forwardEvent(e);
    }

    @Override
    public void mouseExited(MouseEvent e)
    {
        forwardEvent(e);
    }

    @Override
    public void mouseClicked(MouseEvent e)
    {
        forwardEvent(e);
    }

    @Override
    public void mousePressed(MouseEvent e)
    {
        forwardEvent(e);
    }

    @Override
    public void mouseReleased(MouseEvent e)
    {
        forwardEvent(e);
    }

    @Override
    public void mouseWheelMoved(MouseWheelEvent e)
    {
        forwardEvent(e);
    }

    @Override
    public void mouseDragged(MouseEvent e)
    {
        forwardEvent(e);
    }
}

РЕДАКТИРОВАТЬ : Хорошие новости.Они исправили это в JavaFX: http://javafx -jira.kenai.com / browse / RT-19027

0 голосов
/ 18 января 2012

Вот то, что я думаю, вы ищете ниже, но если вы делаете что-то более сложное, вы можете вместо этого заняться созданием CellEditor.:

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreePath;

public class HoverBug {
public static class TreeRenderer implements TreeCellRenderer {
    private final JCheckBox checkbox = new JCheckBox();

    @Override
    public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected,
                                                  boolean expanded, boolean leaf, int row, boolean hasFocus) {
        DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
        checkbox.setSelected((Boolean) node.getUserObject());
        checkbox.setText("Row:" + row);
        return checkbox;
    }
}

public static void main(String[] args) {
    JCheckBox checkbox = new JCheckBox("See... this works!");

    DefaultMutableTreeNode root = new DefaultMutableTreeNode(Boolean.TRUE);
    DefaultMutableTreeNode child1 = new DefaultMutableTreeNode(Boolean.FALSE);
    DefaultMutableTreeNode child2 = new DefaultMutableTreeNode(Boolean.FALSE);
    root.add(child1);
    root.add(child2);
    final DefaultTreeModel model = new DefaultTreeModel(root);

    final JTree tree = new JTree(model);
    tree.setCellRenderer(new TreeRenderer());

    tree.addMouseListener(new MouseListener() {
        @Override
        public void mouseClicked(MouseEvent e) {
            TreePath pathForLocation = tree.getPathForLocation(e.getX(), e.getY());
            Object lastPathComponent = pathForLocation.getLastPathComponent();
            if(lastPathComponent instanceof DefaultMutableTreeNode){
                Boolean oldObject = (Boolean) ((DefaultMutableTreeNode)lastPathComponent).getUserObject();
                ((DefaultMutableTreeNode)lastPathComponent).setUserObject(!oldObject);
                model.nodeChanged((DefaultMutableTreeNode)lastPathComponent);
            }
        }

        @Override
        public void mousePressed(MouseEvent e) {

        }

        @Override
        public void mouseReleased(MouseEvent e) {

        }

        @Override
        public void mouseEntered(MouseEvent e) {

        }

        @Override
        public void mouseExited(MouseEvent e) {

        }
    });


    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    frame.getContentPane().setLayout(new BorderLayout());
    frame.getContentPane().add(checkbox, BorderLayout.NORTH);
    frame.getContentPane().add(tree, BorderLayout.CENTER);
    frame.setSize(800, 600);
    frame.setVisible(true);
}
}
...