簡體   English   中英

TreeCellRenderer 中的 label 寬度不正確

[英]Incorrect label width in TreeCellRenderer

我得到了一個帶有自定義TreeCellRendererJTree
此渲染器是一個面板,其中包含一個復選框和一個 label。
雖然 label 的文本對於每個節點都是固定的(在DefaultMutableTreeNode的 UserObject 中指定),但該文本可能是粗體,也可能不是粗體。 這取決於復選框的狀態。
取消選中該復選框時,label 文本不再是粗體,但其寬度保持不變(太寬)。
類似地,選中復選框時,文本報告為粗體,但 label 未放大。
這會導致文本被截斷。

現實生活中的情況要復雜一些,但下面是一個完整的例子。
為了重現問題:

  • Select 第一個節點通過點擊文字
  • 按空格鍵取消選中復選框。 文字不是粗體,但 label 太大。
  • 單擊第一個節點上的展開按鈕。 label寬度調整
  • 按空格鍵到 select combobox。 文本變為粗體但被截斷。

我嘗試插入幾個調用以invalidaterepaint等,但沒有解決問題。
默認外觀和系統 (Windows) 外觀中都會出現此問題。

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

@SuppressWarnings("serial")
public class TestFrame extends JFrame
{
  public TestFrame()
  {
    getContentPane().setLayout(new GridBagLayout());
    setDefaultCloseOperation(EXIT_ON_CLOSE);
    setTitle("Test TreeCellRenderer");

    JScrollPane tree_pane;
    tree_pane = new JScrollPane();
    tree_pane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
    tree_pane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
    tree_pane.setPreferredSize(new Dimension(300, 200));

    TestTree tree;
    tree = new TestTree();
    tree_pane.getViewport().add(tree, null);

    GridBagConstraints constraints;
    constraints = new GridBagConstraints(0, 0, 1, 1, 1.0, 1.0, GridBagConstraints.NORTH,
                                         GridBagConstraints.BOTH, new Insets(8, 8, 8, 8), 0, 0);
    getContentPane().add(tree_pane, constraints);

    pack();
    setMinimumSize(getPreferredSize());

  }

  public static void main(String[] args)
  {
    try
    {
      UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
      TestFrame frame;
      frame = new TestFrame();
      frame.setVisible(true);
    }
    catch (Exception exception)
    {
      exception.printStackTrace();
    }

  } // main

} // class TestFrame

這個 class 實現了我的樹:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.plaf.basic.BasicTreeUI;
import javax.swing.tree.*;

@SuppressWarnings("serial")
public class TestTree extends JTree
{
  // The width of the checkbox within the renderer
  // We need it when a node is clicked in order to check what part is exactly underneath the mouse
  public int checkboxWidth;

  public TestTree()
  {
    // Initialize object
    super(getNodes());
    setRootVisible(false);
    setShowsRootHandles(true);
    setCellRenderer(new MyCellRenderer());
    addMouseListener(new TreeMouseManager());
    addKeyListener(new TreeKeyManager());

  } // constructor

  private void toggleCheckBox(TreePath treePath)
  {
    // Determine node being toggled
    Object[]               path;
    DefaultMutableTreeNode node;
    NodeInfo            info;
    path = treePath.getPath();
    node = (DefaultMutableTreeNode)path[path.length - 1];
    info = (NodeInfo)node.getUserObject();

    // Toggle selection
    info.checked = !info.checked;
    repaint();

  } // toggleCheckBox

  private class TreeMouseManager extends MouseAdapter
  {
    @Override
    public void mouseClicked(MouseEvent event)
    {
      // Determine node corresponding to location
      TreePath treePath;
      treePath = getPathForLocation(event.getX(), event.getY());
      if (treePath == null)
        return;

      // Manage only single click with left button
      if ((event.getClickCount() != 1) || (event.getButton() != MouseEvent.BUTTON1))
        return;

      // Determine horizontal position of checkbox
      BasicTreeUI ui;
      int         depth;
      int         leftIndent;
      int         rightIndent;
      int         checkboxLeft;
      int         checkboxRight;
      ui = (BasicTreeUI)getUI();
      depth = treePath.getPathCount();
      leftIndent = ui.getLeftChildIndent();
      rightIndent = ui.getRightChildIndent();
      checkboxLeft = (depth - 1) * (leftIndent + rightIndent);
      checkboxRight = checkboxLeft + checkboxWidth - 1;

      // Ignore if not clicked on checkbox
      int x;
      x = event.getX();
      if ((x < checkboxLeft) || (x > checkboxRight))
        return;

      // Toggle checkbox
      toggleCheckBox(treePath);

    } // mouseClicked

  } // class TreeMouseManager

  private class TreeKeyManager extends KeyAdapter
  {
    @Override
    public void keyPressed(KeyEvent event)
    {
      // Determine selected element
      TreePath treePath;
      treePath = getSelectionPath();
      if (treePath == null)
        return;

      // Manage event for this element
      if (event.getKeyCode() == KeyEvent.VK_SPACE)
        toggleCheckBox(treePath);

    } // keyPressed

  } // class TreeKeyManager

  private class MyCellRenderer extends JPanel implements TreeCellRenderer
  {
    public MyCellRenderer()
    {
      // Create components
      checkbox = new JCheckBox();
      checkbox.setBorder(null);
      checkbox.setOpaque(false);
      label = new JLabel();
      label.setBorder(new EmptyBorder(new Insets(0, 2, 0, 2)));

      // Initialize panel
      GridBagConstraints constraints;
      setLayout(new GridBagLayout());
      setOpaque(false);
      constraints = new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.WEST,
                                           GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0);
      add(checkbox, constraints);
      constraints = new GridBagConstraints(1, 0, 1, 1, 1.0, 0.0, GridBagConstraints.WEST,
                                           GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0);
      add(label, constraints);

      // Save the width of the checkbox
      // We need it when the mouse is clicked on a node
      checkboxWidth = (int)checkbox.getPreferredSize().getWidth();

    } // constructor

    @Override
    public Component getTreeCellRendererComponent(JTree   tree,
                                                  Object  value,
                                                  boolean selected,
                                                  boolean expanded,
                                                  boolean leaf,
                                                  int     row,
                                                  boolean hasFocus)
    {
      // Make data accessible
      // Ignore if it's the root node
      DefaultMutableTreeNode node;
      NodeInfo            info;
      node = (DefaultMutableTreeNode)value;
      if (node.getUserObject() instanceof NodeInfo)
        info = (NodeInfo)node.getUserObject();
      else
        return (this);

      // Determine font
      Font font;
      font = label.getFont();
      if (info.checked)
        font = font.deriveFont(font.getStyle() | Font.BOLD);
      else
        font = font.deriveFont(font.getStyle() & ~Font.BOLD);

      // Configure components
      checkbox.setSelected(info.checked);
      label.setText(info.name);
      label.setOpaque(selected);
      label.setFont(font);
      if (selected)
      {
        label.setBackground(SystemColor.textHighlight);
        label.setForeground(SystemColor.textHighlightText);
      }
      else
      {
        label.setBackground(SystemColor.text);
        label.setForeground(SystemColor.textText);
      }

      // Make sure everything is painted correctly
      label.invalidate();
      checkbox.invalidate();
      invalidate();

      // Done
      return (this);

    } // getTreeCellRendererComponent

    private JCheckBox checkbox;
    private JLabel label;

  } // class MyCellRenderer

  private static DefaultMutableTreeNode getNodes()
  {
    // Create root
    DefaultMutableTreeNode root;
    root = new DefaultMutableTreeNode("root");

    // Create first level children
    DefaultMutableTreeNode first;
    DefaultMutableTreeNode second;
    DefaultMutableTreeNode third;
    NodeInfo               info;
    info = new NodeInfo();
    info.name = "This is the first node";
    info.checked = true;
    first = new DefaultMutableTreeNode(info);
    info = new NodeInfo();
    info.name = "And this is the second";
    info.checked = false;
    second = new DefaultMutableTreeNode(info);
    info = new NodeInfo();
    info.name = "Finally, the third";
    info.checked = false;
    third = new DefaultMutableTreeNode(info);
    root.add(first);
    root.add(second);
    root.add(third);

    // Add second level children
    info = new NodeInfo();
    info.name = "Second level node";
    info.checked = true;
    first.add(new DefaultMutableTreeNode(info));
    info = new NodeInfo();
    info.name = "This is another one";
    info.checked = false;
    first.add(new DefaultMutableTreeNode(info));
    info = new NodeInfo();
    info.name = "And this is the last one";
    info.checked = true;
    first.add(new DefaultMutableTreeNode(info));

    // Done
    return (root);

  } // getNodes

  private static class NodeInfo
  {
    public String name;
    public boolean checked;
  }

} // class TestTree

更新
getTreeCellRendererComponent中,我嘗試獲取首選大小。
他們看起來不錯。 選中復選框時,label 和面板本身的首選尺寸都會增加。 取消選中復選框時,它們會減少。

感謝這個問題的答案Change JTree row height resizing behavior when rendering ,我自己設法解決了這個問題:

  private void toggleCheckBox(TreePath treePath)
  {
    // Determine node being toggled
    Object[]               path;
    DefaultMutableTreeNode node;
    NodeInfo               info;
    path = treePath.getPath();
    node = (DefaultMutableTreeNode)path[path.length - 1];
    info = (NodeInfo)node.getUserObject();

    // Toggle selection
    info.checked = !info.checked;

    // Make sure tree recalculates width of the nodes
    BasicTreeUI ui = (BasicTreeUI)getUI();
    try
    {
      Method method = BasicTreeUI.class.getDeclaredMethod("configureLayoutCache");
      method.setAccessible(true);
      method.invoke(ui);
    }
    catch (Exception e1)
    {
     e1.printStackTrace();
    }

  } // toggleCheckBox

暫無
暫無

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

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