2012-01-17 55 views
2

如果我將一個JCheckBox放置在JTree之外,則將其懸停在它上面時播放動畫。當我在JTree節點中放置相同的JCheckbox時,它不再接收任何mouseMoved()事件並且不播放動畫。我嘗試將這些事件從JTree轉發到JCheckBox,但沒有顯示出來。將mouseMoved()事件轉發給JTree節點?

我猜測問題是相同的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); 
    } 
+0

你是對的。 JCheckBox被用作渲染器。 JTree是將接收鼠標事件的組件。如果你有一個獨立的代碼示例,我可以進一步幫助。 – Kylar 2012-01-17 17:13:12

+0

您可能必須實現自己的渲染器。 – mre 2012-01-17 17:14:18

+0

@Kylar:我已經添加了一個測試用例。 – Gili 2012-01-17 17:34:55

回答

1

我不牛逼認爲有兩個原因,一個解決方案:

  1. JCheckBox的UI類(如BasicCheckBoxUI)調用重繪()只要看看被修改,但除非你問JTree的重繪節點,你不會看到這些變化使用DefaultTreeModel.nodeChanged(TreeNode)
  2. 即使你聽同型號事件的UI類和觸發重繪後DefaultTreeModel.nodeChanged(TreeNode)(),一些L- & FS(值得注意的是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

+1作爲事件轉發代碼。如果你能夠弄清楚swing/awt事件的所有細節,這可以工作。整個單元格渲染器模式對我來說似乎受到限制。 – 2012-10-29 19:38:04

0

這就是我認爲你正在尋找下方,但如果你正在做的事情比較複雜,你可能要考慮創建一個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); 
} 
} 
+0

下面是另一個實際實現的MouseMoved:http://pastebin.com/1LLccNyi – Kylar 2012-01-18 15:43:37

+0

請在Stackoverflow中編輯您的答案,而不是在pastebin上發佈。您的mouseMoved()解決方案不起作用。如果將鼠標懸停在真正的JCheckBox上而不是JTree中的鼠標上,則會發現真正的複選框將鼠標懸停在內部邊框上,而JTree則不會。 – Gili 2012-01-18 18:13:00