如何为JTree单元设置透明背景?
伙计们,
我正在尝试创建一个渐变JTree控件。 除了树单元格的背景不透明外,以下代码大部分都有效。 如果有人打电话告诉我,我做得不对,我将不胜感激。
预先感谢您的帮助。
问候,
彼得
package TestPackage; import javax.swing.*; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeCellRenderer; import java.awt.*; public class Test { public Test() { JFrame frame = new JFrame(); JPanel framePanel = new JPanel(); framePanel.setLayout(new BorderLayout()); frame.setContentPane(framePanel); DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode("Item"); DefaultMutableTreeNode childNode = new DefaultMutableTreeNode("Child"); rootNode.add(childNode); GradientTree tree = new GradientTree(rootNode); // JTree tree = new JTree(rootNode); // tree.setBackground(Color.blue); tree.setCellRenderer(new MyRenderer()); JScrollPane scroll = new JScrollPane(tree); scroll.setOpaque(false); framePanel.add(scroll, BorderLayout.CENTER); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { new Test(); } }); } @SuppressWarnings("serial") public static class GradientTree extends JTree { public GradientTree(DefaultMutableTreeNode node) { super(node); } @Override protected void paintComponent(Graphics g) { int h = getHeight(); int w = getWidth(); GradientPaint gradientPaint = new GradientPaint(0, 0, Color.LIGHT_GRAY, 0, h, Color.WHITE); Graphics2D g2D = (Graphics2D) g; g2D.setPaint(gradientPaint); g2D.fillRect(0, 0, w, h); this.setOpaque(false); super.paintComponent(g); this.setOpaque(true); } } @SuppressWarnings({"serial" }) private class MyRenderer extends DefaultTreeCellRenderer { public MyRenderer() { this.setOpaque(false); this.setForeground(Color.RED); } public Component getTreeCellRendererComponent( JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { super.getTreeCellRendererComponent( tree, value, sel, expanded, leaf, row, hasFocus); return this; } } }
这是一个真正的痛苦。 DefaultTreeCellRenderer
将忽略opaque
值并无论如何填充它的内容。 但是,你可以尝试一个标志。 我过去做过,但没有时间测试它……
试试UIManager.put("Tree.rendererFillBackground", false)
。 尝试在渲染任何内容之前执行此操作,但在应用任何外观设置之后。
更新
在创建任何树之前设置此属性非常重要
没有| 随着…
public class TestTreeRenderer { public static void main(String[] args) { new TestTreeRenderer(); } public TestTreeRenderer() { EventQueue.invokeLater(new Runnable() { @Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { } JFrame frame = new JFrame("Test"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setLayout(new BorderLayout()); frame.add(new TreePane()); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } }); } public class TreePane extends JPanel { private JTree tree; public TreePane() { // THIS IS VERY IMPORTANT // You must set this BEFORE creating ANY trees!! UIManager.put("Tree.rendererFillBackground", false); setLayout(new BorderLayout()); tree = new JTree(); tree.setBackground(Color.BLUE); System.out.println("Loading files..."); File root = new File("/etc"); DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode(root.getName()); for (File file : root.listFiles()) { rootNode.add(new DefaultMutableTreeNode(file.getName())); } System.out.println("Loading model"); DefaultTreeModel model = new DefaultTreeModel(rootNode); tree.setModel(model); add(new JScrollPane(tree)); } } }
回答
(根据@Mad的回答,对底层问题的长期分析最后):
如果您希望全局属性在手动设置到树的defaultTreeCellRenderer中有效,则该呈现器必须再次调用updateUI,fi
UIManager.put("Tree.rendererFillBackground", false); ... TreeCellRenderer r = new DefaultTreeCellRenderer() { { updateUI(); } }; tree.setCellRenderer(r);
如果您不想更改全局设置并且透明渲染器只有一些树实例 – 选项是
- 从头开始实现一个TreeCellRenderer并省去所有的肮脏(如重写油漆和做一些意想不到的硬编码技巧…… doooh!)
- 通过在updateUI中临时设置ui属性来欺骗渲染器
欺骗代码:
TreeCellRenderer r = new DefaultTreeCellRenderer() { { updateUI(); } @Override public void updateUI() { Object old = UIManager.get("Tree.rendererFillBackground"); try { UIManager.put("Tree.rendererFillBackground", false); super.updateUI(); } finally { UIManager.put("Tree.rendererFillBackground", old); } } };
分析
从我的评论开始:
奇怪的是,仅仅设置CellRenderer(与让ui安装它的优点相比)使得标志无效
解决了这个难题:
DefaultTreeCellRenderer 有意从UIManager中的设置设置其fillBackground字段 – 但在实例化时失败。 原因是 – 一个太常见的错误;-) – 实际上是在super的实例化中这样做,因为在super的构造函数中调用了一个重写的方法:
// this is implemented in DefaultTreeCellRenderer // but called in JLabel constructor public void updateUI() { .... // we are in JLabel, that is fillBackground not yet known fillBackground = DefaultLookup.getBoolean(this, ui, "Tree.rendererFillBackground", true); ... }
然后在实例化过程中,字段值被硬编码:
private boolean fillBackground = true;
最终结果是(假设我们通过reflection强制访问字段,fi),无论UIManager中的设置如何,总是跟随传递。
DefaultTreeCellRenderer renderer = new DefaultTreeRenderer(); assertTrue(renderer.fillBackground);
有了这个不寻常的东西:为什么UIManager 中的设置在让ui安装它的默认值时有效? 这里的原因是渲染器updateUI被调用两次:一次在实例化时,一次在树的updateUI中:
public void updateUI() { setUI((TreeUI)UIManager.getUI(this)); // JW: at this point the renderer has its fillbackground hard-coded to true SwingUtilities.updateRendererOrEditorUI(getCellRenderer()); // JW: now it's updateUI has been called again, and correctly set to the // UIManager's value SwingUtilities.updateRendererOrEditorUI(getCellEditor()); }
BTW:这个实例化混乱似乎是在jdk7中引入的……很可能(虽然没有检查)渲染器颜色的默认设置也不起作用。
如何扩展DefaultTreeCellRenderer如下:
public class MyRenderer extends DefaultTreeCellRenderer { public Component getTreeCellRendererComponent(JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row, boolean hasFocus) { JComponent c = (JComponent) super.getTreeCellRendererComponent(tree, value, isSelected, expanded, leaf, row, hasFocus); c.setOpaque(true); return c; }
}
设置c.setOpaque(true); 似乎解决了它。
在这些Swing专家面前我真的犹豫推进这个假设……但是,最近的JDK能否真正解决这个问题呢?
我在我的应用程序中有这样的代码,它似乎工作得很好…… JTree的背景完美闪耀…… NB Jython,但应该是可以理解的:
def getTreeCellRendererComponent( self, tree, value, selected, expanded, leaf, row, has_focus ): super_comp = self.super__getTreeCellRendererComponent( tree, value, selected, expanded, leaf, row, has_focus ) super_comp.opaque = not selected ...
Java版本是1.7.0_079