tags:

views:

1927

answers:

4

I have a JComboBox and would like to have a separator in the list of elements. Is this possible at all in Java?

A sample scenario where this would come in handy is when making a combobox for font-family-selection; similar to the font-family-selection-control in Word and Excel. In this case I would like to show the most-used-fonts at the top, then a separator and finally all font-families below the separator in alphabetical order.

Can anyone help me with how to do this or is this not possible in Java?

+2  A: 

You can use a custom ListCellRenderer which would draw the separator items differently. See docs and a small tutorial.

macbirdie
+3  A: 

There is a pretty short tutorial with an example that shows how to use a custom ListCellRenderer on java2s http://www.java2s.com/Code/Java/Swing-Components/BlockComboBoxExample.htm

Basically it involves inserting a known placeholder in your list model and when you detect the placeholder in the ListCellRenderer you return an instance of 'new JSeparator(JSeparator.HORIZONTAL)'

pfranza
A: 

By the time I wrote and tested the code below, you probably got lot of better answers...
I don't mind as I enjoyed the experiment/learning (still a bit green on the Swing front).

The base idea is to use a renderer for the items of the combo box. For most items, it is a simple JLabel with the text of the item. For the last recent/most used item, I decorate the JLabel with a custom border drawing a line on its bottom.

The following code is still a bit rough, just a quick test of the concept. A base to improve upon.

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

public class TestGUI extends JFrame
{
   private JComboBox m_combo;
   private ListCellRenderer m_renderer;
   private int m_lastRecentIndex;

   public TestGUI()
   {
      setTitle("Test GUI");
      setSize(400, 100);
      Container cont = getContentPane();
      cont.setLayout(new FlowLayout());

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

      String[] itemsOther = new String[] { "one", "two", "three" };
      m_combo = new JComboBox(itemsOther);

      String[] itemsRecent = new String[] { "ichi", "ni", "san" };
      m_lastRecentIndex = itemsRecent.length - 1;
      for (int i = 0; i < itemsRecent.length; i++)
      {
         m_combo.insertItemAt(itemsRecent[i], i);
      }

      m_renderer = new JLRenderer();
      m_combo.setRenderer(m_renderer);
      m_combo.setSelectedIndex(-1);
      cont.add(m_combo);
      m_combo.addActionListener(new ActionListener()
      {
         public void actionPerformed(ActionEvent ae)
         {
            System.out.println(m_combo.getSelectedItem().toString());
         }
      });
   }

   class JLRenderer extends JLabel implements ListCellRenderer
   {
      JLabel m_lastRecent;

      public JLRenderer()
      {
         m_lastRecent = new JLabel();
         m_lastRecent.setBorder(new BottomLineBorder(Color.BLACK));
      }

      public Component getListCellRendererComponent(
            JList list,
            Object value,
            int index,
            boolean isSelected,
            boolean cellHasFocus)
      {
         if (value == null)
         {
            value = "Select an option";
         }
         if (index == m_lastRecentIndex)
         {
            m_lastRecent.setText(value.toString());
            return m_lastRecent;
         }
         setText(value.toString());

         return this;
      }

      class BottomLineBorder implements Border
      {
         private Color m_color;

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

         BottomLineBorder(Color color)
         {
            m_color = color;
         }

         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.drawLine(0, height - 1, width - 1, height - 1);
               }
               finally
               {
                  copy.dispose();
               }
            }
         }

         public boolean isBorderOpaque()
         {
            return true;
         }
         public Insets getBorderInsets(Component c)
         {
            return new Insets(0, 0, 1, 0);
         }
      }
   }

   public static void main(String[] args)
   {
      TestGUI gui = new TestGUI();
      gui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      gui.setVisible(true);
   }
}

Yes, I use non standard brace alignment and members/fields decoration... So sue me! ;-)

[EDIT] Just for fun, I also made a class for a thicker separation. I give it because it took me some time to find out I had to override the second getBorderInsets to declare the extra space it needs. So it might be useful to somebody else.

   class BottomThickLineBorder extends AbstractBorder
   {
      private int m_thickness;
      private Color m_color;

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

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

      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();
            }
         }
      }

      public boolean isBorderOpaque()
      {
         return true;
      }
      public Insets getBorderInsets(Component c, Insets i)
      {
         return new Insets(0, 0, m_thickness, 0);
      }
   }
PhiLho
C++ braces, traitor! ;)
macbirdie
PhiLho
+1  A: 

Another Solution: JComboBox mit Separators

Zubi