如何自定义 JComboBox 以便弹出窗口是 JTree(而不是列表)?

问题描述 投票:0回答:6

我正在尝试创建一个组合框,以便我可以在弹出窗口中放置我喜欢的任何控件,在我的具体情况下是 JTree。看看 JComboBox 是如何实现的,弹出窗口实际上是由 UI 委托创建的。改变这一点的问题在于,它需要针对每种外观和感觉重新实现,这是我不想做的事情......

我基本上想要一个组件,它具有 JComboBox 的外观和感觉(在当前的外观和感觉中),并且弹出窗口是 JTree(在当前的外观和感觉中)。

最简单的方法是什么?

java swing popup jtree jcombobox
6个回答
2
投票

JComboBox 本身并不能做你想做的事。如果您绝对坚持让它像 JComboBox 一样工作的概念,您可以使 JButton 在单击时弹出 JPanel。然后 JPanel 中可以包含您想要的任何内容(JTree 等)。


1
投票

我已经尝试过制作一些可以做这样的事情的东西。

起初,我尝试按照 Varun 建议的方式实现一些东西,但事实证明它有点混乱,当我开始使用 ComponentUI 对象时我有点紧张(我宁愿把这类事情留给L&F)。如果有人有这样做的好例子,我有兴趣看看。

然后我尝试了按钮方法......并认为我会与 SO 社区分享代码:

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
import javax.swing.plaf.basic.BasicInternalFrameUI;
import javax.swing.plaf.metal.MetalComboBoxIcon;

public class MockJComboBox extends JPanel  {

  private boolean _isPoppedUp = false;

  public MockJComboBox(String label, final JComponent toShow) {
    setLayout(new BorderLayout());
    JLabel jLabel = new JLabel(label);
    jLabel.setBackground(Color.WHITE);
    add(jLabel, BorderLayout.CENTER);
    Icon icon = new MetalComboBoxIcon();

    final JInternalFrame popup = new JInternalFrame(null, false, false, false, false);
    final JPanel panel = new JPanel(new BorderLayout());
    panel.add(new JScrollPane(toShow), BorderLayout.CENTER);
    if(!(System.getProperty("os.name").startsWith("Mac OS"))){
      BasicInternalFrameUI ui = (BasicInternalFrameUI) popup.getUI();
      ui.getNorthPane().setPreferredSize(new Dimension(0,0));
    }
    popup.setBorder(null);
    popup.setContentPane(panel);
    popup.pack();
    popup.setVisible(true);

    final JButton dropDownButton = new JButton(icon);
    dropDownButton.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        _isPoppedUp = !_isPoppedUp;
        Container parent = getParent();
        if (_isPoppedUp) {
          popup.setLocation(panel.getX(), panel.getY() + panel.getHeight());
          popup.setSize(panel.getWidth(), toShow.getHeight());
          parent.add(popup);
        } else {
          parent.remove(popup);
          parent.repaint();
        }
      }
    });
    add(dropDownButton, BorderLayout.EAST);
  }

  public boolean isPoppedUp() {
    return _isPoppedUp;
  }
}

如果您发现任何错误或对如何改进此代码有建议,我将不胜感激您的评论!


0
投票

您只需要扩展 BasicComboBoxUI,然后重写所需的方法,例如

public static ComponentUI createUI( JComponent c) 

protected ComboPopup createPopup()

创建自定义 ComboPopup 需要您在无法使用 BasicComboPopUp 的地方付出一些努力,因为它扩展了 JPopUpMenu

public class BasicComboPopup extends JPopupMenu implements ComboPopup

因此,在您的情况下,您可能希望扩展 JTree 并实现 ComboPopup。

我对“改变这一点的问题是需要为每个外观和感觉重新实现”部分表示怀疑。我认为重新实施不会有问题。

BasicComboPopup 在不同的外观和感觉中看起来有所不同,因为它是一个 JPopupMenu,而 JPopupMenu 又将具有 UI 委托。因此,如果您只是扩展 JTree,您不应该遇到不同外观和感觉的问题。


0
投票

使用弹出带有 JTree 的 JPanel 的按钮的答案是正确的。 针对 Carcassi 的评论,您可以使用自定义 TableCellRenderer 来更改它,使其看起来不像传统按钮。


0
投票

进一步的网络研究显示,自称为“专业 Java 和 Swing 组件提供商”的 Jidesoft 生产了一个名为 JIDE Grids 的包,其中包括 AbstractComboBox - 其描述表明它可以做到这一点。

不过,这是付费套餐,我没有尝试过…如果有人用过的话,可以评价一下使用体验吗?


0
投票

如果您只想自定义弹出窗口中项目的外观,那么使用

JComboBox.setRenderer
并实现
ListCellRenderer
(或扩展
DefaultListCellRenderer
,这是一个
JLabel
)非常容易。

但是,

ListCellRenderer
提供的组件仅用于绘画弹出列表。它不能是交互式的。例如,它不可能是整个
JTree

您想要的树与常规列表并没有真正的一百万英里不同。您可以自定义渲染器以在每个项目上绘制树线(快速而肮脏的方法:只需将“└─”字符放在文本前面)。通过这种方式您不会获得真正的树组件,因此没有可点击的+/-。但它的实施速度比其他替代方案要快得多。

© www.soinside.com 2019 - 2024. All rights reserved.