Как добавить разделитель к JComboBox в Java?

У меня есть JComboBox, и я хотел бы иметь разделитель в списке элементов. Как мне это сделать на Java?

Пример сценария, в котором это может пригодиться, - создание поля со списком для выбора семейства шрифтов; аналогично элементу управления font-family-selection-control в Word и Excel. В этом случае я хотел бы показать наиболее часто используемые шрифты вверху, затем разделитель и, наконец, все семейства шрифтов под разделителем в алфавитном порядке.

Может ли кто-нибудь помочь мне с тем, как это сделать, или это невозможно на Java?

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
11
0
10 605
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Вы можете использовать собственный ListCellRenderer, который будет рисовать разделители по-разному. См. документы и небольшой руководство.

Ответ принят как подходящий

Существует довольно короткое руководство с примером, показывающим, как использовать настраиваемый ListCellRenderer на java2s. http://www.java2s.com/Code/Java/Swing-Components/BlockComboBoxExample.htm

В основном это включает в себя вставку известного заполнителя в вашу модель списка, и когда вы обнаруживаете заполнитель в ListCellRenderer, вы возвращаете экземпляр 'new JSeparator (JSeparator.HORIZONTAL)'

Пример BlockComboBoxExample нарушает навигацию с помощью клавиши курсора и клавиши с первой буквой. пример Сантоша Кумара, кажется, работает лучше.

bobndrew 07.10.2011 19:37

К тому времени, как я написал и протестировал приведенный ниже код, вы, вероятно, получили много лучших ответов ...
Я не возражаю, так как мне понравился эксперимент / обучение (все еще немного зеленый на фронте Swing).

[РЕДАКТИРОВАТЬ] Три года спустя я стал немного менее зеленым, и я принял во внимание действительные замечания бобндрю. У меня нет проблем с клавиатурой, которая просто работает (возможно, это проблема версии JVM?). Тем не менее, я улучшил рендерер, чтобы отображать подсветку. И я использую лучший демонстрационный код. Принятый ответ, вероятно, лучше (более стандартный), мой, вероятно, более гибкий, если вам нужен настраиваемый разделитель ...

Основная идея - использовать средство визуализации для элементов поля со списком. Для большинства элементов это простая метка JLabel с текстом элемента. Для последнего недавнего / наиболее часто используемого элемента я украшаю JLabel настраиваемой рамкой, рисуя линию внизу.

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


@SuppressWarnings("serial")
public class TwoPartsComboBox extends JComboBox
{
  private int m_lastFirstPartIndex;

  public TwoPartsComboBox(String[] itemsFirstPart, String[] itemsSecondPart)
  {
    super(itemsFirstPart);
    m_lastFirstPartIndex = itemsFirstPart.length - 1;
    for (int i = 0; i < itemsSecondPart.length; i++)
    {
      insertItemAt(itemsSecondPart[i], i);
    }

    setRenderer(new JLRenderer());
  }

  protected class JLRenderer extends JLabel implements ListCellRenderer
  {
    private JLabel m_lastFirstPart;

    public JLRenderer()
    {
      m_lastFirstPart = new JLabel();
      m_lastFirstPart.setBorder(new BottomLineBorder());
//      m_lastFirstPart.setBorder(new BottomLineBorder(10, Color.BLUE));
    }

    @Override
    public Component getListCellRendererComponent(
        JList list,
        Object value,
        int index,
        boolean isSelected,
        boolean cellHasFocus)
    {
      if (value == null)
      {
        value = "Select an option";
      }
      JLabel label = this;
      if (index == m_lastFirstPartIndex)
      {
        label = m_lastFirstPart;
      }
      label.setText(value.toString());
      label.setBackground(isSelected ? list.getSelectionBackground() : list.getBackground());
      label.setForeground(isSelected ? list.getSelectionForeground() : list.getForeground());
      label.setOpaque(true);

      return label;
    }
  }
}

Разделитель класса, может быть толстым, с нестандартным цветом и т. д.

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

/**
 * Draws a line at the bottom only.
 * Useful for making a separator in combo box, for example.
 */
@SuppressWarnings("serial")
class BottomLineBorder extends AbstractBorder
{
  private int m_thickness;
  private Color m_color;

  BottomLineBorder()
  {
    this(1, Color.BLACK);
  }

  BottomLineBorder(Color color)
  {
    this(1, color);
  }

  BottomLineBorder(int thickness, Color color)
  {
    m_thickness = thickness;
    m_color = color;
  }

  @Override
  public void paintBorder(Component c, Graphics g,
      int x, int y, int width, int height)
  {
    Graphics copy = g.create();
    if (copy != null)
    {
      try
      {
        copy.translate(x, y);
        copy.setColor(m_color);
        copy.fillRect(0, height - m_thickness, width - 1, height - 1);
      }
      finally
      {
        copy.dispose();
      }
    }
  }

  @Override
  public boolean isBorderOpaque()
  {
    return true;
  }
  @Override
  public Insets getBorderInsets(Component c)
  {
    return new Insets(0, 0, m_thickness, 0);
  }
  @Override
  public Insets getBorderInsets(Component c, Insets i)
  {
    i.left = i.top = i.right = 0;
    i.bottom = m_thickness;
    return i;
  }
}

Тестовый класс:

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


@SuppressWarnings("serial")
public class TwoPartsComboBoxDemo extends JFrame
{
  private TwoPartsComboBox m_combo;

  public TwoPartsComboBoxDemo()
  {
    Container cont = getContentPane();
    cont.setLayout(new FlowLayout());

    cont.add(new JLabel("Data: ")) ;

    String[] itemsRecent = new String[] { "ichi", "ni", "san" };
    String[] itemsOther = new String[] { "one", "two", "three" };
    m_combo = new TwoPartsComboBox(itemsRecent, itemsOther);

    m_combo.setSelectedIndex(-1);
    cont.add(m_combo);
    m_combo.addActionListener(new ActionListener()
    {
      public void actionPerformed(ActionEvent ae)
      {
        String si = (String) m_combo.getSelectedItem();
        System.out.println(si == null ? "No item selected" : si.toString());
      }
    });

    // Reference, to check we have similar behavior to standard combo
    JComboBox combo = new JComboBox(itemsRecent);
    cont.add(combo);
  }

  /**
   * Start the demo.
   *
   * @param args   the command line arguments
   */
  public static void main(String[] args)
  {
    // turn bold fonts off in metal
    UIManager.put("swing.boldMetal", Boolean.FALSE);

    SwingUtilities.invokeLater(new Runnable()
    {
      public void run()
      {
        JFrame demoFrame = new TwoPartsComboBoxDemo();
        demoFrame.setTitle("Test GUI");
        demoFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        demoFrame.setSize(400, 100);
        demoFrame.setVisible(true);
      }
    });
  }
}

Имена переменных здесь не самое худшее: вы нарушаете весь combobox-item-selection: выделение больше не окрашивается, а переходы по клавишам с первой буквой прерываются. И вы должны определить m_combo и m_renderer в минимально возможном объеме (в public TestGui()). И не следует использовать глобальный m_lastRecentIndex для Renderer и класса TestGui (который должен быть одиноким классом Main, а не подклассом JFrame). Но мне нравятся брекеты!

bobndrew 07.10.2011 19:30

@bobndrew: привет! Как я уже писал, тогда я был новичком (уже 3 года!). Среди недостатков, которые вы не укажете, я, вероятно, использовал какой-то шаблон теста Swing, который я нашел, и, что еще хуже, я не использовал SwingUtilities.invokeLater! Код, который у меня в настоящее время используется, по крайней мере (но это все еще подкласс JFrame ...). И m_lastRecentIndex более локален ... Но большая часть вашей критики касается быстро созданного тестового класса, который на самом деле не является производственным кодом. Сегодня я уделяю больше внимания такому коду, так как новички могут черпать из него вдохновение ... :-) Кроме того, вы правы, говоря о выделении зависших элементов и выделении. СДЕЛАТЬ

PhiLho 07.10.2011 20:17

Примечание: я отредактировал приведенный выше код для более современной / правильной версии. Одним из преимуществ моего подхода является то, что он более гибкий (по крайней мере, на вид), чем JSeparator, и не занимает места.

PhiLho 19.01.2012 13:56

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