2011-03-08 1 views
8

Je veux que mon JComboBox de regrouper plusieurs options ensemble, semblable au HTML optgroup:Quel est le Swing-équivalent à HTML <optgroup>

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

Je ne pouvais pas trouver une solution pour cela dans Swing. Manipuler le UI-Renderer pour le Combobox semble être une mauvaise idée, car il est OS & L & F-dépendante (et ils sont privés, donc ne peut pas étendre).

Répondre

12

Tenir compte de la mise en œuvre suivant comme guide de base comment appliquer un style personnalisé et créer des éléments non sélectionnables:

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

Oui cet exemple fait presque exactement ce que j'ai demandé, je l'ai fonctionné. Merci beaucoup. –

+1

Cela m'a également permis d'être sur la bonne voie, mais je voudrais comprendre comment remplacer DefaultListSelectionModel au lieu de DefaultComboBoxModel, pour empêcher le délimiteur d'être sélectionné. J'ai utilisé GlazedLists pour créer une EventList et une SeparatorList basée sur la EventList, et je crée un JList en utilisant le DefaultEventListModel comme modèle (passant dans ma SeparatorList) et un CellRenderer personnalisé. Tout affiche bien, maintenant je dois juste empêcher la sélection des cellules Separator ... Le DefaultListSelectionModel est assez différent du DefaultComboBoxModel ... – JohnRDOrazio

5

Je ne crois pas qu'il existe une façon simple de le faire, mais il y a un moyen de le faire.

Je voudrais implémenter une classe de modèle de données qui indique le regroupement que vous avez décrit ci-dessus. Placez les instances de ces modèles de données dans votre instance d'implémentation javax.swing.ComboBoxModel.

Vous pouvez ensuite implémenter un javax.swing.ListCellRenderer pour formater la sortie comme vous le souhaitez avec des indentations pour les données de texte. Vous pouvez simplement étendre le javax.swing.DefaultListCellRenderer ou éventuellement emprunter son implémentation en gros à partir de la source Java. En ce qui concerne le L & F, vous devriez être en mesure de rester dans les lignes directrices normales en utilisant les méthodes ci-dessus et vous n'aurez pas à vous battre pour trouver comment l'implémenter. Regardez les composants Swing par défaut, ils fourniront beaucoup de perspicacité dans la façon de traiter L & F.

En outre, je pense qu'il existe des mécanismes (vous aurez à me pardonner, ça fait des ANNÉES depuis que j'ai fait plein développement de Swing) pour vous permettre de déterminer si un élément est sélectionnable ou non.

+0

+1 pour suggérer un modèle personnalisé. – trashgod

7

Vous pouvez le faire dans un moteur de rendu personnalisé, comme indiqué dans How to Use Combo Boxes: Providing a Custom Renderer.

+0

Grande suggestion aussi bien. J'ai oublié le Tutoriel Java (http://download.oracle.com/javase/tutorial/) –

+0

Oui, ce tutoriel m'a beaucoup aidé, Merci. –

2

je voulais moi-même aujourd'hui, et je l'ai passé la journée à figurer dehors des choses piéçage ensemble pour implémenter un modèle similaire avec un JList plutôt qu'avec le JComboBox suggéré. J'ai finalement trouvé une solution en utilisant GlazedLists EventList et SeparatorList avec le DefaultEventListModel correspondant. Je remplace le CellRenderer et le DefaultListSelectionModel. En fin de compte j'ai posté ma réponse à ma propre question à ce sujet: How to prevent selection of SeparatorList.Separator in a JList?

Voici mon code de travail 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()); 
    } 
} 


} 
Questions connexes