2011-03-08 19 views
8

Quiero que mi JComboBox a agrupar varias opciones a la vez, similar al HTML optgroup:¿Cuál es la oscilación equivalente a HTML <optgroup>

<select> 
<optgroup label="A"> 
    <option/> 
    <option/> 
</optgroup> 
</select> 

no pude encontrar ninguna solución para esto en Swing. Manipular el UI-Renderer para el Combobox parece ser una mala idea, ya que es OS & L & F-dependent (y son privados, por lo que no pueden extenderse).

Respuesta

12

Considérese la siguiente implementación como una guía básica cómo aplicar el estilo a medida y crear elementos no seleccionables:

public class ExtendedComboBox extends JComboBox { 

    public ExtendedComboBox() { 
     setModel(new ExtendedComboBoxModel()); 
     setRenderer(new ExtendedListCellRenderer()); 
    } 

    public void addDelimiter(String text) { 
     this.addItem(new Delimiter(text)); 
    } 

    private static class ExtendedComboBoxModel extends DefaultComboBoxModel { 
     @Override 
     public void setSelectedItem(Object anObject) { 
      if (!(anObject instanceof Delimiter)) { 
       super.setSelectedItem(anObject); 
      } else { 
       int index = getIndexOf(anObject); 
       if (index < getSize()) { 
        setSelectedItem(getElementAt(index+1)); 
       } 
      } 
     } 

    } 

    private static class ExtendedListCellRenderer 
        extends DefaultListCellRenderer { 

     @Override 
     public Component getListCellRendererComponent(JList list, Object value, 
         int index, boolean isSelected, boolean cellHasFocus) { 
      if (!(value instanceof Delimiter)) { 
       return super.getListCellRendererComponent(list, value, index, 
         isSelected, cellHasFocus); 
      } else { 
       JLabel label = new JLabel(value.toString()); 
       Font f = label.getFont(); 
       label.setFont(f.deriveFont(f.getStyle() 
          | Font.BOLD | Font.ITALIC)); 
       return label; 
      } 
     } 
    } 

    private static class Delimiter { 
     private String text; 

     private Delimiter(String text) { 
      this.text = text; 
     } 

     @Override 
     public String toString() { 
      return text.toString(); 
     } 
    } 
} 
+0

Sí, este ejemplo hace casi exactamente lo que he pedido, lo tengo funcionando. Muchas gracias. –

+1

Esto me puso en el camino correcto también, pero me gustaría entender cómo anular DefaultListSelectionModel en lugar de DefaultComboBoxModel, para evitar que se seleccione el Delimitador. He utilizado GlazedLists para crear EventList y SeparatorList en función de EventList, y estoy creando una JList utilizando DefaultEventListModel como modelo (que pasa en mi SeparatorList) y un CellRenderer personalizado. Todo se muestra muy bien, ahora solo tengo que evitar la selección de las celdas del Separador ... El DefaultListSelectionModel es bastante diferente del DefaultComboBoxModel ... – JohnRDOrazio

5

No creo que haya una forma simple de hacerlo, pero hay una manera de hacerlo.

Implementaré una clase de modelo de datos que indique la agrupación que ha descrito anteriormente. Coloque instancias de esos modelos de datos en su instancia de implementación javax.swing.ComboBoxModel.

A continuación, puede implementar un javax.swing.ListCellRenderer para formatear la salida como desee con sangrías para los datos de texto. Es posible que desee extender el javax.swing.DefaultListCellRenderer o posiblemente pedir prestado su implementación al por mayor de la fuente de Java.

En cuanto a la L & F debe ser capaz de mantenerse dentro de las pautas normales mediante el uso de los métodos anteriores y no tendrá que luchar con averiguar cómo implementarlo. Mire los componentes por defecto de Swing que proporcionarán mucha información sobre cómo lidiar con L & F.

Además, creo que hay mecanismos (tendrá que perdonarme, han pasado AÑOS desde que completado el desarrollo de Swing) para permitirle determinar si un elemento es seleccionable o no.

+0

+1 por sugerir un modelo personalizado. – trashgod

2

me faltaba esto mismo hoy, y he pasado el día averiguando cosas que juntan las piezas juntos para implementar un modelo similar con un JList en lugar de con el JComboBox sugerido. Finalmente encontré una solución usando GlazedLists EventList y SeparatorList con el DefaultEventListModel correspondiente. Anulo el CellRenderer y el DefaultListSelectionModel. Al final he publicado mi propia respuesta a mi propia pregunta sobre esto: How to prevent selection of SeparatorList.Separator in a JList?

Aquí está mi código de trabajo final:

public class MyFrame extends javax.swing.JFrame { 
    private final EventList<BibleVersion> bibleVersions; 
    private final SeparatorList<BibleVersion> versionsByLang; 
    private boolean[] enabledFlags; 

    public MyFrame(){ 
     bibleVersions = new BasicEventList<>(); 
     bibleVersions.add(new BibleVersion("CEI2008", "Testo della Conferenza Episcopale Italiana", "2008", "Italian")); 
     bibleVersions.add(new BibleVersion("LUZZI", "Diodati Nuova Riveduta - Luzzi", "1927", "Italian")); 
     bibleVersions.add(new BibleVersion("NVBSE", "Nova Vulgata - Bibliorum Sacrorum Editio", "1979", "Latin")); 
     bibleVersions.add(new BibleVersion("NABRE", "New American Bible - Revised Edition", "2011", "English")); 
     bibleVersions.add(new BibleVersion("KJV", "King James Version", "1611", "English")); 
     versionsByLang = new SeparatorList<>(bibleVersions, new VersionComparator(),1, 1000); 
     int listLength = versionsByLang.size(); 
     enabledFlags = new boolean[listLength]; 

     ListIterator itr = versionsByLang.listIterator(); 
     while(itr.hasNext()){ 
      enabledFlags[itr.nextIndex()] = !(itr.next().getClass().getSimpleName().equals("GroupSeparator")); 
     } 
     jList = new javax.swing.JList(); 
     jList.setModel(new DefaultEventListModel<>(versionsByLang)); 
     jList.setCellRenderer(new VersionCellRenderer()); 
     jList.setSelectionModel(new DisabledItemSelectionModel()); 
     ListSelectionModel listSelectionModel = jList.getSelectionModel(); 
     listSelectionModel.addListSelectionListener(new SharedListSelectionHandler()); 

    } 

    public static class BibleVersion { 
     private String abbrev; 
     private String fullname; 
     private String year; 
     private String lang; 

     public BibleVersion(String abbrev, String fullname, String year, String lang) { 
      this.abbrev = abbrev; 
      this.fullname = fullname; 
      this.year = year; 
      this.lang = lang; 
     }   

     public String getAbbrev() { 
      return abbrev; 
     } 

     public void setAbbrev(String abbrev) { 
      this.abbrev = abbrev; 
     } 

     public String getFullname() { 
      return fullname; 
     } 

     public void setFullname(String fullname) { 
      this.fullname = fullname; 
     } 

     public String getYear() { 
      return year; 
     } 

     public void setYear(String year) { 
      this.year = year; 
     } 

     public String getLang() { 
      return lang; 
     } 

     public void setLang(String lang) { 
      this.lang = lang; 
     } 

     @Override 
     public String toString() { 
      return this.getAbbrev() + " — " + this.getFullname() + " (" + this.getYear() + ")"; //To change body of generated methods, choose Tools | Templates. 
     }     

    } 

    private static class VersionComparator implements Comparator<BibleVersion> { 

     @Override 
     public int compare(BibleVersion o1, BibleVersion o2) { 
      return o1.getLang().compareTo(o2.getLang()); 
     }    

    } 

    private static class VersionCellRenderer extends DefaultListCellRenderer{ 

     @Override 
     public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { 
      JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); 

      if (value instanceof SeparatorList.Separator) { 
       SeparatorList.Separator separator = (SeparatorList.Separator) value; 
       BibleVersion bibleversion = (BibleVersion)separator.getGroup().get(0); 
       String lbl = "-- " + bibleversion.getLang() + " --"; 
       label.setText(lbl); 
       label.setFont(label.getFont().deriveFont(Font.BOLD)); 
       label.setBackground(Color.decode("#004400")); 
       label.setBorder(BorderFactory.createEmptyBorder(0,5,0,0)); 
       label.setEnabled(false); 
      } else { 
       label.setFont(label.getFont().deriveFont(Font.PLAIN)); 
       label.setBorder(BorderFactory.createEmptyBorder(0,15,0,0)); 
      } 

      return label; 
     } 
    } 

private class DisabledItemSelectionModel extends DefaultListSelectionModel { 

    private static final long serialVersionUID = 1L; 

    @Override 
    public void setSelectionInterval(int index0, int index1) { 
     if(index0 < index1){ 
      for (int i = index0; i <= index1; i++){ 
       if(enabledFlags[i]){ 
        super.addSelectionInterval(i, i); 
       } 
      } 
     } 
     else if(index1 < index0){ 
      for (int i = index1; i <= index0; i++){ 
       if(enabledFlags[i]){ 
        super.addSelectionInterval(i, i); 
       } 
      } 
     } 
     else if(index0 == index1){ 
      if(enabledFlags[index0]){ super.setSelectionInterval(index0,index0); } 
     } 
    } 

    @Override 
    public void addSelectionInterval(int index0, int index1) { 
     if(index0 < index1){ 
      for (int i = index0; i <= index1; i++){ 
       if(enabledFlags[i]){ 
        super.addSelectionInterval(i, i); 
       } 
      } 
     } 
     else if(index1 < index0){ 
      for (int i = index1; i <= index0; i++){ 
       if(enabledFlags[i]){ 
        super.addSelectionInterval(i, i); 
       } 
      } 
     } 
     else if(index0 == index1){ 
      if(enabledFlags[index0]){ super.addSelectionInterval(index0,index0); } 
     } 
    }   

} 

private class SharedListSelectionHandler implements ListSelectionListener { 
    @Override 
    public void valueChanged(ListSelectionEvent e) { 
     ListSelectionModel lsm = (ListSelectionModel)e.getSource(); 
     StringBuilder output = new StringBuilder(); 
     int firstIndex = e.getFirstIndex(); 
     int lastIndex = e.getLastIndex(); 
     boolean isAdjusting = e.getValueIsAdjusting(); 
     output.append("Event for indexes "); 
     output.append(firstIndex); 
     output.append(" - "); 
     output.append(lastIndex); 
     output.append("; isAdjusting is "); 
     output.append(isAdjusting); 
     output.append("; selected indexes:"); 

     if (lsm.isSelectionEmpty()) { 
      output.append(" <none>"); 
     } else { 
      // Find out which indexes are selected. 
      int minIndex = lsm.getMinSelectionIndex(); 
      int maxIndex = lsm.getMaxSelectionIndex(); 
      for (int i = minIndex; i <= maxIndex; i++) { 
       if (lsm.isSelectedIndex(i)) { 
        output.append(" "); 
        output.append(i); 
       } 
      } 
     } 
     output.append(System.getProperty("line.separator")); 
     System.out.println(output.toString()); 
    } 
} 


} 
Cuestiones relacionadas