如何在Java中为JComboBox添加分隔符?

13

我有一个 JComboBox,希望在元素列表中添加分隔符。如何在Java中实现此功能?

这个功能在创建字体选择的组合框时很有用,类似于Word和Excel中的字体选择控件。在这种情况下,我想在顶部显示最常用的字体,然后是一个分隔符,最后是按字母顺序排列的所有字体家族。

有谁能帮我解决如何实现这一点,或者在Java中是否不可能实现?

4个回答

8

2
BlockComboBoxExample示例破坏了光标键和首字母键导航。Santhosh Kumar的示例似乎工作得更好。 - bobndrew

4
到我写下面的代码并进行测试时,你可能已经获得了更好的答案...... 我不介意,因为我享受这个实验/学习(在Swing方面还有点生疏)。
[编辑]三年后,我不再那么生疏,并考虑到bobndrew的有效评论。我对关键导航没有问题(也许是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);
      }
    });
  }
}

你的变量命名不是最糟糕的事情:你正在破坏整个组合框项目选择:选择不再被绘制,第一个字母键跳跃也被破坏了。你应该在最小的范围内定义m_combom_renderer(在public TestGui()中)。而且不应该为Renderer 'TestGui'类(它应该是一个孤独的Main类,而不是'JFrame'子类)使用全局的m_lastRecentIndex。但我喜欢大括号的风格! - bobndrew
@bobndrew:嘿!就像我写的一样,当时我还是个新手(已经三年了!)。除了你没有指出的那些错误之外,我可能使用了一些我找到的Swing测试模板,更糟糕的是,我没有使用SwingUtilities.invokeLater!目前我拥有的代码使用了它,至少(但仍然是JFrame子类…)。而m_lastRecentIndex更为本地化…但你大部分的批评都是针对快速制作的测试类,这不是真正的生产代码。今天,我会更加关注这样的代码,因为新手可以从中获得灵感… :-) 此外,你关于鼠标悬停元素和选择的突出显示是正确的。待办事项 - PhiLho
注意:我已经编辑了上面的代码,以获得更现代/正确的版本。我的方法的一个优点是它比JSeparator更灵活(至少在外观上),而且不占用插槽。 - PhiLho

2
你可以使用自定义的ListCellRenderer,以不同的方式绘制分隔符项。请参阅文档和一个小的教程

0

尝试添加此渲染器。只需提供您想要分隔符位于上方的索引值列表即可。

 private class SeperatorComboRenderer extends DefaultListCellRenderer
 {
      private final float SEPARATOR_THICKNESS = 1.0f;
      private final float SPACE_TOP = 2.0f;
      private final float SPACE_BOTTOM = 2.0f;
      private final Color SEPARATOR_COLOR = Color.DARK_GRAY;
    
      private final List<Integer> marks;
      private boolean mark;
      private boolean top;
    
      public SeperatorComboRenderer(List<Integer> marks)
      {
           this.marks = marks;
      }
    
      @Override
      public Component getListCellRendererComponent(JList list, Object object, int index, boolean isSelected, boolean hasFocus)
      {
           super.getListCellRendererComponent(list, object, index, isSelected, hasFocus);

           top = false;
           mark = false;
           marks.forEach((idx) ->
           {
               if(index - 1 == idx)
                    top = true;
               if(index == idx)
                    mark = true;
           });
           return this;
      }
    
      @Override
      protected void paintComponent(Graphics g)
      {
           if(mark)
                g.translate(0, (int)(SEPARATOR_THICKNESS + SPACE_BOTTOM));
           Graphics2D g2 = (Graphics2D)g;
           super.paintComponent(g);
           if(mark)
           {
                g2.setColor(SEPARATOR_COLOR);
                g2.setStroke(new BasicStroke(SEPARATOR_THICKNESS));
                g2.drawLine(0, 0, getWidth(), 0);
           }
      }
    
      @Override
      public Dimension getPreferredSize()
      {
           Dimension pf = super.getPreferredSize();
           double height = pf.getHeight();
           if(top) height += SPACE_TOP;
           else if(mark) height += SEPARATOR_THICKNESS + SPACE_BOTTOM;
           return new Dimension((int)pf.getWidth(), (int)height);
      }
  }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接