Как настроить JComboBox, чтобы всплывающее окно представляло собой JTree (а не список)?

Я пытаюсь создать поле со списком, чтобы я мог поместить любой элемент управления во всплывающем окне, в моем конкретном случае, JTree. Посмотрев, как реализован JComboBox, всплывающее окно действительно создается делегатом пользовательского интерфейса. Проблема в изменении заключается в том, что его нужно будет повторно реализовать для каждого внешнего вида, что я не хочу делать...

I basically want a component that it has the look and feel of a JComboBox (in the current look and feel) and the popup is a JTree (in the current look and feel).

Какой самый простой способ сделать это?

5 ответов

Сам JComboBox не может делать то, что вы хотите. Если вы абсолютно привержены идее заставить его работать как JComboBox, вы можете заставить JButton открывать JPanel при нажатии. Тогда внутри JPanel может быть все, что вы захотите (JTree, и так далее).

Я попытался сделать что-то подобное.

Сначала я попытался реализовать что-то в соответствии с рекомендациями Варуна, но это оказалось немного грязным, и я немного нервничаю, когда начинаю играть с объектами 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;
  }
}

Если вы обнаружите какие-либо ошибки или у вас есть предложения по улучшению этого кода, я буду благодарен за ваши комментарии!

Ответ на использование кнопки, которая открывает JPanel с JTree, является правильным. В ответ на комментарий Каркасси вы можете использовать пользовательский TableCellRenderer, чтобы изменить его так, чтобы он не выглядел как традиционная кнопка.

Дальнейшие веб-исследования показали, что Jidesoft, которые описывают себя как "профессиональный поставщик компонентов Java и Swing", создают пакет под названием JIDE Grids, который включает в себя AbstractComboBox - описание, которое предполагает, что это будет сделано.

Тем не менее, это платный пакет, и я не пробовал... Если кто-то использовал это, они могли бы прокомментировать опыт?

Вам просто нужно расширить BasicComboBoxUI, а затем переопределить необходимые методы, такие как

public static ComponentUI createUI( JComponent c) 

а также

protected ComboPopup createPopup()

Создание собственного ComboPopup потребует от вас усилий, когда вы не сможете использовать BasicComboPopUp, поскольку он расширяет JPopUpMenu.

public class BasicComboPopup extends JPopupMenu implements ComboPopup

Так что в вашем случае вы можете расширить JTree и реализовать ComboPopup.

Я сомневаюсь в том, что "проблема в изменении заключается в том, что его нужно будет повторно реализовать для каждого внешнего вида". Я не думаю, что будет проблема повторной реализации.

BasicComboPopup по-разному выглядит и выглядит по-другому, потому что это JPopupMenu, который, в свою очередь, будет иметь делегаты пользовательского интерфейса. Так что если вы просто расширяете JTree, у вас не должно быть проблем с другим внешним видом и ощущениями.

Другие вопросы по тегам