Java 如何自定义JComboBox,使弹出窗口成为JTree(而不是列表)?

Java 如何自定义JComboBox,使弹出窗口成为JTree(而不是列表)?,java,swing,popup,jtree,jcombobox,Java,Swing,Popup,Jtree,Jcombobox,我正在尝试创建一个组合框,这样我就可以在弹出窗口中放置我喜欢的任何控件,在我的特定情况下是JTree。看看JComboBox是如何实现的,弹出窗口实际上是由UI委托创建的。改变的问题是,它需要为每个外观重新实现,这是我不想做的 我基本上想要一个组件,它具有JComboBox的外观(在当前的外观中),弹出窗口是JTree(在当前的外观中) 最简单的方法是什么?JComboBox本身无法完成您想要的任务。如果您完全坚持让它像JComboBox一样工作的概念,那么您可以在单击时使JButton弹出一个

我正在尝试创建一个组合框,这样我就可以在弹出窗口中放置我喜欢的任何控件,在我的特定情况下是JTree。看看JComboBox是如何实现的,弹出窗口实际上是由UI委托创建的。改变的问题是,它需要为每个外观重新实现,这是我不想做的

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


最简单的方法是什么?

JComboBox本身无法完成您想要的任务。如果您完全坚持让它像JComboBox一样工作的概念,那么您可以在单击时使JButton弹出一个JPanel。然后JPanel可以包含任何您想要的内容(JTree,等等)。

您只需扩展BasicComboxUI,然后覆盖所需的方法,如

public static ComponentUI createUI( JComponent c) 

创建自定义ComboPopup需要您在不能使用BasicComboPopUp的地方投入一些精力,因为它扩展了JPOppMenu

public class BasicComboPopup extends JPopupMenu implements ComboPopup
因此,在您的情况下,您可能需要扩展JTree并实现ComboPopup

我怀疑“改变的问题是,它需要为每个外观部分重新实现”。我不认为会有重新实施的问题


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

使用按钮弹出一个带有JTree的JPanel是正确的。作为对Carcasi评论的回应,您可以使用自定义TableCellRenderer对其进行更改,使其看起来不像传统的按钮。

我尝试过制作类似于此的内容

起初,我试图按照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;
  }
}

如果您发现任何错误或对如何改进此代码有任何建议,我将非常感谢您的评论

进一步的网络研究表明,自称为“专业Java和Swing组件提供商”的人制作了一个名为的软件包,其中包括-的描述表明它可以做到这一点


不过,这是一个付费套餐,我还没有试过。。。如果有人用过这个,他们能评论一下这个体验吗?

我不确定我是否理解这个概念。当树弹出时,您是否能够从树中选择一个节点,并在组合框中显示该值?当树弹出时,是否已根据组合框中的值选择/展开树中的节点?或者这仅仅是一种鼠标点击显示一棵树的方式?第一种情况:树弹出,节点已经被选中(如果有的话),点击一个节点,JComboBox会触发一个ActionEvent,表示选择发生了变化,树消失了。事实上,我有一种方法可以通过鼠标点击来显示一棵树(使用flamingo组件套件中的JCommandButton)。是的,我现在就是这样做的。问题是我想把它嵌入到一个JTable中作为TableCellEditor,在里面有一个按钮是非常难看的…:-/
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;
  }
}