簡體   English   中英

如何設置JTree單元格的透明背景?

[英]How to set transparent background for JTree cell?

民間,

我正在嘗試創建一個漸變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值,並DefaultTreeCellRenderer填充其內容。 但是,有一個標志可以嘗試。 我過去做過,但是沒有時間進行測試...

嘗試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的答案,對基本問題的冗長分析在最后):

如果要使global屬性在手動設置為樹的defaultTreeCellRenderer中生效,則該渲染器必須再次調用updateUI,

UIManager.put("Tree.rendererFillBackground", false);
    ...
TreeCellRenderer r = new DefaultTreeCellRenderer() {
     {
          updateUI();
     }
};
tree.setCellRenderer(r);

如果你不想改變全局設置,並有透明渲染只有一些樹的實例-該選項

  • 要么從頭開始實現TreeCellRenderer,然后消除所有臟污(例如覆蓋油漆並執行一些意外的硬編碼技巧...哎呀!)
  • 通過在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;

最終結果是(假設我們通過反射強制訪問該字段),無論UIManager中的設置如何,始終執行以下操作。

DefaultTreeCellRenderer renderer = new DefaultTreeRenderer();
assertTrue(renderer.fillBackground);

與此不同尋常的是:為什么讓UI安裝默認設置時,UIManager 的設置會生效? 這是因為渲染器的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());
}

順便說一句:這種實例化混亂似乎是在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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM