2008-09-26 15 views
10

Mam JComboBox i chciałbym mieć separator na liście elementów. Jak to zrobić w Javie?Jak dodać separator do JComboBox w Javie?

Przykładowy scenariusz, w którym przydałoby się to podczas tworzenia combobox do wyboru czcionki-rodziny; podobne do sterowania wyborem czcionki-rodziny w programach Word i Excel. W tym przypadku chciałbym pokazać najczęściej używane czcionki u góry, a następnie separator i wreszcie wszystkie rodziny czcionek poniżej separatora w kolejności alfabetycznej.

Czy ktoś może mi pomóc, jak to zrobić, czy jest to niemożliwe w Javie?

Odpowiedz

7

Jest to dość krótki poradnik z przykładem, który pokazuje, jak używać niestandardowych ListCellRenderer na java2s http://www.java2s.com/Code/Java/Swing-Components/BlockComboBoxExample.htm

Zasadniczo polega na wprowadzeniu znanej zastępczy na liście modelu i jeśli wykryje zastępczy w ListCellRenderer ciebie powrót wystąpienie „nowej JSeparator (JSeparator.HORIZONTAL)”

+2

Opcja 'przykład BlockComboBoxExample' łamie kursora klucz i za pierwszym nawigacja za pomocą klawiszy literowych. Santhosh Kumar [przykład] (http://www.jroller.com/santhosh/entry/jcombobox%5Fitems%5Fwith%5Fseparators) wydaje się działać lepiej. – bobndrew

2

Można użyć niestandardowego ListCellRenderer, który będzie rysował elementy separatora w różny sposób. Zobacz docs i mały tutorial.

4

Kiedy pisałem i przetestowany kod poniżej, prawdopodobnie dostał dużo lepsze odpowiedzi ...
nie przeszkadza mi jak podobał mi się eksperyment/learning (nadal nieco zielony na froncie Swinga).

[EDIT] Trzy lata później jestem nieco mniej zielony i wziąłem pod uwagę ważne uwagi bobndrew. Nie mam problemu z kluczową nawigacją, która działa (może to był problem z wersją JVM?). Jednak ulepszyłem renderer, aby podkreślić jego atrakcyjność. Używam lepszego kodu demo. Przyjęta odpowiedź jest prawdopodobnie lepsza (bardziej standardowa), moja jest prawdopodobnie bardziej elastyczna, jeśli chcesz niestandardowego separatora ...

Podstawową ideą jest użycie renderera dla elementów pola kombi. W przypadku większości produktów jest to prosty JLabel z tekstem elementu. W przypadku ostatniego ostatnio używanego elementu ozdobiam JLabel niestandardową ramką, rysując linię na jej spodzie.

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

klasa Separator może być gruba, z niestandardowego koloru itp

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

klasa Test:

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); 
     } 
    }); 
    } 
} 
+0

Twoje zmienne nazewnictwo nie jest najgorsze: przełamujesz cały wybór combobox-item: wybór nie jest już malowany, a skoki z pierwszą literą są zerwane. I powinieneś zdefiniować 'm_combo' i' m_renderer' w najmniejszym możliwym zakresie (w 'public TestGui()'). I nie powinien używać glooooobal 'm_lastRecentIndex' dla Renderer ** i ** klasy 'TestGui' (która powinna być samotną klasą Main, a nie podklasą JFrame). Ale podoba mi się styl szelek! – bobndrew

+0

@bobndrew: hej! Jak napisałem, byłem wtedy początkującym (3 lata!). Pośród grzechów, na które nie zwracasz uwagi, prawdopodobnie użyłem jakiegoś szablonu testu Swinga, a nawet gorzej, nie użyłem SwingUtilities.invokeLater! Kod, który mam aktualnie używa, przynajmniej (ale wciąż jest podklasą JFrame ...). A m_lastRecentIndex jest bardziej lokalny ... Ale większość twoich krytyków dotyczy szybko wykonanej klasy testowej, która nie jest tak naprawdę kodem produkcyjnym.Dziś zwracam większą uwagę na taki kod, ponieważ nowicjusze mogą czerpać z tego inspirację ... :-) Masz również rację podświetlając ukryte elementy i selekcje. TODO – PhiLho

+0

Uwaga: Poprawiłem powyższy kod, aby uzyskać bardziej nowoczesną/poprawną wersję. Zaletą mojego podejścia jest to, że jest bardziej elastyczny (przynajmniej w spojrzeniu) niż JSeparator i nie zajmuje miejsca. – PhiLho