IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Composants Java Discussion :

Q: JTable dimensions variables et qques colonnes à JRadioButton - 2


Sujet :

Composants Java

  1. #1
    Membre régulier
    Profil pro
    Inscrit en
    Décembre 2008
    Messages
    263
    Détails du profil
    Informations personnelles :
    Âge : 73
    Localisation : Belgique

    Informations forums :
    Inscription : Décembre 2008
    Messages : 263
    Points : 121
    Points
    121
    Par défaut Q: JTable dimensions variables et qques colonnes à JRadioButton - 2
    Voir directement le résumé-simplification

    Une JTable aux dimensions modifiables dynamiquement et dont les cellules des rangées contiennent, dans quelques colonnes contigües, des objets non basiques (ici JRadioButton regroupés en rangées), cela devrait intéresser beaucoup de programmeurs en Java, il me semble.

    Dans un premier SSCCE se trouve le code le plus concis possible d'une JTable dont toutes les cellules contiennent un bouton-radio et où un seul bouton-radio à la fois peut effectivement être sélectionné dans chaque rangée; pour me prouver qu'avec une JTable aux dimensions statiques, ça fonctionne. Voir ma propre réponse à ma question dans http://www.developpez.net/forums/d15...roupes-rangee/

    Dans un deuxième SSCCE, je tente d'insérer dynamiquement quelques colonnes à JRadioButtons, entre deux colonnes aux données calculées : https://www.developpez.net/forums/d1...n/#post8898511
    Mon problème : Les colonnes et rangées sont bien ajoutées, mais les boutons-radios de leurs cellules ne sont pas activables. Les séquences d'invocation de méthodes par les classes JTable, JTableModel, AbstractTableModel, JRadioButton font apparaître un malfonctionnement (Séquences des méthodes 'getRowCount', 'getColumnCount', 'getCellRenderer', 'prepapreRenderer', 'getValueAt', 'setValueAt', 'propertyChange', 'getTableCellRendererComponent', 'getTableCellEditorComponent', 'getCellEditorValue', 'actionPerformed', ...).
    Apparamment, ce ne fut pas une bonne idée de tenter de découvrir d'où pourrait provenir l'erreur sur base du relevé des séquences de méthodes appelées, car je n'ai obtenu aucune réponse à ma question. Entretemps, le problème est resté le même.
    Je profite de l'occasion pour demander au gérant de cette discussion placée à un mauvais endroit de bien vouloir la déplacer vers 'Interfaces graphiques > AWT Swing > Tables'.

    Le SSCCE que j'envoie cette fois-ci est le plus compact possible, mais toutefois suffisant pour montrer la complexité d'insertion dynamique de colonnes de boutons-radios et de rangées, et se base sur les quatre fichiers sources (des mêmes classes TestTable (renomée ici 'XYExtensTable_SSCCE'), MonModTable, GestionnObjetsDUneRangée et MonObjet) identiquement à ceux de mon premier SSCCE.

    J'utilise un modèle de données propre ('MonModTable') héritant de 'AbstractTableModel' (non de 'DefaultTableModel') et générant ...
    - une List<GestionnObjetsDUneRangée> gestionnDonnéesTteLaTable (rangées). 'GestionnObjetsDUneRangée' elle-même n'est pas directement une List<Object> (colonnes), mais est une classe qui en contient une (List<Object> objetsDUneRangée).
    - et une List<String> libellésColonnes.
    À chaque ajout/retrait d'une colonne de boutons-radios, ces deux listes sont remises à jour par la méthode 'élaborTtsRangéesTable()'.
    Important : Dans cette application,
    - Une rangée est ajoutée chaque fois qu'une colonne (de boutons-radios) est ajoutée.
    - Le modèle de la table est reconçu intégralement à chaque ajout/retrait d'une colonne (et d'une rangée) et la table entière est repeinte. Il n'est donc pas question d'insérer une seule rangée dans une table existante, de modifier le contenu d'une seule cellule, etc., tel qu'expliqué p. ex. dans http://michelangemfwamba.blogspot.be...dynamique.html

    Le fonctionnement devrait être le suivant : À chaque actionnement du bouton-poussoir 'Ajout d'une colonne' (= boutAjoutUneColon dans 'XYExtensTable_SSCCE'), la méthode 'miseÀJourModèleEtStructTable(...)' (dans la classe 'XYExtensTable_SSCCE') est appelée, qui elle-même appelle, en premier lieu, 'élaborTtsRangéesTable()' (dans la classe 'MonModTable').
    - 'élaborTtsRangéesTable()' est en charge ...
    - - d'insérer le libellé de la nouvelle colonne (aux boutons-radios) ajoutée
    - - de composer une série de valeurs décimales factices et de les disposer en ArrayList (dans 'objetsDUneRangée' dans 'GestionnObjetsDUneRangée'), rangée par rangée, dans les cellules autres que celles des boutons-radios.
    - - de générer les boutons-radios ('monObjet') à disposer dans les places adéquates de l'ArrayList 'objetsDUneRangée'.
    - - fireTableStructureChanged();
    - 'miseÀJourModèleEtStructTable(...)' est en charge ...
    - - de déplacer la colonne pour qu'elle devienne l'avant-dernière

    Supposons, lors de l'ajout d'une colonne à boutons-radios, que les 'objetsDUneRangée' (dans 'GestionnObjetsDUneRangée') de chaque rangée reçoivent une nouvelle cellule (un booléen qui est rendu comme bouton-radio) et que 'libellésColonnes' se fait insérer un nouveau libellé. Il faut que la JTable réalise que son modèle a grandi en nombre de colonnes et en nombre de rangées. Je suppose que c'est le but même de 'fireTableStructureChanged();'.
    Par exemple au re-dessinage de la table est générée une séquence d'appels de méthodes (getColumnCount(), getRowCount, setValueAt(), ... que j'ai "@Overridé" dans mon 'MonModTable').
    Je suppose aussi que dans 'miseÀJourModèleEtStructTable(...)' (dans 'XYExtensTable_SSCCE') et juste après la mise à jour du modèle dans 'élaborTtsRangéesTable()', il ne faut plus ajouter de colonne, ni en bouger de place, ni quoi que ce soit d'autre.

    Cette application à quatre classes ne fonctionne pas encore complètement :
    - L'ajout dynamique de colonnes de boutons-radios fait apparaître des boutons-radios inertes. Ils n'inter-agissent plus par rangée; ils ne sont même plus sélectionnables.
    - Les '0' de la dernière colonne (Z) sont remplacés par de boutons-radios (inertes).
    - 'coloDeTable.setMaxWidth(40);' ne fonctionne plus, apparamment depuis l'implémentation de 'fireTableStructureChanged()' à la fin de 'élaborTtsRangéesTable()'.
    ... contrairement à ce qui se passe dans l'SSCCE de la table statique (1er URL de ci-dessus). Je pense cependant être près du bon fonctionnement complet.
    Un féru en JTables dynamiques pourrait-il m'aider à trouver ce qui se passe, ce que j'omets, ce qui m'échappe, ... ? Merci d'avance d'essayer de m'aider.

    Voici les quatre classes de mon SSCEE à copier :
    - XYExtensTable_SSCCE (375 l.)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
    365
    366
    367
    368
    369
    370
    371
    372
    373
    374
    /*
     * To change this license header, choose License Headers in Project Properties.
     * To change this template file, choose Tools | Templates and open the template in the editor.
     * Short, Self Contained, Correct (Compilable), Example     http://sscce.org/
     * @author Chavadam
     */
    package xyextenstable_sscce;
     
    import java.awt.BorderLayout;
    import java.awt.Component;
    import java.awt.Dimension;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
     
    import javax.swing.JFrame;
    // import javax.swing.JPanel;
    // import javax.swing.JSeparator;
    import javax.swing.AbstractCellEditor;
    import javax.swing.JRadioButton;
    // import javax.swing.ButtonGroup;
    import javax.swing.JScrollPane;
    import javax.swing.JTable;
    // import javax.swing.table.AbstractTableModel;
    import javax.swing.table.DefaultTableCellRenderer;      // RenduCol3
    import javax.swing.table.TableCellEditor;
    import javax.swing.table.TableCellRenderer;
    import javax.swing.table.TableColumn;
    // import javax.swing.table.TableColumnModel;
    import javax.swing.JButton;
    import javax.swing.GroupLayout;
    // import javax.swing.LayoutStyle;             // addPreferredGap(
    import javax.swing.SwingConstants;          // CENTER
    import javax.swing.SwingUtilities;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;     // main()
    import javax.swing.event.*;             // TableModelListener, TableModelevent
     
    import static xyextenstable_sscce.MonModTable.NBRE_FIXE_COL_AVANT;
    import static xyextenstable_sscce.MonModTable.NBRE_FIXE_COL_APRES;
     
     
    /**
     *
     */
    public class XYExtensTable_SSCCE extends JFrame implements ActionListener   // TableModelListener
    {
        // <editor-fold defaultstate="collapsed" desc="Déclarations des variables">
        private /* final */ JTable table;
       /* Local variables 'renduCellBoutRad' and 'monModTable' are accessed from 
        * within a inner class (JTable) --> needs to be declared final.       */
        private /* final */ MonModTable monModTable;
    //    private TableColumnModel modèColonTable;
                        // Constructeur : La largeur des intitulés de chaque colonne
        private TableColumn coloDeTable;    // et 'miseÀJourModèleEtStructTable()' 
        private byte nbreColoTable;
        private static final byte LARGEUR_MAX_COL = 50;
        private int largTotTable;
        private int largTotTable_TtesRangées;
     
        private byte nbreColÀBP_AvantModif, nbreColÀBP;
        private JButton boutAjoutUneColon;
        private JButton boutRetraitUneColon;
     
                static final boolean DEBUG = false;
        // </editor-fold>
     
     
        protected void initUI()     // public XYExtensTable_SSCCE()
        {   
            // <editor-fold defaultstate="collapsed" desc="Initialisation des variables">
            monModTable = new MonModTable(nbreColÀBP = 0, nbreColÀBP_AvantModif = 0);
     
            table = new JTable(monModTable)
            {   
                DefaultTableCellRenderer renduCell;
                RendEdit_CellBoutRad rendu_CellBoutRad;
     
     
                // Bloc constructeur par défaut de la JTable.
                {   // Méthode héritée de JLabel
           /* As we are calling 'super.getTableCellRendererComponent', we need to extend
            * 'DefaultTableCellRenderer'; not simply implement 'TableCellRenderer'.   */
           /* If you do not extend from 'DefaultTableCellRenderer', you will need to be 
            * careful to find ways to indicate whether the cell has focus or not, is 
            * editable or not, and is selected or not. If you don't, then those cells 
            * won't match the appearance of other table cells.      */
                    renduCell = new DefaultTableCellRenderer();
                    renduCell.setHorizontalAlignment(SwingConstants.CENTER);
                    rendu_CellBoutRad = new RendEdit_CellBoutRad();
                    // setRowHeight(55);    // Rendu inopérationnel par 'ajustageLargeurCol()'.
     
                    // La largeur des intitulés de chaque colonne
                    largTotTable = 0;
                    // = (byte) table.getModel().getColumnCount();
                    nbreColoTable = (byte) monModTable.getColumnCount();
     
                    for (int col = 0; col < nbreColoTable; col++)
                    {   // Cette quinzaine de lignes rien que pour trouver la largeur des intitulés de chaque colonne ...
                        coloDeTable = this.getColumnModel().getColumn(col);
                        int largeurColPréfér = coloDeTable.getMinWidth();
                        // int largeurMaxCol = coloDeTable.getMaxWidth();
                        coloDeTable.setMaxWidth(LARGEUR_MAX_COL);
     
                        TableCellRenderer renduDEntête = coloDeTable.getHeaderRenderer();
                        if (renduDEntête == null)
                            renduDEntête = this.getTableHeader().getDefaultRenderer();
                        Object contenuDEntête = coloDeTable.getHeaderValue();
                        Component composDeRenduDEntête = renduDEntête.getTableCellRendererComponent(
                                this, contenuDEntête, false, false, 0, col);
                        //                     + table.getIntercellSpacing().width;    Pourquoi ne va pas ?
                        int largeurEntêteCol = composDeRenduDEntête.getPreferredSize().width + 12;
                        // colonneDeTable.setPreferredWidth(largeurEntêteCol);
     
                        largeurColPréfér = Math.max(largeurColPréfér, largeurEntêteCol);
                        // Lors de la création du JFrame et de la JTable, aucune rangée n'est à afficher.         */
                        largTotTable += largeurColPréfér;
                        coloDeTable.setPreferredWidth(largeurColPréfér);
                        // colonneDeTable.setWidth(largeurColPréfér);
                    }   // Fin de 'La largeur des intitulés de chaque colonne'
     
                   /* Pour que les intitulés de colonnes soient affichés même lorsqu'il 
                    * n'y a aucune colonne à bouton-radio (dynamique).          */
                    largTotTable_TtesRangées = Math.max(largTotTable_TtesRangées, largTotTable);
                }   // Fin du bloc constructeur par défaut de la JTable.
     
     
                // Overrides method from javax.swing.JTable
                @Override public TableCellRenderer getCellRenderer(int rang, int col)
                {   
                    int nbreColo = monModTable.getColumnCount() - NBRE_FIXE_COL_APRES;
     
                    if (col < NBRE_FIXE_COL_AVANT)
                        return renduCell;
                    else
                    if (col >= NBRE_FIXE_COL_AVANT && col <= nbreColo)
                        // col <= monModTable.idxColFinBoutRadio)
                        return rendu_CellBoutRad;
                    else        // if (col > nbreColo)
                        return renduCell;            // CENTER
                }
     
     
               /* Prepares the renderer by querying the data model for the 
                * value and selection state of the cell at row, column.
                * With Java, you can specify cell renderers and editors either by 
                * column or by data type.       */
                @Override public Component prepareRenderer(
                        TableCellRenderer renduCelluleTableÀPréparer, int rang, int col)
                {   // La classe parente de 'JTable' est 'javax.swing.JComponent'.
    short wave10 = 0;  // Rien que pour placer un breakpoint. À ôter.
                   /* Pour toutes les cellules des colonnes ne contenant pas un objet de type 'MonObjet' :
                    * - 'renduCelluleTableÀPréparer' contient un objet de type 'DefaultTableCellRenderer'.
                    * - 'compos' contient un objet de type 'DefaultTableCellRenderer'.
                    * Pour toutes les cellules des colonnes contenant bien un objet de type 'MonObjet' :
                    * - 'renduCelluleTableÀPréparer' contient un objet de type 'RendEdit_CellBoutRad'.
                    * - 'compos' contient un objet de type 'JRadioBouton'.      */
                   /* Appel de 'getValueAt()' dans 'MonModTable'
                    * et de 'isSélectionné()" dans 'MonObjet'   */
                    final Component compos = 
                            super.prepareRenderer(renduCelluleTableÀPréparer, rang, col);
                    // compos.setBackground(Color.GRAY);
                    return compos;
                }
     
     
                // Nécessaire ?
                @Override public void tableChanged(TableModelEvent e)
                {   // La classe parente de 'JTable' est 'javax.swing.JComponent'.
                    super.tableChanged(e);
                    repaint();
                }
            };      // Fin de 'new JTable(...'
     
            // <editor-fold defaultstate="collapsed" desc="Largeur chaque colonne = minimum">
            /* Anihile l'extension horizontale automatique de la table faisant en sorte qu'elle 
             * occupe tout l'espace horizontal du panneau JTabbedPane qui la contient.  */
            table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
            // table.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
     
            // table.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
            // table.setPreferredScrollableViewportSize(new Dimension(620, 80)); // Width, height
            // table.setFillsViewportHeight(true);       // Ne fonctionne pas.
            table.setFillsViewportHeight(true);
            table.setPreferredScrollableViewportSize(new Dimension(
                    largTotTable_TtesRangées,           // Width    table_ChoixMach.getTableHeader().getWidth()
                    160) );             // Height   table_ChoixMach.getHeight()     = 10 rows * 16 pixels/row
            // table.getTableHeader().setToolTipText("Tool tip commun à toutes les colonnes");
            // </editor-fold>       // Fin de "Largeur chaque colonne = minimum"
     
            JScrollPane panDéroul_Table = new JScrollPane(table);
            // panDéroul_Table.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
            // panDéroul_Table.setViewportView(table);
     
            //  -     -     -     -     -     -     -     -     -     -     -
     
            boutAjoutUneColon = new JButton("Ajout d'une colonne");
            boutAjoutUneColon.addActionListener(this);
            boutRetraitUneColon = new JButton("Retrait d'une colonne");
    //        boutRetraitUneColon.addActionListener(this);
     
            //  -     -     -     -     -     -     -     -     -     -     -
     
            this.add(panDéroul_Table, BorderLayout.CENTER);
            this.add(boutAjoutUneColon, BorderLayout.NORTH);
            this.add(boutRetraitUneColon, BorderLayout.SOUTH);
            this.pack();
            this.setVisible(true);
            this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        }   // Fin du constructeur de 'XYExtensTable_SSCCE'
            // </editor-fold>      // Fin de 'Initialisation des variables'
     
     
        // Boutons-Poussoirs Ajout / retrait d'une colonne
        @Override public void actionPerformed(ActionEvent évé)
        {
            nbreColoTable = (byte) table.getColumnModel().getColumnCount();
            nbreColÀBP = (byte) (nbreColoTable - (NBRE_FIXE_COL_AVANT + NBRE_FIXE_COL_APRES));
            nbreColÀBP_AvantModif = nbreColÀBP;
     
            if (évé.getSource() == boutAjoutUneColon)
            {
                miseÀJourModèleEtStructTable(++nbreColÀBP, nbreColÀBP_AvantModif);
                boutRetraitUneColon.setEnabled(true);
            }
            else
            if (évé.getSource() == boutRetraitUneColon)
            {
                if (nbreColÀBP <= 0)
                    boutRetraitUneColon.setEnabled(false);
                else
                {
                    miseÀJourModèleEtStructTable(--nbreColÀBP, nbreColÀBP_AvantModif);
                    boutRetraitUneColon.setEnabled(true);
                }
            }
        }
     
     
       /** Remet à jour les données de 'monModTable' 
        * Note : table.getModel().addColumn(new TableColumn(libelléCol)); est seulement 
        * fourni dans 'DefaultTableModel', pas prévu par 'AbstractTableModel'.
        * Cette méthode est appelée suite à l'actionnement des deux boutons-poussoirs.
        * @param nbreColÀBP3 
        * @param nbreColÀBP_AvantModif3
        */
        public void miseÀJourModèleEtStructTable(byte nbreColÀBP3, byte nbreColÀBP_AvantModif3)
        {   
            byte nbreRangées = monModTable.élaborTtsRangéesTable();
            // table.setModel(monModTable);
     
            // nbreColoTable = (byte) monModTable.libellésColonnes.size();       // 4
            nbreColoTable = (byte) table.getColumnModel().getColumnCount();      // 3
            // Pour ce SSCCE, ce n'est toujours qu'une seule colonne à la fois qui est à ajouter ou à retirer.
            byte nbreColÀAjouter = (byte) ((nbreColÀBP3 - nbreColÀBP_AvantModif3));
            // Dernière colonne de radio-boutons après laquelle la prochaine sera à ajouter.
            byte idxDernColoBoutRad;
     
            if (nbreColÀAjouter > 0)
            {   
    /*            coloDeTable = new TableColumn();
                coloDeTable.setMaxWidth(40);
                
                // Il faut des 'RendEdit_CellBoutRad' différents pour 'CellEditor' et 'CellRenderer'.
                // edit_CellBoutRad = new RendEdit_CellBoutRad();
                coloDeTable.setCellEditor  (new RendEdit_CellBoutRad());
                coloDeTable.setCellRenderer(new RendEdit_CellBoutRad());
                
                table.addColumn(coloDeTable);
                
                nbreColoTable++;    // ou nbreColoTable = (byte) table.getColumnCount();
                idxDernColoBoutRad = (byte) (nbreColoTable - NBRE_FIXE_COL_APRES -1);
               /* La nouvelle colonne est toujours ajoutée à l'extrême droite de la table et 
                * est donc à déplacer à la première des NBRE_FIXE_COL_APRES dernières colonnes. * /
                table.moveColumn(nbreColoTable -1, idxDernColoBoutRad);
                // nbreColÀAjouter--;
    */            
                // Le groupage fonctionnel des radio-boutons est fait par 'GestionnObjetsDUneRangée'.
            }   // fin de 'if (nbreColÀAjouter > 0)' 
            else if (nbreColÀAjouter < 0)      // nbreColÀAjout est négatif => Colonnes à ôter.
            {
                nbreColoTable = (byte) table.getColumnCount();
                idxDernColoBoutRad = (byte) (nbreColoTable - NBRE_FIXE_COL_APRES -1);
                coloDeTable = table.getColumn(idxDernColoBoutRad);
                monModTable.libellésColonnes.remove((int) (NBRE_FIXE_COL_AVANT + nbreRangées));
                table.removeColumn(coloDeTable);
                // Deselects the columns from index0 to index1, inclusive.
    //            table.removeColumnSelectionInterval(
    //                (int) NBRE_FIXE_COL_AVANT, (int)(NBRE_FIXE_COL_AVANT - nbreColÀAjouter));
            }
        }
     
     
        /**
         * @param args the command line arguments
         * @throws ClassNotFoundException
         * @throws InstantiationException
         * @throws IllegalAccessException
         * @throws UnsupportedLookAndFeelException
         */
        public static void main(String[] args) throws ClassNotFoundException, InstantiationException, 
                                             IllegalAccessException, UnsupportedLookAndFeelException
        {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
            SwingUtilities.invokeLater(new Runnable()
            {
                @Override public void run()     // Implements method from java.lang.Runnable
                {
                    new XYExtensTable_SSCCE().initUI();
                }
            }                         );
        }
     
            //  -     -     -     -     -     -     -     -     -     -     -
     
        // Pas "extends JRadioButton".
        private class RendEdit_CellBoutRad extends AbstractCellEditor
                implements TableCellRenderer, TableCellEditor, ActionListener
        {
            private final JRadioButton boutonRadio;
     
     
            public RendEdit_CellBoutRad()
            {
                this.boutonRadio = new JRadioButton();
                boutonRadio.addActionListener(this);    // Leaking 'this' in constructor.
                boutonRadio.setHorizontalAlignment(SwingConstants.CENTER);
                boutonRadio.setOpaque(false);
            }
     
     
           /* Implements the unique method from 'javax.swing.table.TableCellRenderer' interface.
            * https://docs.oracle.com/javase/7/docs/api/index.html?javax/swing/table/TableCellRenderer.html 
            * L'objet renvoyé par 'getValueAt()' dans 'MonModTable' est traité ici.     */
            @Override public Component getTableCellRendererComponent(JTable table, Object valeur, 
                                boolean estSélectionné, boolean aLeFocus, int rangée, int colonne)
            {
                boutonRadio.setSelected(Boolean.TRUE.equals(valeur));
                boutonRadio.setOpaque(true);
    //            boutonRadio.setBackground(java.awt.Color.yellow);
                return boutonRadio;
            }
     
     
            // This method is called when a cell value is edited by the user.
            // Implements method from 'javax.swing.table.TableCellEditor'.
            @Override public Component getTableCellEditorComponent(JTable table, Object valeur, 
                                                 boolean estSélectionné, int rangée, int colonne)
            {
                boutonRadio.setSelected(Boolean.TRUE.equals(valeur));
                return boutonRadio;
            }
     
     
            // Implements method from 'java.awt.event.ActionListener'.
            @Override public void actionPerformed(ActionEvent e)
            {   
                stopCellEditing();
            }
     
     
            // Implements method from 'javax.swing.CellEditor'. JTable or JTree
            @Override public Object getCellEditorValue()
            {   // 'isSelected()' is inherited from class javax.swing.AbstractButton .
                return boutonRadio.isSelected();        // boolean
            }
     
    /*        
            @Override public void setBackground(Color bg)
            {
                ;
            }
    */
        }
    }
    - MonModTable (395 l.)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
    365
    366
    367
    368
    369
    370
    371
    372
    373
    374
    375
    376
    377
    378
    379
    380
    381
    382
    383
    384
    385
    386
    387
    388
    389
    390
    391
    392
    393
    394
    395
    /*
     * @author Chavadam
     */
     
    package xyextenstable_sscce;
     
    import java.math.BigDecimal;
    import java.util.List;          // Interface
    import java.util.ArrayList;
    // import java.util.Comparator;
    // import java.util.Collections;
    // import static java.util.Collections.sort;
    import java.beans.PropertyChangeEvent;
    import java.beans.PropertyChangeListener;
    import javax.swing.table.AbstractTableModel;
    // import static xyextenstable_sscce.XYExtensTable_SSCCE.DEBUG;
     
     
    /**
     * Swing ne prévoit pas la possibilité d'implémenter un "Listener" (= "guêteur") 
     * cellule par cellule --> en implémenter un à 'class XYExtensTable_SSCCE'.
     */
        class MonModTable extends AbstractTableModel implements PropertyChangeListener
    {   
        // <editor-fold defaultstate="collapsed" desc="Déclaration des variables">
        private List<BigDecimal> échelleValeurs1eColonne;
     
        private byte nbreColÀBP2;
        private byte nbreColÀBP_AvantModif2;
        // Horizontalement
        // Not 'private' because capability to remove items from XYExtenTable_SSCCE.
             List<String> libellésColonnes;
    //    private Byte libelléDernièreColonne;
        public List<String> libelAutresColQueColÀBP;
        // Formats des contenus des colonnes : String, RendeurEditeur_CelluleBoutonRadio, BigDecimal
        public final List<String> renduCellCol;
     
        /* 'idxRangée' dans 'Composer la base des rangées de données de la table' dans 
        * 'élaborTtsRangéesTable()' doit être accessible dans '@Override propertyChange(evt)'. */
        private byte idxRangée;
    //    private BigDecimal[] énElecTherm_DuréeFonct;  
        // Avant et après le groupe d'un nombre variable de colonnes à radio-boutons.
                static final byte NBRE_FIXE_COL_AVANT = 2;
                static final byte NBRE_FIXE_COL_APRES = 1;
    //     private int colCommencementBoutRadio = NBRE_FIXE_COL_AVANT;
                     int idxColFinBoutRadio = 0;
        // Contient et gère les rangées de boutons-radios et leurs regroupements.
        private List<GestionnObjetsDUneRangée> gestionnDonnéesTteLaTable;
        private GestionnObjetsDUneRangée gestionObjDUneRang;
        // </editor-fold>      // Fin de 'Déclaration des variables'
     
     
        MonModTable(byte nbreColÀBP1, byte nbreColÀBP_AvantModif1)
        {   
           /* Appelle le constructeur de 'AbstractTableModel'. This abstract class provides
            * default implementations for most of the methods in the TableModel interface.
            * https://docs.oracle.com/javase/7/docs/api/javax/swing/table/AbstractTableModel.html
            * Pas nécessaire car les méthodes requises sont implémentées dans cette sous-classe-ci.
            */ 
            // super();        // 
     
            this.nbreColÀBP2 = nbreColÀBP1;
            this.nbreColÀBP_AvantModif2 = nbreColÀBP_AvantModif1;
     
            // Instanciée ici aussi pour éviter 'NullPointerException' dans 'getRowCount()'.
            gestionnDonnéesTteLaTable = new ArrayList<>();
     
            // Verticalement
            libellésColonnes = new ArrayList<>();
            libellésColonnes.add("A");
            libellésColonnes.add("B");
            // Entre ces colonnes-ci viendront s'insérer celles des boutons-radios.
            libellésColonnes.add("Z");          // ((Byte) nbreColÀBP2).toString());
     
            // Format des contenus des colonnes - Voir 'ajustageLargeurCol()'
           /*  renduCellCol = new ArrayList(new Collection{"#.0", "#.0", "#.00"});  */
            renduCellCol = new ArrayList();
            renduCellCol.add("#.0");    // A
            renduCellCol.add("#.00");    // B
            // Entre ces colonnes-ci viendront s'insérer celles des boutons-radios.
            renduCellCol.add("#.0");    // C
        }
     
     
       /** Appelée
     - lors de l'initialisation, dans 'élaborTtsRangéesTable()', dans la boucle 
     empilant des 'MonObjet's.
        */
        @Override public void propertyChange(PropertyChangeEvent evt)
        {
            Object objet2 = evt.getSource();
     
            if (objet2 == gestionObjDUneRang)
            {   
                switch (evt.getPropertyName())
                {   case "objetsDUneTranPuiss":
                        // 'supportChang_tPropri'
    //                    ((MonObjet) evt.getNewValue()).getSupportChangtPropri_Obj().addPropertyChangeListener(this);
                        break;
                // MonModèleTable
                    case "objectBijgevoegd":
                        // Constructeur
                        short wave2 = 0;        // Rien que pour placer un breakpoint
                        break;
                    case "objectenVanEenRij":
                        // Ne passe jamais par ici.
                        short wave3 = 0;        // Rien que pour placer un breakpoint
                        break;
                //    fireTableDataChanged();
                    case "dataVanEenRij":
                        // Ne passe jamais par ici.
                        short wave4 = 0;        // Rien que pour placer un breakpoint
                        break;
                }
            } else
            if (objet2 instanceof MonObjet)
            {
                switch (evt.getPropertyName()) {
                    case "juistVeranderd":
                        // Constructeur et utilisation
                        short wave5 = 0;        // Rien que pour placer un breakpoint
                        break;
                    case "ObjMgrDezeRijIsBijgevoegd":
                        // Constructeur
                        short wave6 = 0;        // Rien que pour placer un breakpoint
                        break;
                }
                ;
    //             int idx_Tran = gestionObjDUneRang.getObjetsDUneRangée().indexOf(objet2);
                // short[] coordBR = ((MonObjet) objet2).getCoordBP();
                // fireTableRowsUpdated(coordBR[0]-1, coordBR[0]-1);       // Rien que la rangée concernée.
            }
     
        }
     
     
       /** Méthode exigée par l'interface 'AbstractTableModel' --> Nom en Anglais
        * Implements method from javax.swing.table.TableModel
        * @return libellésColonnes.size()
        */
        @Override public int getColumnCount()
        {   
            return libellésColonnes.size();
            // return gestionObjDUneRang.getObjetsDUneRangée().size();
        }
     
     
       /** Méthode exigée par l'interface 'AbstractTableModel'
        * 'gestionnDonnéesTteLaTable' doit avoir été instanciée dans la méthode-constructeur.
        * @return 
        */
        @Override public int getRowCount()
        {   
            return gestionnDonnéesTteLaTable.size();
        }
     
     
        // Méthode existante mais non exigée par l'interface 'AbstractTableModel'
        @Override public String getColumnName(int col)
        {   
            return libellésColonnes.get(col);
        }
     
     
       /** If you did not specify a renderer for a particular column, then the table 
        * invokes the table model's getColumnClass method, which gets the data type 
        * of the column's cells.
        * Challenge : Le nombre de colonnes est variable.
        * @param colo
        * @return 
        */
        @Override public Class<?> getColumnClass(int colo)
        {   
            if (colo < NBRE_FIXE_COL_AVANT)
            {
                return BigDecimal.class;
            } else
            if (colo >= NBRE_FIXE_COL_AVANT      &&      // 2
                colo < NBRE_FIXE_COL_AVANT + nbreColÀBP2)
            {
                return MonObjet.class;      // Contient une propriété JRadioButton
            } else
            if (colo >= NBRE_FIXE_COL_AVANT + nbreColÀBP2  &&
                colo < NBRE_FIXE_COL_AVANT + nbreColÀBP2 + NBRE_FIXE_COL_APRES)
            {
                return BigDecimal.class;
            } else            // Sans 'else' ni ';' : "Confusing identation"
            return null;      // Jamais
        }
     
     
        // Seules les colonnes à boutons-radios sont éditable.
        @Override public boolean isCellEditable(int rang, int col)
        {   
            if (col < NBRE_FIXE_COL_AVANT)
                return false;
            else if (col >= NBRE_FIXE_COL_AVANT     && 
                     col <= NBRE_FIXE_COL_AVANT + nbreColÀBP2)
                if (rang == 0)  return false;
                else            return true;
            else if (col > idxColFinBoutRadio)
                return false;
     
            return false;       // Jamais
        }
     
     
       /** Méthode exigée par l'interface 'AbstractTableModel'.
        * Le contCellTabl renvoyé sera traité par la fonction 
        * 'TableCellRenderer.getTableCellRendererComponent (JTable table, 
        * Object value, boolean isSelected, boolean hasFocus, int row, int column)' 
        * dans 'XYExtensTable_SSCCE'. 
        * Renvoie un String ou un Boolean.      */
        @Override public Object getValueAt(int rang, int col)
        {   
            Object contCellTabl = getValeurÀ(rang, col);
     
            if (col < NBRE_FIXE_COL_AVANT)
            {
                return contCellTabl;
            } else
            if (col >= NBRE_FIXE_COL_AVANT && col <= idxColFinBoutRadio)
            {   // Bouton radio : Boolean       boolean --> Boolean
                if ((Class<?>)contCellTabl.getClass() == MonObjet.class)
                {
                    Boolean étaitSélect_é = ((MonObjet) contCellTabl).isSélectionné();
                    return étaitSélect_é;
                } 
            } else
            if (col > idxColFinBoutRadio)
            {
                return contCellTabl;
            }
            ;           // Sans 'else' ni ';' : "Confusing identation"
            return null;
        }
     
     
        // Méthode appelée par 'getValueAt() et 'setValueAt()'.
        Object getValeurÀ(int rang, int colo)
        {   // Il y a [NBRE_RANG] gestionnObjetsDUneRangée dans 'gestionnDonnéesTteLaTable'.
            return gestionnDonnéesTteLaTable.get(rang).getObjetsDUneRangée().get(colo);
        }
     
     
       /** Need to implement this method because some table's data can be manually changed.
        * A pour but de n'activer ou désactiver le bouton-radio que d'une seule cellule de 
       ¨* la table. Si c'est l'activer, les conséquences pour les autres cellules de la rangée
        * sont gérées par la méthode 'miseàjourTousBP_Rangée()' dans 'gestionnObjetsDUneRangée', 
        * elle-même appelée ici par 'setSélectionné()' dans 'monObjet'.
        * Overrides method from javax.swing.table.AbstractTableModel
        */
        @Override public void setValueAt(Object bpActionné, int idxRang2, int idxColo2)
        {
            if (idxColo2 >= NBRE_FIXE_COL_AVANT && idxColo2 <= idxColFinBoutRadio)
            {
                MonObjet contCellTabl = (MonObjet) getValeurÀ(idxRang2, idxColo2);
                contCellTabl.setSélectionné(Boolean.TRUE.equals(bpActionné));
            }
           /* It works without 'fireTableCellUpdated(..;)' if you changes the data by 
            * using the table editor, because once the editor is closed, the cell will 
            * get repainted by the table. 
            * However, if you manually invoke setValueAt() then it won't work. 
            * Also if you don't invoke that method, the 'no event' will be fired for 
            * a TableModelListener that might be added to the TableModel.
            */
            fireTableCellUpdated(idxRang2, idxColo2);
        }
     
     
        List<GestionnObjetsDUneRangée> getDonnéesTteLaTable()   // ArrayList<Object>
        {   
            return gestionnDonnéesTteLaTable;
        }
     
        //  -     -     -     -     -     -     -     -     -     -     -
     
       /** Méthode appelée dans 'miseÀJourModèleEtStructTable()'. 
        *
        */ 
        byte élaborTtsRangéesTable()
        {   
            nbreColÀBP_AvantModif2 = nbreColÀBP2++;
            idxColFinBoutRadio = NBRE_FIXE_COL_AVANT + nbreColÀBP2 -1;
    //        byte nbreCellulesChaqueRangée = (byte)(idxColFinBoutRadio + NBRE_FIXE_COL_APRES);
            MonObjet objet;
    //        String libelNouvelleCol;
     
            // <editor-fold defaultstate="collapsed" desc="Insérer les libellés des colonnes aux boutons-radio">
            if (nbreColÀBP2 > nbreColÀBP_AvantModif2)
            {   
                for (byte nièmeCol = (byte) nbreColÀBP_AvantModif2 ; 
                                            nièmeCol < nbreColÀBP2 ; nièmeCol++)
                {   
                    libellésColonnes.add(NBRE_FIXE_COL_AVANT + nièmeCol, /* libelNouvelleCol */ 
                                                                ((Byte) nièmeCol).toString());
                }
            } else      // Fin de 'if (nbreColÀBP2 > nbreColÀBP_AvantModif2)'
            // Suppression d'une colonne
            {  /* Le nécessaire est fait par 'ajoutRetraitCol(int pos, byte nbreColÀBP2, 
                * List columnData)' dans 'XYExtensTable_SSCCE'               */
            }
            // Fin de "Ajuster le nombre de colonnes de boutons-radios".
            // </editor-fold>
     
            // <editor-fold defaultstate="collapsed" desc="Composer une série de valeurs factices pour les premières colonnes">
           /* Avant d'élaborer les ArrayList 'gestionObjDUneRangée', élaborer 
            * l'ArrayList 'échelleValeurs1eColonne'.        */
            BigDecimal uneValeur = new BigDecimal(10);
            final BigDecimal multiplicateur = new BigDecimal(1.414213);
            échelleValeurs1eColonne = new ArrayList<>(nbreColÀBP2);
     
            for (byte num = 0 ; num < (nbreColÀBP2 +1) ; num++)
            {   
                uneValeur = uneValeur.multiply(multiplicateur);
                échelleValeurs1eColonne.add(uneValeur);
            }
     
            byte nbreRangées = (byte) (échelleValeurs1eColonne.size() -1);
            // </editor-fold>
     
            // <editor-fold defaultstate="collapsed" desc="Composer la base des rangées de données de la table">
           /* c à d la liste 'gestionnDonnéesTteLaTable' de toutes les listes 'gestionObjDUneRang' 
            * de <nbreCellulesChaqueLigne> cellules, en fonction du nombre <nbreColÀBP>.
            * 'Object' est 'MonObjet' lorsqu'il s'agit des colonnes ajoutées dynamiquement.     */
            gestionnDonnéesTteLaTable = new ArrayList<>(nbreRangées);
     
            // 'idxRangée' est aussi utilisée par '@Override ... propertyChange(PropertyChangeEvent evt)
            for (idxRangée = 0 ; idxRangée < nbreRangées ; idxRangée++)
            {   
                gestionObjDUneRang = new GestionnObjetsDUneRangée(nbreColÀBP2);
     
               /* Support pour propriété qui change.
                * Seule 'monModTable' est enregistrée comme "listener" (= guêteur) 
                * de toutes les propriétés de 
                * - chaque instanciation de 'gestionObjDUneRang' (dont 'objetsDUneTranPuiss') 
                * cela dans          de 'MonModTable' > '
                * - chaque instanciation de 'MonObjet', cela dans la 'propertyChange()'
                */
               /* Pour que le support de changement de propriété 'suppChanProp_GestRang' 
                * propre à ce 'gestionObjDUneRang' enregistre 'MonModTable' comme guêteur 
                * de changement de chacune des propriétés de ce 'gestionObjDUneRang'.
                */
                // gestionObjDUneRangée.getSuppoChangePropri().addPropertyChangeListener(this);
                gestionObjDUneRang.ajoutGuêteurChangtPropri_GestRang(this);
     
                // Première colonne
                // Valeur de la deuxième cellules de la rangée précédente.
                gestionObjDUneRang.getObjetsDUneRangée().add(échelleValeurs1eColonne.get(idxRangée));
                // Deuxième colonne
                gestionObjDUneRang.getObjetsDUneRangée().add(échelleValeurs1eColonne.get(idxRangée+1));
                // Entre deuxième et dernière colonne
                for (byte col = NBRE_FIXE_COL_AVANT ; col < NBRE_FIXE_COL_AVANT + nbreColÀBP2 ; col++)
                {   
                    objet = new MonObjet(idxRangée, col);
     
                   /* Pour que le support de changement de propriété 'suppChanProp_Obj' 
                    * propre à ce nouvel 'objet' enregistre 'monModTable' comme guêteur 
                    * de changement de chacune des propriétés de ce nouvel 'objet'.   */
                    objet.ajoutGuêteurChangtPropri_Obj(this);
     
                   /* Grâce à 'setGestionnObjetsDUneRangée(...)' dans 'ajoutObjetÀRangée(...)' 
                    * dans 'GestionnObjetsDUneRangée', la propriété 'gestionObjDUneRang2' dans 
                    * 'contCellTabl' réfèrera au 'gestionObjDUneRang' d'ici ('monModTable')   */
                    gestionObjDUneRang.ajoutObjetÀRangée(objet);
    /*              
                    if (col == NBRE_FIXE_COL_AVANT)
                        objet.setSélectionné(true);
    */
                }
     
                // Dernière colonne
                gestionObjDUneRang.getObjetsDUneRangée().add(BigDecimal.ZERO);
     
                gestionnDonnéesTteLaTable.add(gestionObjDUneRang);
     
           /* L'activation initiale d'un bouton-radio par rangée n'est exécutable que 
            * lorsque le tout dernier bouton-radio d'une rangée - c à d les [nbreColÀBP2] 
            * objets - ont été ajoutés, c à d :
            * - au terme de l'empilement de tous les boutons-radios d'une rangée dans 
            * 'Composer la base des rangées de données de la table'.
            * - au cours de l'utilisation de la table.
            * 'setSélectionné()' ne peut donc avoir lieu qu'ensuite.
            ((MonObjet) gestionObjDUneRang.getObjetsDUneRangée()
                                .get(NBRE_FIXE_COL_AVANT)).setSélectionné(true);
            */
            // The fireXxx() methods simply instruct the view to query the model via getValueAt(). 
    //            fireTableStructureChanged();
            }   // Fin de "Composer la base des rangées de données de la table"
            // </editor-fold>// </editor-fold>
     
            fireTableStructureChanged();
            return nbreRangées;
        }
    }
    - GestionnObjetsDUneRangée (128 l.)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    /*
     * @author Chavadam
     * http://www.developpez.net/forums/d1577944/java/interfaces-graphiques-java/awt-swing/composants/tables/jtable-jradiobuttons-regroupes-rangee/
     */
    package xyextenstable_sscce;
     
    import java.beans.PropertyChangeSupport;    // PropertyChangeEvent
    import java.beans.PropertyChangeListener;
    import java.util.ArrayList;
    import java.util.List;
    import static xyextenstable_sscce.MonModTable.NBRE_FIXE_COL_AVANT;
    import static xyextenstable_sscce.MonModTable.NBRE_FIXE_COL_APRES;
    // import static xyextenstable_sscce.XYExtensTable_SSCCE.DEBUG;
     
     
     
    /**  Bean that support bound properties. (All Swing components are also beans)
     Manages a list of listeners and dispatches PropertyChangeEvents to them. 
     Ce n'est pas que des boutons-radio (RenduEditeur_CellBoutRad) ont plusieurs 
     guêteurs, mais que tous les boutons-radios ont un même guëteur. 
     http://docs.oracle.com/javase/7/docs/api/java/beans/PropertyChangeSupport.html
     A bound property notifies listeners when its value changes. This has two implications:
     1. This bean class includes 'addPropertyChangeListener()' [and 'removePropertyChangeListener()'] 
       methods for managing the bean's listener(s) (here only 'MonModTable').
     2. When a bound property is changed (dans 'GestionnObjetsDUneRangée gestionObjDUneRang' 
     et dans chaque 'MonObjet objet' créé dans 'MonModTable'), 
     ainsi qu'à l'exécution de 'propertyChangeSupport.firePropertyChange()', 
     the bean sends a PropertyChangeEvent to its registered listeners.
     Getter method but no setter : read-only property.
     La liste ... mais plutôt 
     la partie centrale de l''List<ArrayList<Object>> objetsTteLaTable' dans
     'MonModTable', c à d les colonnes après 'NBRE_FIXE_COL_AVANT' 
     et avant 'NBRE_FIXE_COL_APRES'. Les deux dimensions de 'donnéesTteLaTable' sont 
     variables.
     */
    public class GestionnObjetsDUneRangée
    {  
       /* Chaque rangée (objetsDUneRangée) contient des 'BigDecimal' et  
        * un nombre variable de 'MonObjet' (contenant un 'JRadioButton'). */
        private List<Object> objetsDUneRangée;
    //    private final ButtonGroup groupeHorizBoutRad = new ButtonGroup();
        private /* final */ PropertyChangeSupport suppChanProp_GestRang;
        /* Propriété en fait oommune à 'objetsDUneRangée', c à d qui ne devrait être 
        * instanciée que dans l'entité englobant tous les 'gestionObjDUneRangée'.
        * Existe aussi dans 'monModTable'.           */
        private final byte nbreColÀBP1;
     
     
        GestionnObjetsDUneRangée(byte nbreColÀBP2)
        {   // 'nbreColÀBP1' rien que pour 'miseàjourTousBR_Rangée()'.
            this.nbreColÀBP1 = nbreColÀBP2;
            objetsDUneRangée = new ArrayList<>(
                    NBRE_FIXE_COL_AVANT + nbreColÀBP1 + NBRE_FIXE_COL_APRES);
            suppChanProp_GestRang = new PropertyChangeSupport(this);
        }
     
     
        public List<Object> getObjetsDUneRangée()
        {
            return objetsDUneRangée;
        }
     
     
        // Appelée par ?
        void setObjetsDUneRangée(List<Object> objetsDUneRangée2)
        {
            List<Object> ancienObjetsDUneRangée = objetsDUneRangée;
            this.objetsDUneRangée = objetsDUneRangée2;
            suppChanProp_GestRang.firePropertyChange("objectenVanEenRij",
                                     ancienObjetsDUneRangée, objetsDUneRangée);
        }
     
     
       /* Méthode appelée 
        * - dans 'MonModTable' > 'élaborTtsRangéesTable()' 
        * > 'Composer la base des rangées de données de la table'
        * Chaque rangée (objetsDUneRangée) contient des 'BigDecimal' et 
        * un nombre variable de 'MonObjet' (contenant un 'RadioButton').  */
        void ajoutObjetÀRangée(Object objet)
        {   
            objetsDUneRangée.add(objet);
            // Met dans chaque objet la référence au gestionnaire d'objets qui le contient.
            if (objet != null)
            {
                ((MonObjet) objet).setGestionnObjetsDUneRangée(this);
    /*            ((MonObjet) objet).getSupportChangtPropri().firePropertyChange("dataVanEenRij",
                                      null, ((MonObjet) objet).getGestionnObjetsDUneRangée());
    */
                suppChanProp_GestRang.firePropertyChange("objectBijgevoegd", null, (MonObjet) objet);
            }
        }
     
     
       /** Appelée par 'setSélectionné()' dans 'monObjet' et appelle 'setSélectionné()'
        * c à d fonction réentrante.
        * Met tous les boutons-poussoirs d'une rangée dans leurs états adéquats.
        * 'MonObjet' ne dispose pas de 'nbreColÀBP'; 'GestionObjDUneRang' bien.
        * @param objetActionné
        */
        void miseàjourTousBR_Rangée(MonObjet objetActionné)
        {   
            MonObjet obj;
     
            for (byte j = NBRE_FIXE_COL_AVANT ; j < NBRE_FIXE_COL_AVANT + nbreColÀBP1 ; j++)
            {   
                obj = ((MonObjet) objetsDUneRangée.get(j));
                obj.setSélectionné(objetActionné == obj);
            }
        }
     
     
        PropertyChangeSupport getSuppoChangePropri_GestRang()
        {
            return suppChanProp_GestRang;
        }
     
     
        void ajoutGuêteurChangtPropri_GestRang(PropertyChangeListener guêteur)
        {
            suppChanProp_GestRang.addPropertyChangeListener(guêteur);
        }
     
     
        void retraitGuêteurChangePropri_GestRang(PropertyChangeListener guêteur)
        {
            suppChanProp_GestRang.removePropertyChangeListener(guêteur);
        }
    }
    - MonObjet (104 l.)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    /*
     * @author Chavadam
     */
    package xyextenstable_sscce;
     
    import java.beans.PropertyChangeListener;
    import java.beans.PropertyChangeSupport;
    // import static xyextenstable_sscce.XYExtensTable_SSCCE.DEBUG;
     
     
     
    /** Bean supporting bound properties.
     * La seule classe guêteuse (Listener) de 'MonObjet' est 'MonModTable'.
     */
        class MonObjet
    {  /* Delegating to 'suppChanProp_Obj' the management of a list of listeners 
        * and the dispatching of 'PropertyChangeEvents' ?        */ 
        private boolean sélectionné;
        private GestionnObjetsDUneRangée gestionObjDUneRang;
        private final PropertyChangeSupport suppChanProp_Obj;
     
     
        public MonObjet(short idxRang, short idxColo)
        {   
            suppChanProp_Obj = new PropertyChangeSupport(this);
        }
     
     
        // The accessor method to be defined uses 'is...' instead of 'get...' because boolean.
        boolean isSélectionné()
        {
            return sélectionné;
        }
     
     
       /** Appelée par
        * - le constructeur de 'MonModTable()'.
        * - 'setValueAt()' dans 'MonModTable()'.
        * - 'setSélectionné(objet)' dans 'gestionnObjetsDUneRangée').
        * 
        * @override setValueAt() > 
        * getValeurÀ(idxRang2, idxColo2)).setSélectionné() > 
        * gestionObjDUneRang2.miseàjourTousBR_Rangée() > 
        * obj.setSélectionné()
        * miseàjourTousBR_Rangée()
        */
        void setSélectionné(boolean sélectionné2)
        {   
            if (this.sélectionné != sélectionné2)
            {   // Ne passe par ici que s'il y a changement d'état.
                this.sélectionné = sélectionné2;
                if (sélectionné == true)
                {   // Ne passe par ici que si le bouton-radio vient de passer à 'true'.
                   /* 'gestionObjDUneRang' est devenu une référence concrète (non nulle) dans 
                    * le constructeur de 'MonModTable' par l'appel de 'gestionObjetsDUneRangée'.
                    * ajoutObjetÀRangée()' qui contient l'appel de 'objet.setGestionnObjetsDUneRangée()'.
                    */
                    gestionObjDUneRang.miseàjourTousBR_Rangée(this);
                }
                suppChanProp_Obj.firePropertyChange("juistVeranderd", !sélectionné, sélectionné);
            }
        }
     
     
        // Méthode appelée par 'ajoutObjetÀRangée(Object objet)' dans 'GestionnObjetsDUneRangée'.
        void setGestionnObjetsDUneRangée(GestionnObjetsDUneRangée gestionnObjetsDUneRangée2)
        {   
            GestionnObjetsDUneRangée ancienGestionnObjetsDUneRangée = gestionObjDUneRang;
     
           /* Une référence au conteneur de l'objet (gestionObjDUneRang) est placée 
            * dans chaque objet afin ...
            */
            this.gestionObjDUneRang = gestionnObjetsDUneRangée2;
     
            suppChanProp_Obj.firePropertyChange("ObjMgrDezeRijIsBijgevoegd",
                                    ancienGestionnObjetsDUneRangée, gestionnObjetsDUneRangée2);
        }
     
     
        // Méthode appelée par 'ajoutObjetÀRangée(MonObjet objet)' dans 'GestionnObjetsDUneRangée'.
        GestionnObjetsDUneRangée getGestionnObjetsDUneRangée()
        {
            return gestionObjDUneRang;
        }
     
     
        PropertyChangeSupport getSupportChangtPropri_Obj()
        {
            return suppChanProp_Obj;
        }
     
     
        // Ici, le 'guêteur' (= 'listener') est 'monModTable'
        void ajoutGuêteurChangtPropri_Obj(PropertyChangeListener guêteur)
        {
            suppChanProp_Obj.addPropertyChangeListener(guêteur);
        }
     
     
        void retraitGuêteurChangePropri_Obj(PropertyChangeListener guêteur)
        {
            suppChanProp_Obj.removePropertyChangeListener(guêteur);
        }
    }
    Bons "cut and paste"s.

    P.S.: À maîtriser : JTables - Un autre regard
    "Ah oui ! Juste encore cette toute dernière petite question ..." (Columbo - Peter Falk)

  2. #2
    Membre régulier
    Profil pro
    Inscrit en
    Décembre 2008
    Messages
    263
    Détails du profil
    Informations personnelles :
    Âge : 73
    Localisation : Belgique

    Informations forums :
    Inscription : Décembre 2008
    Messages : 263
    Points : 121
    Points
    121
    Par défaut
    Re-bonjour.
    Un pas plus loin. Est résolu :
    - Les '0' de la dernière colonne (Z) sont remplacés par de boutons-radios (inertes).
    grâce à qques légères modifications dans
    Classe de base XYExtensTable_SSCCE
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    /*
    /* 
     * @author Chavadam
     */
    package xyextenstable_sscce;
     
    import java.awt.BorderLayout;
    import java.awt.Component;
    import java.awt.Dimension;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
     
    import javax.swing.JFrame;
    import javax.swing.AbstractCellEditor;
    import javax.swing.JRadioButton;
    import javax.swing.JScrollPane;
    import javax.swing.JTable;
    import javax.swing.table.DefaultTableCellRenderer;      // RenduCol3
    import javax.swing.table.TableCellEditor;
    import javax.swing.table.TableCellRenderer;
    import javax.swing.table.TableColumn;
    import javax.swing.JButton;
    import javax.swing.GroupLayout;
    import javax.swing.SwingConstants;          // CENTER
    import javax.swing.SwingUtilities;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;     // main()
    import javax.swing.event.*;             // TableModelListener, TableModelevent
     
    import static xyextenstable_sscce.MonModTable.NBRE_FIXE_COL_AVANT;
    import static xyextenstable_sscce.MonModTable.NBRE_FIXE_COL_APRES;
     
     
    /**
     *
     */
    public class XYExtensTable_SSCCE extends JFrame implements ActionListener   // TableModelListener
    {
        // <editor-fold defaultstate="collapsed" desc="Déclarations des variables">
        private /* final */ MonModTable monModTable;
        private JScrollPane panDéroul_Table;
        private /* final */ JTable table;
     
        private TableColumn coloDeTable;
        private byte nbreColoTable;
        private static final byte LARGEUR_MAX_COL = 50;
        private int largTotTable;
        private int largTotTable_TtesRangées;
     
        private byte nbreColÀBR_AvantModif, nbreColÀBR;
        private JButton boutAjoutUneColon;
        private JButton boutRetraitUneColon;
     
                static final boolean DEBUG = false;
        // </editor-fold>
     
     
        protected void initUI()     // public XYExtensTable_SSCCE()
        {   
            // <editor-fold defaultstate="collapsed" desc="Initialisation des variables">
            monModTable = new MonModTable(nbreColÀBR = 0, nbreColÀBR_AvantModif = 0);
     
            table = new JTable(monModTable)
            {   
                DefaultTableCellRenderer renduCell;
                RendEdit_CellBoutRad rendu_CellBoutRad;
     
     
                // Bloc constructeur par défaut de la JTable.
                {   // Méthode héritée de JLabel
                    renduCell = new DefaultTableCellRenderer();
                    renduCell.setHorizontalAlignment(SwingConstants.CENTER);
                    rendu_CellBoutRad = new RendEdit_CellBoutRad();
                    // setRowHeight(55);    // Rendu inopérationnel par 'ajustageLargeurCol()'.
     
                    // La largeur des intitulés de chaque colonne
                    largTotTable = 0;
                    // = (byte) table.getModel().getColumnCount();
                    nbreColoTable = (byte) monModTable.getColumnCount();
     
                    for (int col = 0; col < nbreColoTable; col++)
                    {   // Cette quinzaine de lignes rien que pour trouver la largeur des intitulés de chaque colonne ...
                        coloDeTable = this.getColumnModel().getColumn(col);
                        int largeurColPréfér = coloDeTable.getMinWidth();
                        // int largeurMaxCol = coloDeTable.getMaxWidth();
                        coloDeTable.setMaxWidth(LARGEUR_MAX_COL);
     
                        TableCellRenderer renduDEntête = coloDeTable.getHeaderRenderer();
                        if (renduDEntête == null)
                            renduDEntête = this.getTableHeader().getDefaultRenderer();
                        Object contenuDEntête = coloDeTable.getHeaderValue();
                        Component composDeRenduDEntête = renduDEntête.getTableCellRendererComponent(
                                this, contenuDEntête, false, false, 0, col);
                        //                     + table.getIntercellSpacing().width;    Pourquoi ne va pas ?
                        int largeurEntêteCol = composDeRenduDEntête.getPreferredSize().width + 12;
                        // colonneDeTable.setPreferredWidth(largeurEntêteCol);
     
                        largeurColPréfér = Math.max(largeurColPréfér, largeurEntêteCol);
                        // Lors de la création du JFrame et de la JTable, aucune rangée n'est à afficher.         */
                        largTotTable += largeurColPréfér;
                        coloDeTable.setPreferredWidth(largeurColPréfér);
                        // colonneDeTable.setWidth(largeurColPréfér);
                    }   // Fin de 'La largeur des intitulés de chaque colonne'
     
                   /* Pour que les intitulés de colonnes soient affichés même lorsqu'il 
                    * n'y a aucune colonne à bouton-radio (dynamique).          */
                    largTotTable_TtesRangées = Math.max(largTotTable_TtesRangées, largTotTable);
                }   // Fin du bloc constructeur par défaut de la JTable.
     
     
                // Overrides method from javax.swing.JTable
                @Override public TableCellRenderer getCellRenderer(int rang, int col)
                {   
                    if (col < NBRE_FIXE_COL_AVANT)
                        return renduCell;
                    else
                    if (col >= NBRE_FIXE_COL_AVANT && col < monModTable.colFinBoutRadio)
                        return rendu_CellBoutRad;
                    else        
                    if (col >= monModTable.colFinBoutRadio &&
                        col < NBRE_FIXE_COL_AVANT + nbreColÀBR + NBRE_FIXE_COL_APRES)
                        return renduCell;
                    else            // Sans 'else' ni ';' : "Confusing identation"
                        return null;      // Jamais
                }
     
     
               /* Prepares the renderer by querying the data model for the 
                * value and selection state of the cell at row, column.
                * With Java, you can specify cell renderers and editors either by column or by data type.       */
                @Override public Component prepareRenderer(
                        TableCellRenderer renduCelluleTableÀPréparer, int rang, int col)
                {   // La classe parente de 'JTable' est 'javax.swing.JComponent'.
                   /* Pour toutes les cellules des colonnes ne contenant pas un objet de type 'MonObjet' :
                    * - 'renduCelluleTableÀPréparer' contient un objet de type 'DefaultTableCellRenderer'.
                    * - 'compos' contient un objet de type 'DefaultTableCellRenderer'.
                    * Pour toutes les cellules des colonnes contenant bien un objet de type 'MonObjet' :
                    * - 'renduCelluleTableÀPréparer' contient un objet de type 'RendEdit_CellBoutRad'.
                    * - 'compos' contient un objet de type 'JRadioBouton'.      */
                   /* Appel de 'getValueAt()' dans 'MonModTable'
                    * et de 'isSélectionné()" dans 'MonObjet'   */
                    final Component compos = 
                            super.prepareRenderer(renduCelluleTableÀPréparer, rang, col);
                    // compos.setBackground(Color.GRAY);
                    return compos;
                }
     
     
                // Nécessaire ?
                @Override public void tableChanged(TableModelEvent e)
                {   // La classe parente de 'JTable' est 'javax.swing.JComponent'.
                    super.tableChanged(e);
                    repaint();
                }
            };      // Fin de 'new JTable(...'
     
            // <editor-fold defaultstate="collapsed" desc="Largeur chaque colonne = minimum">
            /* Anihile l'extension horizontale automatique de la table faisant en sorte qu'elle 
             * occupe tout l'espace horizontal du panneau JTabbedPane qui la contient.  */
            table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
            // table.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
     
            // table.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
            // table.setPreferredScrollableViewportSize(new Dimension(620, 80)); // Width, height
            // table.setFillsViewportHeight(true);       // Ne fonctionne pas.
            table.setFillsViewportHeight(true);
            table.setPreferredScrollableViewportSize(new Dimension(
                    largTotTable_TtesRangées,           // Width    table_ChoixMach.getTableHeader().getWidth()
                    160) );             // Height   table_ChoixMach.getHeight()     = 10 rows * 16 pixels/row
            // table.getTableHeader().setToolTipText("Tool tip commun à toutes les colonnes");
            // </editor-fold>       // Fin de "Largeur chaque colonne = minimum"
     
            panDéroul_Table = new JScrollPane(table);
            // panDéroul_Table.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
            // panDéroul_Table.setViewportView(table);
     
            //  -     -     -     -     -     -     -     -     -     -     -
     
            boutAjoutUneColon = new JButton("Ajout d'une colonne");
            boutAjoutUneColon.addActionListener(this);
            boutRetraitUneColon = new JButton("Retrait d'une colonne");
    //        boutRetraitUneColon.addActionListener(this);
     
            //  -     -     -     -     -     -     -     -     -     -     -
     
            this.add(panDéroul_Table, BorderLayout.CENTER);
            this.add(boutAjoutUneColon, BorderLayout.NORTH);
            this.add(boutRetraitUneColon, BorderLayout.SOUTH);
            this.pack();
            this.setVisible(true);
            this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        }   // Fin du constructeur de 'XYExtensTable_SSCCE'
            // </editor-fold>      // Fin de 'Initialisation des variables'
     
     
        // Boutons-Poussoirs Ajout / retrait d'une colonne
        @Override public void actionPerformed(ActionEvent évé)
        {   
            nbreColoTable = (byte) table.getColumnModel().getColumnCount();
            nbreColÀBR = (byte) (nbreColoTable - (NBRE_FIXE_COL_AVANT + NBRE_FIXE_COL_APRES));
            nbreColÀBR_AvantModif = nbreColÀBR;
     
            if (évé.getSource() == boutAjoutUneColon)
            {
                miseÀJourModèleEtStructTable(++nbreColÀBR, nbreColÀBR_AvantModif);
                boutRetraitUneColon.setEnabled(true);
            }
            else
            if (évé.getSource() == boutRetraitUneColon)
            {
                if (nbreColÀBR <= 0)
                    boutRetraitUneColon.setEnabled(false);
                else
                {
                    miseÀJourModèleEtStructTable(--nbreColÀBR, nbreColÀBR_AvantModif);
                    boutRetraitUneColon.setEnabled(true);
                }
            }
        }
     
     
       /** Remet à jour les données de 'monModTable' 
        * Note : table.getModel().addColumn(new TableColumn(libelléCol)); est seulement 
        * fourni dans 'DefaultTableModel', pas prévu par 'AbstractTableModel'.
        * Cette méthode est appelée suite à l'actionnement de l'un des deux boutons-poussoirs.
        * @param nbreColÀBR5 
        * @param nbreColÀBR_AvantModif5
        */
        public void miseÀJourModèleEtStructTable(byte nbreColÀBR5, byte nbreColÀBR_AvantModif5)
        {   
            this.remove(panDéroul_Table);
            this.remove(table.getTableHeader());
            this.revalidate();      // Tels Swing that an area of the window is dirty.
     
    //        byte nbreRangées = monModTable.élaborTtsRangéesTable();
            // table.setModel(monModTable);
     
            // nbreColoTable = (byte) monModTable.libellésColonnes.size();       // 4
    //        nbreColoTable = (byte) table.getColumnModel().getColumnCount();      // 3
            // Pour ce SSCCE, ce n'est toujours qu'une seule colonne à la fois qui est à ajouter ou à retirer.
    //        byte nbreColÀAjouter = (byte) ((nbreColÀBR5 - nbreColÀBR_AvantModif5));
            // Dernière colonne de radio-boutons après laquelle la prochaine sera à ajouter.
    //        byte idxDernColoBoutRad;
    /*
            if (nbreColÀAjouter > 0)
            {   
                coloDeTable = new TableColumn();
                coloDeTable.setMaxWidth(40);
                
                // Il faut des 'RendEdit_CellBoutRad' différents pour 'CellEditor' et 'CellRenderer'.
                // edit_CellBoutRad = new RendEdit_CellBoutRad();
                coloDeTable.setCellEditor  (new RendEdit_CellBoutRad());
                coloDeTable.setCellRenderer(new RendEdit_CellBoutRad());
                
                table.addColumn(coloDeTable);
                
                nbreColoTable++;    // ou nbreColoTable = (byte) table.getColumnCount();
                idxDernColoBoutRad = (byte) (nbreColoTable - NBRE_FIXE_COL_APRES -1);
               /* La nouvelle colonne est toujours ajoutée à l'extrême droite de la table et 
                * est donc à déplacer à la première des NBRE_FIXE_COL_APRES dernières colonnes. * /
                table.moveColumn(nbreColoTable -1, idxDernColoBoutRad);
                // nbreColÀAjouter--;
    */            
        //        monModTable.fireTableDataChanged();
                monModTable.fireTableStructureChanged();
                monModTable = new MonModTable(nbreColÀBR5, nbreColÀBR_AvantModif5);
                table.setModel(monModTable);
                // Le groupage fonctionnel des radio-boutons est fait par 'GestionnObjetsDUneRangée'.
    /*        }   // fin de 'if (nbreColÀAjouter > 0)'
            else if (nbreColÀAjouter < 0)      // nbreColÀAjout est négatif => Colonnes à ôter.
            {
                nbreColoTable = (byte) table.getColumnCount();
                idxDernColoBoutRad = (byte) (nbreColoTable - NBRE_FIXE_COL_APRES -1);
                coloDeTable = table.getColumn(idxDernColoBoutRad);
                monModTable.libellésColonnes.remove((int) (NBRE_FIXE_COL_AVANT + nbreRangées));
                table.removeColumn(coloDeTable);
                // Deselects the columns from index0 to index1, inclusive.
    //            table.removeColumnSelectionInterval(
    //                (int) NBRE_FIXE_COL_AVANT, (int)(NBRE_FIXE_COL_AVANT - nbreColÀAjouter));
            }
    */        
            panDéroul_Table.setViewportView(table);
            this.add(BorderLayout.CENTER, panDéroul_Table);
            this.repaint();         // Tells the layout manager to recalculate the layout.
        }
     
     
        /**
         * @param args the command line arguments
         * @throws ClassNotFoundException
         * @throws InstantiationException
         * @throws IllegalAccessException
         * @throws UnsupportedLookAndFeelException
         */
        public static void main(String[] args) throws ClassNotFoundException, InstantiationException, 
                                             IllegalAccessException, UnsupportedLookAndFeelException
        {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
            SwingUtilities.invokeLater(new Runnable()
            {
                @Override public void run()     // Implements method from java.lang.Runnable
                {
                    new XYExtensTable_SSCCE().initUI();
                }
            }                         );
        }
     
            //  -     -     -     -     -     -     -     -     -     -     -
     
        // Pas "extends JRadioButton".
                class RendEdit_CellBoutRad extends AbstractCellEditor
                    implements TableCellRenderer, TableCellEditor, ActionListener
        {
            private final JRadioButton boutonRadio;
     
     
            public RendEdit_CellBoutRad()
            {
                this.boutonRadio = new JRadioButton();
                boutonRadio.addActionListener(this);    // Leaking 'this' in constructor.
                boutonRadio.setHorizontalAlignment(SwingConstants.CENTER);
                boutonRadio.setOpaque(false);
            }
     
     
           /* Implements the unique method from 'javax.swing.table.TableCellRenderer' interface.
            * https://docs.oracle.com/javase/7/docs/api/index.html?javax/swing/table/TableCellRenderer.html 
            * L'objet renvoyé par 'getValueAt()' dans 'MonModTable' est traité ici.     */
            @Override public Component getTableCellRendererComponent(JTable table, Object valeur, 
                                boolean estSélectionné, boolean aLeFocus, int rangée, int colonne)
            {
                boutonRadio.setSelected(Boolean.TRUE.equals(valeur));
                boutonRadio.setOpaque(true);
    //            boutonRadio.setBackground(java.awt.Color.yellow);
                return boutonRadio;
            }
     
     
            // This method is called when a cell value is edited by the user.
            // Implements method from 'javax.swing.table.TableCellEditor'.
            @Override public Component getTableCellEditorComponent(JTable table, Object valeur, 
                                                 boolean estSélectionné, int rangée, int colonne)
            {
                boutonRadio.setSelected(Boolean.TRUE.equals(valeur));
                return boutonRadio;
            }
     
     
            // Implements method from 'java.awt.event.ActionListener'.
            @Override public void actionPerformed(ActionEvent e)
            {   
                stopCellEditing();
            }
     
     
            // Implements method from 'javax.swing.CellEditor'. JTable or JTree
            @Override public Object getCellEditorValue()
            {   // 'isSelected()' is inherited from class javax.swing.AbstractButton .
                return boutonRadio.isSelected();        // boolean
            }
        }
    }
    et classe MonModTable
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
    365
    366
    367
    368
    369
    370
    371
    372
    373
    374
    375
    376
    377
    378
    379
    380
    381
    382
    383
    384
    385
    386
    387
    388
    389
    390
    391
    392
    393
    394
    395
    396
    397
    /*
    /* @author Chavadam
     */
     
    package xyextenstable_sscce;
     
    import java.math.BigDecimal;
    import java.util.List;          // Interface
    import java.util.ArrayList;
    import java.beans.PropertyChangeEvent;
    import java.beans.PropertyChangeListener;
    import javax.swing.table.AbstractTableModel;
    // import static xyextenstable_sscce.XYExtensTable_SSCCE.DEBUG;
     
     
    /**
     * Swing ne prévoit pas la possibilité d'implémenter un "Listener" (= "guêteur") 
     * cellule par cellule --> en implémenter un à 'class XYExtensTable_SSCCE'.
     */
        class MonModTable extends AbstractTableModel implements PropertyChangeListener
    {   
        // <editor-fold defaultstate="collapsed" desc="Déclaration des variables">
        private List<BigDecimal> échelleValeurs1eColonne;
     
        private byte nbreColÀBR3;
        private byte nbreColÀBR_AvantModif3;
        // Horizontalement
        // Not 'private' because capability to remove items from XYExtenTable_SSCCE.
             List<String> libellésColonnes;
        public List<String> libelAutresColQueColÀBP;
        // Formats des contenus des colonnes : String, RendeurEditeur_CelluleBoutonRadio, BigDecimal
        public final List<Object> renduCellCol;
     
       /* 'idxRangée' dans 'Composer la base des rangées de données de la table' dans 
        * 'élaborTtsRangéesTable()' doit être accessible dans '@Override propertyChange(evt)'. */
        private byte idxRangée;
        // Avant et après le groupe d'un nombre variable de colonnes à radio-boutons.
                static final byte NBRE_FIXE_COL_AVANT = 2;
                static final byte NBRE_FIXE_COL_APRES = 1;
    //     private int colCommencementBoutRadio = NBRE_FIXE_COL_AVANT;
           /* Pour 'getColumnClass()', 'isCellEditable()', 'getValueAt()', 'setValueAt()'
            * et 'getCellRenderer()'.             */
                     int colFinBoutRadio = 0;
        // Contient et gère les rangées de boutons-radios et leurs regroupements.
        private List<GestionnObjetsDUneRangée> gestionnDonnéesTteLaTable;
        private GestionnObjetsDUneRangée gestionObjDUneRang;
        // </editor-fold>      // Fin de 'Déclaration des variables'
     
     
        MonModTable(byte nbreColÀBR2, byte nbreColÀBR_AvantModif2)
        {   
           /* Appelle le constructeur de 'AbstractTableModel'. This abstract class provides
            * default implementations for most of the methods in the TableModel interface.
            * https://docs.oracle.com/javase/7/docs/api/javax/swing/table/AbstractTableModel.html
            * Pas nécessaire car les méthodes requises sont implémentées dans cette sous-classe-ci.
            */ 
            // super();
     
           /* Pour '@Override public Class<?> getColumnClass(int colo)', isCellEditable(), 
            * 'élaborTtsRangéesTable()', ...        */
            this.nbreColÀBR3 = nbreColÀBR2;
            this.nbreColÀBR_AvantModif3 = nbreColÀBR_AvantModif2;
     
            // Instanciée ici aussi pour éviter 'NullPointerException' dans 'getRowCount()'.
            gestionnDonnéesTteLaTable = new ArrayList<>();
     
            // Verticalement
            libellésColonnes = new ArrayList<>();
            libellésColonnes.add("A");
            libellésColonnes.add("B");
            // Entre ces colonnes-ci viendront s'insérer celles des boutons-radios.
            libellésColonnes.add("Z");
     
            // Format des contenus des colonnes - Voir 'ajustageLargeurCol()'
           /*  renduCellCol = new ArrayList(new Collection{"#.0", "#.00", "#.0"});  */
            renduCellCol = new ArrayList();
            renduCellCol.add("#.0");    // A
            renduCellCol.add("#.00");   // B
            // Entre ces colonnes-ci viendront s'insérer celles des boutons-radios.
            renduCellCol.add("#.0");    // C
     
            élaborTtsRangéesTable();    // Assign a return value to the new variable
        }
     
     
       /** Appelée
     - lors de l'initialisation, dans 'élaborTtsRangéesTable()', dans la boucle 
     empilant des 'MonObjet's.
        */
        @Override public void propertyChange(PropertyChangeEvent evt)
        {
            Object objet2 = evt.getSource();
     
            if (objet2 == gestionObjDUneRang)
            {   
                switch (evt.getPropertyName())
                {   case "objetsDUneTranPuiss":
                        // 'supportChang_tPropri'
    //                    ((MonObjet) evt.getNewValue()).getSupportChangtPropri_Obj().addPropertyChangeListener(this);
                        break;
                // MonModèleTable
                    case "objectBijgevoegd":
                        // Constructeur
                        break;
                    case "objectenVanEenRij":
                        // Ne passe jamais par ici.
                        break;
                //    fireTableDataChanged();
                    case "dataVanEenRij":
                        // Ne passe jamais par ici.
                        break;
                }
            } else
            if (objet2 instanceof MonObjet)
            {
                switch (evt.getPropertyName())
                {  case "juistVeranderd":
                        // Constructeur et utilisation
                        break;
                    case "ObjMgrDezeRijIsBijgevoegd":
                        // Constructeur
                        break;
                }
                ;
    //             int idx_Tran = gestionObjDUneRang.getObjetsDUneRangée().indexOf(objet2);
                // short[] coordBR = ((MonObjet) objet2).getCoordBP();
                // fireTableRowsUpdated(coordBR[0]-1, coordBR[0]-1);       // Rien que la rangée concernée.
            }
        }
     
     
       /** Méthode exigée par l'interface 'AbstractTableModel' --> Nom en Anglais
        * Implements method from javax.swing.table.TableModel
        * @return libellésColonnes.size()
        */
        @Override public int getColumnCount()
        {   
            return libellésColonnes.size();
            // return gestionObjDUneRang.getObjetsDUneRangée().size();
        }
     
     
       /** Méthode exigée par l'interface 'AbstractTableModel'
        * 'gestionnDonnéesTteLaTable' doit avoir été instanciée dans la méthode-constructeur.
        * @return 
        */
        @Override public int getRowCount()
        {   
            return gestionnDonnéesTteLaTable.size();
        }
     
     
        // Méthode existante mais non exigée par l'interface 'AbstractTableModel'
        @Override public String getColumnName(int col)
        {   
            return libellésColonnes.get(col);
        }
     
     
       /** If you did not specify a renderer for a particular column, then the table 
        * invokes the table model's getColumnClass method, which gets the data type 
        * of the column's cells.
        * Challenge : Le nombre de colonnes est variable.
        * @param colo
        * @return 
        */
        @Override public Class<?> getColumnClass(int idxColo1)
        {   
            if (idxColo1 < NBRE_FIXE_COL_AVANT)
            {
                return BigDecimal.class;
            } else
            if (idxColo1 >= NBRE_FIXE_COL_AVANT && idxColo1 < colFinBoutRadio)
            {
                return MonObjet.class;      // Contient une propriété JRadioButton
            } else
            if (idxColo1 >= colFinBoutRadio &&
                idxColo1 < NBRE_FIXE_COL_AVANT + nbreColÀBR3 + NBRE_FIXE_COL_APRES)
            {
                return BigDecimal.class;
            } else            // Sans 'else' ni ';' : "Confusing identation"
            return null;      // Jamais
        }
     
     
        // Seules les colonnes à boutons-radios sont éditable.
        @Override public boolean isCellEditable(int rang, int idxColo2)
        {   
            if (idxColo2 < NBRE_FIXE_COL_AVANT)
                return false;
            else if (idxColo2 >= NBRE_FIXE_COL_AVANT && idxColo2 < colFinBoutRadio)
                return true;
            else if (idxColo2 >= colFinBoutRadio  &&
                     idxColo2 < NBRE_FIXE_COL_AVANT + nbreColÀBR3 + NBRE_FIXE_COL_APRES)
                return false;
     
            return false;       // Jamais
        }
     
     
       /** Méthode exigée par l'interface 'AbstractTableModel'.
        * Le contCellTabl renvoyé sera traité par la méthode 
        * 'TableCellRenderer.getTableCellRendererComponent (JTable table, 
        * Object value, boolean isSelected, boolean hasFocus, int row, int column)' 
        * dans 'XYExtensTable_SSCCE'. 
        * Renvoie un String ou un Boolean.      */
        @Override public Object getValueAt(int rang, int idxColo3)
        {   
            Object contCellTabl = getValeurÀ(rang, idxColo3);
     
            if (idxColo3 < NBRE_FIXE_COL_AVANT)
            {
                return contCellTabl;        // BigDecimal
            } else
            if (idxColo3 >= NBRE_FIXE_COL_AVANT && idxColo3 < colFinBoutRadio)
            {   // Bouton radio : Boolean       boolean --> Boolean
                if ((Class<?>)contCellTabl.getClass() == MonObjet.class)
                {
                    Boolean étaitSélect_é = (Boolean)((MonObjet) contCellTabl).isSélectionné();
                    return étaitSélect_é;
                } 
            } else
            if (idxColo3 >= colFinBoutRadio  &&
                     idxColo3 < NBRE_FIXE_COL_AVANT + nbreColÀBR3 + NBRE_FIXE_COL_APRES)
            {
                return contCellTabl;        // BigDecimal
            }
            ;
            return null;
        }
     
     
       /** Need to implement this method because some table's data can be manually changed.
        * A pour but de n'activer ou désactiver le bouton-radio que d'une seule cellule de 
       ¨* la table. Si c'est l'activer, les conséquences pour les autres cellules de la rangée
        * sont gérées par la méthode 'miseàjourTousBP_Rangée()' dans 'gestionnObjetsDUneRangée', 
        * elle-même appelée ici par 'setSélectionné()' dans 'monObjet'.
        * Overrides method from javax.swing.table.AbstractTableModel
        */
        @Override public void setValueAt(Object bpActionné, int idxRang2, int idxColo4)
        {
            if (idxColo4 >= NBRE_FIXE_COL_AVANT && idxColo4 < colFinBoutRadio)
            {
                MonObjet contCellTabl = (MonObjet) getValeurÀ(idxRang2, idxColo4);
                contCellTabl.setSélectionné(Boolean.TRUE.equals(bpActionné));
            }
           /* It works without 'fireTableCellUpdated(..;)' if you change the data by 
            * using the table editor, because once the editor is closed, the cell will 
            * get repainted by the table.
            * However, if you manually invoke 'setValueAt()', then it won't work. 
            * Also if you don't invoke that method, the 'no event' will be fired for 
            * a TableModelListener that might be added to the TableModel.
            */
            fireTableCellUpdated(idxRang2, idxColo4);
        }
     
     
        // Méthode appelée par 'getValueAt() et 'setValueAt()'.
        Object getValeurÀ(int rang, int idxCol5)
        {   // Il y a [NBRE_RANG] gestionnObjetsDUneRangée dans 'gestionnDonnéesTteLaTable'.
            return gestionnDonnéesTteLaTable.get(rang).getObjetsDUneRangée().get(idxCol5);
        }
     
     
        List<GestionnObjetsDUneRangée> getDonnéesTteLaTable()   // ArrayList<Object>
        {   
            return gestionnDonnéesTteLaTable;
        }
     
        //  -     -     -     -     -     -     -     -     -     -     -
     
       /** Méthode appelée dans 'miseÀJourModèleEtStructTable()' ou dans 'MonModTable()'
        * 
        */ 
        private byte élaborTtsRangéesTable()
        {   
           /* Pour 'getColumnClass()', 'isCellEditable()', 'getValueAt()', 'setValueAt()'
            * et 'getCellRenderer()'.             */
            colFinBoutRadio = NBRE_FIXE_COL_AVANT + this.nbreColÀBR3;
    //        byte nbreCellulesChaqueRangée = (byte)(colFinBoutRadio + NBRE_FIXE_COL_APRES);
            MonObjet objet;
     
            // System.gc();
     
            // <editor-fold defaultstate="collapsed" desc="Insérer les libellés des colonnes aux boutons-radio">
            if (this.nbreColÀBR3 == this.nbreColÀBR_AvantModif3)
            {   // Colonne ni à ajouter, ni à ôter.
     
            } else 
            if (this.nbreColÀBR3 > this.nbreColÀBR_AvantModif3)
            {   
                for (byte nièmeCol = 0 ; // (byte) nbreColÀBR_AvantModif3 ; 
                                            nièmeCol < nbreColÀBR3 ; nièmeCol++)
                {   
                    libellésColonnes.add(NBRE_FIXE_COL_AVANT + nièmeCol, 
                                         ((Byte) nièmeCol).toString());
                //    renduCellCol.add(NBRE_FIXE_COL_AVANT + nièmeCol, "-");
                }
            } else      // Fin de 'if (nbreColÀBR3 > nbreColÀBR_AvantModif3)'
            // Suppression d'une colonne
            if (this.nbreColÀBR3 < this.nbreColÀBR_AvantModif3)
            {  /* Le nécessaire est fait par 'ajoutRetraitCol(int pos, byte nbreColÀBR3, 
                * List columnData)' dans 'XYExtensTable_SSCCE'               */
            };
            // Fin de "Ajuster le nombre de colonnes de boutons-radios".
            // </editor-fold>
     
            // <editor-fold defaultstate="collapsed" desc="Composer une série de valeurs factices pour les premières colonnes">
           /* Avant d'élaborer les ArrayList 'gestionObjDUneRangée', élaborer 
            * l'ArrayList 'échelleValeurs1eColonne'.        */
            BigDecimal uneValeur = new BigDecimal(10);
            final BigDecimal multiplicateur = new BigDecimal(1.414213);
            échelleValeurs1eColonne = new ArrayList<>(nbreColÀBR3);
     
            for (byte num = 0 ; num < (nbreColÀBR3 +1) ; num++)
            {   
                uneValeur = uneValeur.multiply(multiplicateur);
                échelleValeurs1eColonne.add(uneValeur);
            }
     
            byte nbreRangées = (byte) (échelleValeurs1eColonne.size() -1);
            // </editor-fold>
     
            // <editor-fold defaultstate="collapsed" desc="Composer la base des rangées de données de la table">
           /* c à d la liste 'gestionnDonnéesTteLaTable' de toutes les listes 'gestionObjDUneRang' 
            * de <nbreCellulesChaqueLigne> cellules, en fonction du nombre <nbreColÀBP>.
            * 'Object' est 'MonObjet' lorsqu'il s'agit des colonnes ajoutées dynamiquement.     */
            gestionnDonnéesTteLaTable = new ArrayList<>(nbreRangées);
     
            // 'idxRangée' est aussi utilisée par '@Override ... propertyChange(PropertyChangeEvent evt)
            for (idxRangée = 0 ; idxRangée < nbreRangées ; idxRangée++)
            {   
                gestionObjDUneRang = new GestionnObjetsDUneRangée(nbreColÀBR3);
     
               /* Support pour propriété qui change.
                * Seule 'monModTable' est enregistrée comme "listener" (= guêteur) 
                * de toutes les propriétés de 
                * - chaque instanciation de 'gestionObjDUneRang' (dont 'objetsDUneTranPuiss') 
                * cela dans          de 'MonModTable' > '
                * - chaque instanciation de 'MonObjet', cela dans la 'propertyChange()'
                */
               /* Pour que le support de changement de propriété 'suppChanProp_GestRang' 
                * propre à ce 'gestionObjDUneRang' enregistre 'MonModTable' comme guêteur 
                * de changement de chacune des propriétés de ce 'gestionObjDUneRang'.
                */
                // gestionObjDUneRangée.getSuppoChangePropri().addPropertyChangeListener(this);
                gestionObjDUneRang.ajoutGuêteurChangtPropri_GestRang(this);
     
                // Première colonne
                // Valeur de la deuxième cellules de la rangée précédente.
                gestionObjDUneRang.getObjetsDUneRangée().add(échelleValeurs1eColonne.get(idxRangée));
                // Deuxième colonne
                gestionObjDUneRang.getObjetsDUneRangée().add(échelleValeurs1eColonne.get(idxRangée+1));
                // Entre deuxième et dernière colonne
                for (byte col = NBRE_FIXE_COL_AVANT ; col < NBRE_FIXE_COL_AVANT + nbreColÀBR3 ; col++)
                {   
                    objet = new MonObjet(idxRangée, col);
     
                   /* Pour que le support de changement de propriété 'suppChanProp_Obj' 
                    * propre à ce nouvel 'objet' enregistre 'monModTable' comme guêteur 
                    * de changement de chacune des propriétés de ce nouvel 'objet'.   */
                    objet.ajoutGuêteurChangtPropri_Obj(this);
     
                   /* Grâce à 'setGestionnObjetsDUneRangée(...)' dans 'ajoutObjetÀRangée(...)' 
                    * dans 'GestionnObjetsDUneRangée', la propriété 'gestionObjDUneRang2' dans 
                    * 'contCellTabl' réfèrera au 'gestionObjDUneRang' d'ici ('monModTable')   */
                    gestionObjDUneRang.ajoutObjetÀRangée(objet);
    /*              
                    if (col == NBRE_FIXE_COL_AVANT)
                        objet.setSélectionné(true);
    */
                }
     
                // Dernière colonne
                gestionObjDUneRang.getObjetsDUneRangée().add(BigDecimal.ZERO);
     
                gestionnDonnéesTteLaTable.add(gestionObjDUneRang);
     
           /* L'activation initiale d'un bouton-radio par rangée n'est exécutable que 
            * lorsque le tout dernier bouton-radio d'une rangée - c à d les [nbreColÀBR3] 
            * objets - ont été ajoutés, c à d :
            * - au terme de l'empilement de tous les boutons-radios d'une rangée dans 
            * 'Composer la base des rangées de données de la table'.
            * - au cours de l'utilisation de la table.
            * 'setSélectionné()' ne peut donc avoir lieu qu'ensuite.
            ((MonObjet) gestionObjDUneRang.getObjetsDUneRangée()
                                .get(NBRE_FIXE_COL_AVANT)).setSélectionné(true);
            */
            // The fireXxx() methods simply instruct the view to query the model via getValueAt(). 
    //            fireTableStructureChanged();
            }   // Fin de "Composer la base des rangées de données de la table"
            // </editor-fold>// </editor-fold>// </editor-fold>// </editor-fold>
     
            // fireTableStructureChanged();     // plutôt dans 'miseÀJourModèleEtStructTable()'
            return nbreRangées;
        }
    }
    * Question à se poser : À chaque événement (actionnement du bouton-poussoir d'ajoute
    * d'une colonne, ...), faut-il ajouter une colonne (de boutons-radios) ou bien reconcevoir
    * intégralement le modèle de la table ? Dans cette application, il faut opter pour la
    * deuxième option car la table se caractérise par le fait qu'à chaque événement ...
    * - l'échelle des valeurs en première colonne, subséquemment les valeurs de toutes
    * les cellules, ainsi que le nombre de rangées et de colonnes sont recalculés. Il
    * faut donc reconcevoir le modèle et l'ArrayList des intitulés des colonnes à chaque
    * événement complètement.
    * - un groupe variable de colonnes à boutons-radios pourrait être inséré dans la table
    * existante, mais comme il faut quand même recalculer tout le modèle de la table, autant
    * repasser à celle-ci un nouveau modèle à chaque événement.
    * En conséquence, on n'emploie donc pas de 'TableColumn tc = new TableColumn();'
    * ni de 'table.addColumn(tc);'

    J'effaye les férus en JTable ?
    Merci d'avance à celui qui tente de m'aider.

    P.S.: Short, Self Contained, Correct (Compilable) Example
    "Ah oui ! Juste encore cette toute dernière petite question ..." (Columbo - Peter Falk)

  3. #3
    Membre régulier
    Profil pro
    Inscrit en
    Décembre 2008
    Messages
    263
    Détails du profil
    Informations personnelles :
    Âge : 73
    Localisation : Belgique

    Informations forums :
    Inscription : Décembre 2008
    Messages : 263
    Points : 121
    Points
    121
    Par défaut
    Re-bonjour.
    Un résumé-simplification s'impose :

    - J'ai une première application de JTable - Table_NRangées_BoutRadio - à nombre de colonnes et de rangées fixes (3 et 3) et dont toutes les cellules contiennent des JRadioButtons, regroupés par rangées de la table.
    Elle est intitulée "JTable de JRadioButtons, regroupés par rangée" et se trouve dans https://www.developpez.net/forums/d1...roupes-rangee/
    Le code (en 4 classes) se trouve dans la 2e intervention (07/06/2016) de la discussion.
    La séquence des appels des méthodes se trouve dans la 3e intervention (17/04/2017) de cette même discussion.
    Fonctionne complètement comme il faut (l'initialisation de la table et puis l'emploi des radio-boutons).
    Cette application sert surtout à la comparaison la de suite des méthodes appelées avec celle de l'application suivante.

    - J'ai une deuxième application de JTable - XYExtensTable_SSCCE -
    à nombre de colonnes et de rangées variable,
    précédées par 2 colonnes de valeurs décimales et suivies d'une colonne de valeurs décimales,
    et dont les colonnes intermédiaires contiennent des JRadioButtons, regroupés par rangées de la table.
    Elle est intitulée "Q: JTable dimensions variables et qques colonnes à JRadioButton - 2" et se trouve dans la présente discussion.
    Le code (en 4 classes) se trouve dans la 2e intervention (07/04/2017).
    La séquence des appels des méthodes se trouve ci-dessous (= la 3e intervention) :

    Ordre des appels de méthodes
    D'abord s'arranger pour que la table affiche deux colonnes de boutons-radios entre les colonnes de valeurs décimales.
    Le bouton-radio activé avant le dernier actionnement est en colonne d'indexe 2 (3e colonne), et ensuite en 3, de la 1e rangée.
    Le bouton-radio effectivement actionné est d'abord celui de la colonne d'indexe 3 et puis 2.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    @ isCellEditable(0,3)	MonModTable	true
    @ getColumnClass(3)	MonModTable		MonObjet.class
     
    getCellRenderer(0,0)	XYExtensTable_SSCCE	renduCell
    prepareRenderer(0,0)	XYExtensTable_SSCCE
    	super.prepareRenderer(0,0)	XYExtensTable_SSCCE
    		getValueAt(0,0)	MonModTable	BigDecimal
     
    getCellRenderer(0,1)	XYExtensTable_SSCCE	renduCell
    prepareRenderer(0,1)	XYExtensTable_SSCCE
    	super.prepareRenderer(0,1)	XYExtensTable_SSCCE
    		getValueAt(0,1)	MonModTable	BigDecimal
     
    La première colonne à radios-boutons. Ne commence donc pas avec la
    colonne dont le radio-bouton vient d'être actionné = faute.
    getCellRenderer(0,2)	XYExtensTable_SSCCE	rendu_CellBoutRad
    prepareRenderer(0,2)	XYExtensTable_SSCCE
    	super.prepareRenderer(0,2)	XYExtensTable_SSCCE
    		getValueAt(0,2)	MonModTable
    			isSélectionné()	true
    Le b-r qui vient d'être quitté ...
    		@ getTableCellRendererComponent(true,0,2)
    			boutonRadio.setSelected(true)
     
    getCellRenderer(0,3)	XYExtensTable_SSCCE	rendu_CellBoutRad
    prepareRenderer(0,3)	XYExtensTable_SSCCE
    	super.prepareRenderer(0,3)	XYExtensTable_SSCCE
    		getValueAt(0,3)	MonModTable
    			isSélectionné()	false
    Le b-r qui vient d'être actionné reste à 'false' !?
    		@ getTableCellRendererComponent(false,0,3)
    			boutonRadio.setSelected(false)
     
    getCellRenderer(0,4)	XYExtensTable_SSCCE	renduCell
    prepareRenderer(0,4)	XYExtensTable_SSCCE
    	super.prepareRenderer(0,4)	XYExtensTable_SSCCE
    		getValueAt(0,4)	MonModTable	BigDecimal
    Ce qui ne fonctionne pas encore : En cliquant sur un deuxième radio-bouton, le radio-bouton qui était précédemment activé ne s'éteint pas et le nouveau radio-bouton activé ne s'allume pas.

    Ce qu'il faut trouver : Pourquoi dans la première application (qui fonctionne complètement bien), Swing appelle d'abord 'setValueAt(0,1)' de la cellule sur laquelle on vient de cliquer, et dans la deuxième application pas (pas de '@ getTableCellEditorComponent(false,0,3)' ni de '@ setValueAt(0,3)').
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    isCellEditable(0,1)	MonModTable	true
    La colonne dont le radio-bouton vient d'être actionné.
    @ getValueAt(0,1)(0,0)	MonModTable
    	isSélectionné()	false
    @ getTableCellEditorComponent(false,0,1)(false,0,0)	RendEdit_CellBoutRad
    	boutonRadio.setSelected(false)
    		@ setValueAt(0,1)(0,0)	MonModTable
    			getValeurÀ(0,1)(0,0)
    			setSélectionné(true)	MonObjet
    				miseàjourTousBP_Rangée()	GestionnObjetsDUneRangée
    Merci d'avance à ceux qui tentent d'aider tous ceux qui sont intéressés par une table aux dimensions variables dynamiquement et dont quelques colonnes contiennent des objets 'avancés'.
    "Ah oui ! Juste encore cette toute dernière petite question ..." (Columbo - Peter Falk)

  4. #4
    Membre régulier
    Profil pro
    Inscrit en
    Décembre 2008
    Messages
    263
    Détails du profil
    Informations personnelles :
    Âge : 73
    Localisation : Belgique

    Informations forums :
    Inscription : Décembre 2008
    Messages : 263
    Points : 121
    Points
    121
    Par défaut
    Bonsoir,
    J'ai trouvé la solution.
    Voici les quatre classes de mon SSCEE à copier :

    - XYExtensTable_SSCCE (371 l.)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
    365
    366
    367
    368
    369
    370
    371
     
    package xyextenstable_sscce;
     
    import java.awt.BorderLayout;
    import java.awt.Component;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
     
    import javax.swing.JFrame;
    import javax.swing.AbstractCellEditor;
    import javax.swing.JRadioButton;
    import javax.swing.JScrollPane;
    import javax.swing.JTable;
    import javax.swing.table.DefaultTableCellRenderer;      // RenduCol3
    import javax.swing.table.TableCellEditor;
    import javax.swing.table.TableCellRenderer;
    import javax.swing.table.TableColumn;
    import javax.swing.JButton;
    import javax.swing.SwingConstants;          // CENTER
    import javax.swing.SwingUtilities;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;     // main()
     
    import static xyextenstable_sscce.MonModTable.NBRE_FIXE_COL_AVANT;
    import static xyextenstable_sscce.MonModTable.NBRE_FIXE_COL_APRES;
     
     
    /**
     * 
     */
    public class XYExtensTable_SSCCE extends JFrame
        // Seul.t pour les deux boutons-poussoirs 'boutAjoutUneColon' et 'boutRetraitUneColon'.
                                     implements ActionListener     // , TableModelListener
    {   
        // <editor-fold defaultstate="collapsed" desc="Déclarations des variables">
        // If 'final', error : Variable 'monModTable' not initialized in the default constructor.
        private MonModTable monModTable;
        private JScrollPane panDéroul_Table;
        private JTable table;
     
        private TableColumn coloDeTable;
        private byte nbreColoTable;
        private static final byte LARGEUR_MAX_COL = 40;
        private int largTotTable;
     
        private byte nbreColÀBR_AvantModif, nbreColÀBR;
        private JButton boutAjoutUneColon;
        private JButton boutRetraitUneColon;
     
                static final boolean DEBUG = false;
        // </editor-fold>
     
     
        protected void initUI()     // Au lieu de 'public XYExtensTable_SSCCE()'
        {   
            // <editor-fold defaultstate="collapsed" desc="Initialisation des variables">
            monModTable = new MonModTable(nbreColÀBR = 0, nbreColÀBR_AvantModif = 0);
     
            table = new JTable(monModTable)
            {   
                DefaultTableCellRenderer renduCell;
                RendEdit_CellBoutRad rendu_CellBoutRad;
     
                // Bloc constructeur de la JTable.
                {   // Méthode héritée de JLabel
                   /* As we are calling 'super.getTableCellRendererComponent', we need to extend
                    * 'DefaultTableCellRenderer'; not simply implement 'TableCellRenderer'.   */
                   /* If we do not extend from 'DefaultTableCellRenderer', we will need to be 
                    * careful to find ways to indicate whether the cell has focus or not, is 
                    * editable or not, and is selected or not. If we don't, then those cells 
                    * won't match the appearance of other table cells.      */
                    // Pour le coloriage de fond des lignes de la table_TtesCombMach_TranPuiss
                    // Appelée dans 'ajustageLargeurCol()'.
                    renduCell = new DefaultTableCellRenderer(); // Méthode héritée de JLabel
                    renduCell.setHorizontalAlignment(SwingConstants.CENTER);
                   /* Utilisée par 'prepareRenderer(renduCelluleTableÀPréparer,...' 
                    * et par 'getCellRenderer()'.           */
                    rendu_CellBoutRad = new RendEdit_CellBoutRad();
                    // setRowHeight(55);    // Rendu inopérationnel par 'ajustageLargeurCol()'.
                }   // Fin du bloc constructeur de la JTable.
     
     
                // Overrides method from javax.swing.JTable
                @Override public TableCellRenderer getCellRenderer(int rang, int col)
                {   
                    // Ici appel de la méthode de minimalisation de la largeur de la colonne ?
     
                    if (col < NBRE_FIXE_COL_AVANT)
                    {
                        return renduCell;
                    }
                    else
                    if (col >= NBRE_FIXE_COL_AVANT && col < monModTable.idxDernColÀBoutRadio1 +1)
                    {
                        return rendu_CellBoutRad;
                    }
                    else        
                    if (col >= (monModTable.idxDernColÀBoutRadio1 +1) &&
                        col < monModTable.idxDernColÀBoutRadio1 +1 + NBRE_FIXE_COL_APRES)
                    {
                        return renduCell;
                    }
                    else            // Sans 'else' ni ';' : "Confusing identation"
                        return null;      // Jamais
                }
     
     
               /* Prepares the renderer by querying the data model for the 
                * value and selection state of the cell at row, column.
                * With Java, you can specify cell renderers and editors either by 
                * column or by data type.       */
                @Override public Component prepareRenderer(
                        TableCellRenderer renduCelluleTableÀPréparer, int rang, int col)
                {   // La classe parente de 'JTable' est 'javax.swing.JComponent'.
                   /* Pour toutes les cellules des colonnes ne contenant pas un objet de type 'MonObjet' :
                    * - 'renduCelluleTableÀPréparer' contient un objet de type 'DefaultTableCellRenderer'.
                    * - 'compos' contient un objet de type 'DefaultTableCellRenderer'.
                    * Pour toutes les cellules des colonnes contenant bien un objet de type 'MonObjet' :
                    * - 'renduCelluleTableÀPréparer' contient un objet de type 'RendEdit_CellBoutRad'.
                    * - 'compos' contient un objet de type 'JRadioBouton'.      */
                   /* Appel de 'getValueAt()' dans 'MonModTable'
                    * et de 'isSélectionné()" dans 'MonObjet'   */
                    final Component compos = 
                    // xyextenstable_sscce.XYExtensTable_SSCCE$RendEdit_CellBoutRad@7bd94799
                            super.prepareRenderer(renduCelluleTableÀPréparer, rang, col);
                    // compos.setBackground(Color.GRAY);
                    // monModTable.fireTableCellUpdated(rang, col);
     
                    return compos;
                }
            };      // Fin de 'new JTable(...'
     
            // <editor-fold defaultstate="collapsed" desc="Largeur chaque colonne = minimum">
            // </editor-fold>       // Fin de "Largeur chaque colonne = minimum"
     
            panDéroul_Table = new JScrollPane(table);
            // panDéroul_Table.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
            // panDéroul_Table.setViewportView(table);
     
            //  -     -     -     -     -     -     -     -     -     -     -
     
            boutAjoutUneColon = new JButton("Ajout d'une colonne");
            boutAjoutUneColon.addActionListener(this);  // --> 'implement ActionListener'.
            boutRetraitUneColon = new JButton("Retrait d'une colonne"); // Nog niet geïmplementeerd.
     
            //  -     -     -     -     -     -     -     -     -     -     -
     
            this.add(panDéroul_Table, BorderLayout.CENTER);
            this.add(boutAjoutUneColon, BorderLayout.NORTH);
            this.add(boutRetraitUneColon, BorderLayout.SOUTH);
            this.pack();
            // this.setLocationRelativeTo( null );
            this.setVisible(true);
            this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        }   // Fin de 'initUI()'
            // </editor-fold>      // Fin de 'Initialisation des variables'
     
     
       /* Boutons-poussoirs Ajout / retrait d'une colonne --> 'implement ActionListener'.
        * Implements method from 'java.awt.event.ActionListener'.
        * Once the editor is closed, the cell will get repainted by the table.
        * N'est pas appelée suite à l'actionnement d'un bouton-radio.
        */
        @Override public void actionPerformed(ActionEvent évé)
        {   
            nbreColoTable = (byte) table.getColumnModel().getColumnCount();
            nbreColÀBR = (byte) (nbreColoTable - (NBRE_FIXE_COL_AVANT + NBRE_FIXE_COL_APRES));
            nbreColÀBR_AvantModif = nbreColÀBR;
     
            if (évé.getSource() == boutAjoutUneColon)
            {
                miseÀJourModèleEtStructTable(++nbreColÀBR, nbreColÀBR_AvantModif);
            //    boutRetraitUneColon.setEnabled(true);
            } else
            if (évé.getSource() == boutRetraitUneColon)
            {
                if (nbreColÀBR <= 0)
                    boutRetraitUneColon.setEnabled(false);
                else
                {
                    miseÀJourModèleEtStructTable(--nbreColÀBR, nbreColÀBR_AvantModif);
                    boutRetraitUneColon.setEnabled(true);
                }
            } 
    /*        else
            if (évé.getSource() == instanceOf(MonObjet))
            {
                
            }
    */      // Au retour de cette méthode, la table est mise à jour à l'écran.
        }
     
     
       /** Remet à jour les données de 'monModTable' 
        * Question à se poser : À chaque événement (actionnement du bouton-poussoir d'ajoute 
        * ou de retrait d'une colonne), faut-il ajouter ou retirer une seule colonne de 
        * boutons-radios ou bien reconstruire intégralement le modèle de la table ? Dans 
        * cette application, il faut opter pour la deuxième option car ...
        * 
        * Note : table.getModel().addColumn(new TableColumn(libelléCol)); est seulement 
        * fourni dans 'DefaultTableModel', pas par 'AbstractTableModel'.
        * 
        * Cette méthode est appelée suite à l'actionnement de l'un des deux boutons-poussoirs.
        * @param nbreColÀBR5
        * @param nbreColÀBR_AvantModif5
        */
        public void miseÀJourModèleEtStructTable(byte nbreColÀBR5, byte nbreColÀBR_AvantModif5)
        {   
            this.remove(panDéroul_Table);
            this.remove(table.getTableHeader());
            this.revalidate();      // Tells Swing that an area of the window is dirty.
     
            monModTable = new MonModTable(nbreColÀBR5, nbreColÀBR_AvantModif5);
            table.setModel(monModTable);
            nbreColoTable = (byte) monModTable.getColumnCount();
     
            // Pour ce SSCCE, ce n'est toujours qu'une seule colonne à la fois qui est à ajouter ou à retirer.
            byte nbreColÀAjouterOuSupprimer = (byte) (nbreColÀBR5 - nbreColÀBR_AvantModif5);
            // Dernière colonne de radio-boutons après laquelle la prochaine sera à ajouter.
            byte idxDernColoBoutRad;
     
            if (nbreColÀAjouterOuSupprimer > 0)
            {   
                for (idxDernColoBoutRad = NBRE_FIXE_COL_AVANT ; 
                     idxDernColoBoutRad < nbreColoTable - NBRE_FIXE_COL_APRES ; 
                     idxDernColoBoutRad++)
                {   
                    coloDeTable = table.getColumnModel().getColumn(idxDernColoBoutRad);
                    coloDeTable.setPreferredWidth(30);
                    coloDeTable.setMaxWidth(40);
     
                   /* With Java, one can specify cell renderers and editors either by column 
                    * or by data type.
                    * We specify a renderer and an editor for this particular column -->
                    * 'monModTable.getColumnClass()' will not be called.
                    * Il faut des 'RendEdit_CellBoutRad' différents pour 'CellEditor' et 
                    * pour 'CellRenderer' parce que ...
                    * Unique cell renderer et cell editor pour tous les BR de chaque colonne à BRs.
                    * 'setCellEditor(TableCellEditor)' sets the editor for the currently active 
                    * cell only and will be lost once it's no longer active.
                    */
                    // jTable.getColumnModel().getColumn(2).setCellEditor(tableCellEditor);
                    // coloDeTable.setHeaderRenderer(getHeaderRenderer(0));
                    RendEdit_CellBoutRad rendEdit_CellBoutRad = new RendEdit_CellBoutRad();
                    coloDeTable.setCellRenderer(rendEdit_CellBoutRad);
                    coloDeTable.setCellEditor  (rendEdit_CellBoutRad);
     
                    nbreColÀAjouterOuSupprimer--;   // Inutilisé
                }
                // Le groupage fonctionnel des radio-boutons est fait par 'GestionnObjetsDUneRangée'.
            }   // fin de 'if (nbreColÀAjouterOuSupprimer > 0)'
            else if (nbreColÀAjouterOuSupprimer < 0)      // nbreColÀAjout est négatif => Colonnes à ôter.
            {
                for (idxDernColoBoutRad = NBRE_FIXE_COL_AVANT ; 
                                      // = NBRE_FIXE_COL_AVANT + nbreColÀRB5
                     idxDernColoBoutRad < nbreColoTable - NBRE_FIXE_COL_APRES ; 
                     idxDernColoBoutRad--)
                {
                    nbreColoTable = (byte) table.getColumnCount();
                    idxDernColoBoutRad = (byte) (nbreColoTable - NBRE_FIXE_COL_APRES -1);
                    coloDeTable = table.getColumn(idxDernColoBoutRad);
                    monModTable.libellésColonnes.remove((int) (NBRE_FIXE_COL_AVANT + 1/* nbreRangées */ ));
                    table.removeColumn(coloDeTable);
     
                    nbreColÀAjouterOuSupprimer++;   // Inutile
                }
            }
            else if (nbreColÀAjouterOuSupprimer == 0)      // Colonnes ni à ajouter, ni à ôter.
            {
            }   // fin de 'if (nbreColÀAjouterOuSupprimer = 0)'
     
            panDéroul_Table.setViewportView(table);
            this.add(BorderLayout.CENTER, panDéroul_Table);
            this.repaint();         // Tells the layout manager to recalculate the layout.
        }
     
     
        /**
         * @param args the command line arguments
         * @throws ClassNotFoundException
         * @throws InstantiationException
         * @throws IllegalAccessException
         * @throws UnsupportedLookAndFeelException
         */
        public static void main(String[] args) throws ClassNotFoundException, InstantiationException, 
                                             IllegalAccessException, UnsupportedLookAndFeelException
        {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
            SwingUtilities.invokeLater(new Runnable()
            {
                @Override public void run()     // Implements method from java.lang.Runnable
                {
                    new XYExtensTable_SSCCE().initUI();
                }
            }                         );
        }
     
            //  -     -     -     -     -     -     -     -     -     -     -
     
       /** Pas "extends JRadioButton".
        * If we do not extend from 'DefaultTableCellRenderer', we will need to be 
        * careful to find ways to indicate whether the cell has focus or not, is 
        * editable or not, and is selected or not. If you don't, then those cells 
        * won't match the appearance of other table cells.
        * http://docs.oracle.com/javase/tutorial/uiswing/components/table.html#editor > 
        * 'Using Other Editors' : 
        * The AbstractCellEditor class implements TableCellEditor's superinterface CellEditor, 
        * which implements the event firing code necessary for cell editors.
        */
        class RendEdit_CellBoutRad extends AbstractCellEditor
                    implements TableCellRenderer, TableCellEditor, ActionListener
        {
            private final JRadioButton boutonRadio;
     
     
            public RendEdit_CellBoutRad()
            {
                this.boutonRadio = new JRadioButton();
                boutonRadio.addActionListener(this);    // Leaking 'this' in constructor.
                boutonRadio.setHorizontalAlignment(SwingConstants.CENTER);
                boutonRadio.setOpaque(false);
                // boutonRadio.setBackground(Color.yellow);
            }
     
     
           /* Implements the unique method from 'javax.swing.table.TableCellRenderer' interface.
            * https://docs.oracle.com/javase/7/docs/api/index.html?javax/swing/table/TableCellRenderer.html 
            * L'objet renvoyé par 'getValueAt()' dans 'MonModTable' est traité ici.     */
            @Override public Component getTableCellRendererComponent(JTable table, Object valeur, 
                                boolean estSélectionné, boolean aLeFocus, int rangée, int colonne)
            {
                boutonRadio.setSelected(Boolean.TRUE.equals(valeur));
                // boutonRadio.setOpaque(true);
                // boutonRadio.setBackground(java.awt.Color.lightblue);
                return boutonRadio;
            }
     
     
           /* This method is called when a cell value is edited by the user.
            * Implements the method required by 'javax.swing.table.TableCellEditor'.
            * Configure and return the component that you want to use as the editor.    */
            @Override public Component getTableCellEditorComponent(JTable table, Object valeur, 
                                                 boolean estSélectionné, int rangée, int colonne)
            {
                boutonRadio.setSelected(Boolean.TRUE.equals(valeur));
                // System.out.println("getTableCellEditorComponent !");
                // boutonRadio.setOpaque(true);
                return boutonRadio;
            }
     
     
           /* Implements method from 'java.awt.event.ActionListener'.
            * Once the editor is closed, the cell will get repainted by the table.
            */
            @Override public void actionPerformed(ActionEvent e)
            {   
               /* The call to 'fireEditingStopped' lets the table know that it can deactivate 
                * the editor, letting the cell be handled by the renderer again.        */
                fireEditingStopped();       //Make the renderer reappear.
                // https://tips4java.wordpress.com/2008/12/12/table-stop-editing/
                // stopCellEditing();
            }
     
     
            // Implements method required by 'javax.swing.CellEditor'. JTable or JTree
            @Override public Object getCellEditorValue()
            {   // 'isSelected()' is inherited from class javax.swing.AbstractButton .
                return boutonRadio.isSelected();        // boolean
            }
        }
    }
    - MonModTable (395 l.)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
    365
    366
    367
    368
    369
    370
    371
    372
    373
    374
    375
    376
    377
    378
    379
    380
    381
    382
    383
    384
    385
    386
    387
    388
    389
    390
    391
    392
    393
    394
    395
     
    package xyextenstable_sscce;
     
    // import java.lang.Math;          // sqrt()
    import java.math.RoundingMode;
    import java.math.BigDecimal;
    import java.util.List;          // Interface
    import java.util.ArrayList;
    import java.beans.PropertyChangeEvent;
    import java.beans.PropertyChangeListener;
    import javax.swing.table.AbstractTableModel;
     
    /**
     * Swing ne prévoit pas la possibilité d'implémenter un "Listener" (= "guêteur") 
     * cellule par cellule --> en implémenter un à 'class MonModTable'.
     * DefaultTableModel is not a suitable base class for my custom table model class
     * because ...
     * --> subclassing AbstractTableModel.
     * AbstractTableModel implements a simple framework for constructing TableModelEvent objects.
     * A "PropertyChange" event gets fired whenever a bean changes a "bound" property. You 
     * can register a PropertyChangeListener with a source bean so as to be notified of any 
     * bound property updates.
     */
        class MonModTable extends AbstractTableModel implements PropertyChangeListener
    {   
        // <editor-fold defaultstate="collapsed" desc="Déclaration des variables">
        private List<BigDecimal> échelleValeurs1eColonne;
     
        private final byte nbreColÀBR3;
        private final byte nbreColÀBR_AvantModif3;
        // Horizontalement
        // Not 'private' because capability to remove items from XYExtenTable_SSCCE.
             List<String> libellésColonnes;
        public List<String> libelAutresColQueColÀBR;
        // Formats des contenus des colonnes : String, RendeurEditeur_CelluleBoutonRadio, BigDecimal
        public final List<Object> renduCellCol;
     
       /* 'idxRang' dans 'Composer la base des rangées de données de la table' dans 
        * 'élaborTtsRangéesTable()' doit être accessible dans '@Override propertyChange(evt)'. */
        private byte idxRang;
        // Avant et après le groupe d'un nombre variable de colonnes à radio-boutons.
                static final byte NBRE_FIXE_COL_AVANT = 2;
                static final byte NBRE_FIXE_COL_APRES = 1;
           /* Pour 'getColumnClass()', 'isCellEditable()', 'getValueAt()', 'setValueAt()'
            * et 'getCellRenderer()'.             */
                     int idxDernColÀBoutRadio1 = NBRE_FIXE_COL_AVANT -1;
        // Contient et gère les rangées de boutons-radios et leurs regroupements.
        private List<GestionnObjetsDUneRangée> gestionnDonnéesTteLaTable;
        private GestionnObjetsDUneRangée gestionObjDUneRang;
        // </editor-fold>      // Fin de 'Déclaration des variables'
     
     
        MonModTable(byte nbreColÀBR2, byte nbreColÀBR_AvantModif2)
        {   
           /* Appelle le constructeur de 'AbstractTableModel'. This abstract class provides
            * default implementations for most of the methods in the TableModel interface.
            * https://docs.oracle.com/javase/7/docs/api/javax/swing/table/AbstractTableModel.html
            * Pas nécessaire car les méthodes requises sont implémentées dans cette classe-ci.
            */ 
            // super();
     
           /* Pour '@Override public Class<?> getColumnClass(int colo)', isCellEditable(), 
            * 'élaborTtsRangéesTable()', ...        */
            this.nbreColÀBR3 = nbreColÀBR2;
            this.nbreColÀBR_AvantModif3 = nbreColÀBR_AvantModif2;
     
           /* Instanciée ici pour éviter 'NullPointerException' dans 'getRowCount()', 
            * 'getValeurÀ()' et 'getDonnéesTteLaTable().       */
            gestionnDonnéesTteLaTable = new ArrayList<>();
     
            // Verticalement
            libellésColonnes = new ArrayList<>();
            libellésColonnes.add("A");
            libellésColonnes.add("B");
            // Entre ces colonnes-ci viendront s'insérer celles des boutons-radios.
            libellésColonnes.add("Z");          // ((Byte) nbreColÀBR3).toString());
     
            // Format des contenus des colonnes - Voir 'ajustageLargeurCol()'
           /*  renduCellCol = new ArrayList(new Collection{"#.0", "#.00", "#.0"});  */
            renduCellCol = new ArrayList();
            renduCellCol.add("#.0");    // A
            renduCellCol.add("#.00");   // B
            // Entre ces colonnes-ci viendront s'insérer celles des boutons-radios.
            renduCellCol.add("#.0");    // C
     
            élaborTtsRangéesTable();    // Assign a return value to the new variable
        }
     
     
       /** Appelée
         - lors de l'initialisation, dans 'élaborTtsRangéesTable()', dans la boucle 
         empilant des 'MonObjet's.
        */
        @Override public void propertyChange(PropertyChangeEvent evt)
        {
            Object objet2 = evt.getSource();
     
            if (objet2 == gestionObjDUneRang)
            {   
                switch (evt.getPropertyName())
                {   case "objetsDUneTranPuiss":
                        // 'supportChang_tPropri'
                        break;
                    case "objectBijgevoegd": // à la liste 'gestionnObjetsDUneRangée'
                        // Constructeur
                        break;
                    case "objectenVanEenRij":
                        // Ne passe jamais par ici.
                        break;
                    case "dataVanEenRij":
                        // Ne passe jamais par ici.
                        break;
                }
            } else
            if (objet2 instanceof MonObjet)
            {
                switch (evt.getPropertyName()) {
                    case "juistVeranderd":
                        // Constructeur et utilisation
                        break;
                    case "ObjMgrDezeRijIsBijgevoegd":   // à un nouvel objet.
                        // Constructeur
                        break;
                }
                ;
               /* À ce stade, la toute première rangée 
                * - n'a pas encore été ajoutée si première élaboration d'une nouvelle colonne.
                * - a été ajoutée si utilisation des boutons-radios mais pas ajout ou retrait de 
                * boutons-radios dans la table.
                */
            }
        }
     
     
       /** Méthode exigée par l'interface 'AbstractTableModel' --> Nom en Anglais
        * Implements method from javax.swing.table.TableModel
        * @return libellésColonnes.size()
        */
        @Override public int getColumnCount()
        {   
            return libellésColonnes.size();
            // return gestionObjDUneRang.getObjetsDUneRangée().size();
        }
     
     
       /** Méthode exigée par l'interface 'AbstractTableModel'
        * 'gestionnDonnéesTteLaTable' doit avoir été instanciée dans la méthode-constructeur.
        * @return 
        */
        @Override public int getRowCount()
        {   
            return gestionnDonnéesTteLaTable.size();
        }
     
     
        // Méthode existante mais non exigée par l'interface 'AbstractTableModel'
        @Override public String getColumnName(int col)
        {   
            return libellésColonnes.get(col);
        }
     
     
       /** If you did not specify a renderer for a particular column, then the table invokes 
        * the table model's getColumnClass method, which gets the data type of the column's cells.
        * Next, the table compares the column's data type with a list of data types for which 
        * cell renderers are registered. This list is initialized by the table, but you can add 
        * to it or change it.
        * Challenge : Le nombre de colonnes est variable.
        * @param colo
        * @return 
        */
        @Override public Class<?> getColumnClass(int idxColo1)
        {   
            if (idxColo1 < NBRE_FIXE_COL_AVANT)
            {
                return BigDecimal.class;
            } else
            if (idxColo1 >= NBRE_FIXE_COL_AVANT && idxColo1 <= idxDernColÀBoutRadio1)
            {
                return MonObjet.class;      // Contient une propriété JRadioButton
            } else
            if (idxColo1 > idxDernColÀBoutRadio1 &&
                idxColo1 < NBRE_FIXE_COL_AVANT + nbreColÀBR3 + NBRE_FIXE_COL_APRES)
            {
                return BigDecimal.class;
            } else            // Sans 'else' ni ';' : "Confusing identation"
            return null;      // Jamais
        }
     
     
        // Seules les colonnes à boutons-radios sont éditables.
        @Override public boolean isCellEditable(int rang, int idxColo2)
        {   
            if (idxColo2 < NBRE_FIXE_COL_AVANT)
                return false;
            else if (idxColo2 >= NBRE_FIXE_COL_AVANT && idxColo2 <= idxDernColÀBoutRadio1)
                return true;
            else if (idxColo2 > idxDernColÀBoutRadio1  &&
                     idxColo2 < NBRE_FIXE_COL_AVANT + nbreColÀBR3 + NBRE_FIXE_COL_APRES)
                return false;
     
            return false;       // Jamais
        }
     
     
       /** Méthode exigée par l'interface 'AbstractTableModel'.
        * Le contCellTabl renvoyé sera traité par la méthode 
        * 'TableCellRenderer.getTableCellRendererComponent (JTable table, 
        * Object value, boolean isSelected, boolean hasFocus, int row, int column)' 
        * dans 'XYExtensTable_SSCCE'. 
        * Renvoie un String ou un Boolean.      */
        @Override public Object getValueAt(int rang, int idxColo3)
        {   
            Object contCellTabl = getValeurÀ(rang, idxColo3);
     
            if (idxColo3 < NBRE_FIXE_COL_AVANT)
            {
                return contCellTabl;        // BigDecimal
            } else
            if (idxColo3 >= NBRE_FIXE_COL_AVANT && idxColo3 <= idxDernColÀBoutRadio1)
            {   // Bouton radio : Boolean       boolean --> Boolean
                if ((Class<?>)contCellTabl.getClass() == MonObjet.class)
                {
                    Boolean étaitSélect_é = (Boolean)((MonObjet) contCellTabl).isSélectionné();
                    return étaitSélect_é;
                } 
            } else
            if (idxColo3 > idxDernColÀBoutRadio1  &&
                     idxColo3 < NBRE_FIXE_COL_AVANT + nbreColÀBR3 + NBRE_FIXE_COL_APRES)
            {
                return contCellTabl;        // BigDecimal
            }
            ;           // Sans 'else' ni ';' : "Confusing identation"
            return null;
        }
     
     
       /** Need to implement this method because some table's data can be manually changed.
        * A pour but de n'activer ou désactiver le bouton-radio que d'une seule cellule de 
        * la table. Si c'est l'activer, les conséquences pour les autres cellules de la rangée
        * sont gérées par la méthode 'miseàjourTousBP_Rangée()' dans 'gestionnObjetsDUneRangée', 
        * elle-même appelée ici par 'setSélectionné()' dans 'monObjet'.
        * Overrides method from javax.swing.table.AbstractTableModel
        */
        @Override public void setValueAt(Object brNouvStatutAction_t, int idxRang2, int idxColo4)
        {
            if (idxColo4 >= NBRE_FIXE_COL_AVANT && idxColo4 <= idxDernColÀBoutRadio1)
            {
                MonObjet contCellTabl = (MonObjet) getValeurÀ(idxRang2, idxColo4);
                contCellTabl.setSélectionné(Boolean.TRUE.equals(brNouvStatutAction_t), 
                                            (byte) idxDernColÀBoutRadio1);
            }
           /* It works - with a functionnal fault - without 'fireTableCellUpdated(..;)' 
            * if you change the data by using the table editor, because once the editor 
            * is closed, the cell will get repainted by the table.
            * However, if you manually invoke 'setValueAt()', then it won't work. 
            * Also if you don't invoke that method, the 'no event' will be fired for 
            * a TableModelListener that might be added to the TableModel.
            */
            fireTableRowsUpdated(idxRang2, idxRang2);
        }
     
     
        // Méthode appelée par 'getValueAt() et 'setValueAt()'.
        Object getValeurÀ(int rang, int idxCol5)
        {   // Il y a [NBRE_RANG] gestionnObjetsDUneRangée dans 'gestionnDonnéesTteLaTable'.
            return gestionnDonnéesTteLaTable.get(rang).getObjetsDUneRangée().get(idxCol5);
        }
     
     
        List<GestionnObjetsDUneRangée> getDonnéesTteLaTable()   // ArrayList<Object>
        {   
            return gestionnDonnéesTteLaTable;
        }
     
        //  -     -     -     -     -     -     -     -     -     -     -
     
       /** Méthode appelée dans 'MonModTable()'.
        * 
        */ 
        private byte élaborTtsRangéesTable()
        {   
           /* Pour 'getColumnClass()', 'isCellEditable()', 'getValueAt()', 'setValueAt()'
            * et 'getCellRenderer()'.             */
            idxDernColÀBoutRadio1 = NBRE_FIXE_COL_AVANT -1 + this.nbreColÀBR3;
            MonObjet objet;
     
            // <editor-fold defaultstate="collapsed" desc="Insérer les libellés des colonnes aux boutons-radio">
            if (this.nbreColÀBR3 == this.nbreColÀBR_AvantModif3)
            {   // Colonne ni à ajouter, ni à ôter.
            } else 
            if (this.nbreColÀBR3 > this.nbreColÀBR_AvantModif3)
            {   
                for (byte nièmeCol = 0 ; // (byte) nbreColÀBR_AvantModif3 ; 
                                            nièmeCol < nbreColÀBR3 ; nièmeCol++)
                {   
                    libellésColonnes.add(NBRE_FIXE_COL_AVANT + nièmeCol, 
                                         ((Byte) nièmeCol).toString());
               }
            } else      // Fin de 'if (nbreColÀBR3 > nbreColÀBR_AvantModif3)'
            // Suppression d'une colonne
            if (this.nbreColÀBR3 < this.nbreColÀBR_AvantModif3)
            {  /* Le nécessaire est fait par 'ajoutRetraitCol(int pos, byte nbreColÀBR3, 
                * List columnData)' dans 'XYExtensTable_SSCCE'               */
            };
            // Fin de "Insérer les libellés des colonnes aux boutons-radio".
            // </editor-fold>
     
            // <editor-fold defaultstate="collapsed" desc="Composer une série de valeurs factices pour les premières colonnes">
           /* Avant d'élaborer les ArrayList 'gestionObjDUneRangée', élaborer 
            * l'ArrayList 'échelleValeurs1eColonne'.
            * Ici et pas dans le constructeur car le nombre de rangées (et de colonnes à BR) 
            * est variable.
            * Erreur car le constructeur est activé à chaque changement du nombre de BR.
            */
            BigDecimal uneValeur = new BigDecimal(10);
            final BigDecimal multiplicat = new BigDecimal(Math.sqrt(2.0));  // 1.414213
     
            // Dans cette application, le nbreRangées = nbreColÀBR3 .
            échelleValeurs1eColonne = new ArrayList<>(nbreColÀBR3);
     
            for (byte num = 0 ; num < (nbreColÀBR3 +1) ; num++)
            {   
                uneValeur = uneValeur.multiply(multiplicat).setScale(3, RoundingMode.CEILING);
                échelleValeurs1eColonne.add(uneValeur);
            }
     
            byte nbreRangées = (byte) (échelleValeurs1eColonne.size() -1);
            // </editor-fold>
     
            // <editor-fold defaultstate="collapsed" desc="Composer la base des rangées de données de la table">
           /* c à d la liste 'gestionnDonnéesTteLaTable' de toutes les listes 'gestionObjDUneRang' 
            * de <nbreCellulesChaqueLigne> cellules, en fonction du nombre <nbreColÀBR>.
            * 'Object' est 'MonObjet' lorsqu'il s'agit des colonnes ajoutées dynamiquement.     */
            gestionnDonnéesTteLaTable = new ArrayList<>(nbreRangées);
     
            // 'idxRang' est aussi utilisée par '@Override ... propertyChange(PropertyChangeEvent evt)
            for (idxRang = 0 ; idxRang < nbreRangées ; idxRang++)
            {   
                gestionObjDUneRang = new GestionnObjetsDUneRangée(nbreColÀBR3);
     
               /* Support pour propriété qui change.
                * Seule 'monModTable' est enregistrée comme "listener" (= guêteur) 
                * de toutes les propriétés de 
                * - chaque instanciation de 'gestionObjDUneRang' (dont 'objetsDUneTranPuiss') 
                * cela ici même.
                * - chaque instanciation de 'MonObjet', cela dans la 'propertyChange()'
                */
               /* Pour que le support de changement de propriété 'suppChanProp_GestRang' 
                * propre à ce 'gestionObjDUneRang' enregistre 'MonModTable' comme guêteur 
                * de changement de chacune des propriétés de ce 'gestionObjDUneRang'.
                * this = PropertyChangeListener guêteur.
                */
                // gestionObjDUneRangée.getSuppoChangePropri().addPropertyChangeListener(this);
                gestionObjDUneRang.ajoutGuêteurChangtPropri_GestRang(this);
     
                // Première colonne
                // Valeur de la deuxième cellules de la rangée précédente.
                gestionObjDUneRang.getObjetsDUneRangée().add(échelleValeurs1eColonne.get(idxRang));
                // Deuxième colonne
                gestionObjDUneRang.getObjetsDUneRangée().add(échelleValeurs1eColonne.get(idxRang+1));
                // Entre deuxième et dernière colonne
                for (byte idxDernColÀBoutRadio3 = NBRE_FIXE_COL_AVANT ;
                          idxDernColÀBoutRadio3 < NBRE_FIXE_COL_AVANT + nbreColÀBR3 ;
                          idxDernColÀBoutRadio3++)
                {   
                    objet = new MonObjet(idxRang, idxDernColÀBoutRadio3);
     
                   /* Pour que le support de changement de propriété 'suppChanProp_Obj' 
                    * propre à ce nouvel 'objet' enregistre 'monModTable' comme guêteur 
                    * de changement de chacune des propriétés de ce nouvel 'objet'.   */
                    // Pourrait être déplacé dans le constructeur de 'monObjet'.
                    objet.ajoutGuêteurChangtPropri_Obj(this);
     
                   /* Grâce à 'setGestionnObjetsDUneRangée(...)' dans 'ajoutObjetÀRangée(...)' 
                    * dans 'GestionnObjetsDUneRangée', la propriété 'gestionObjDUneRang2' dans 
                    * 'contCellTabl' réfèrera au 'gestionObjDUneRang' d'ici ('monModTable')   */
                    gestionObjDUneRang.ajoutObjetÀRangée(objet);
     
                    if (idxDernColÀBoutRadio3 == NBRE_FIXE_COL_AVANT)
                        objet.setSélectionné(true, idxDernColÀBoutRadio3);
                }
                // Dernière colonne
                gestionObjDUneRang.getObjetsDUneRangée().add(BigDecimal.ZERO);
     
                gestionnDonnéesTteLaTable.add(gestionObjDUneRang);
            }   // Fin de "Composer la base des rangées de données de la table"
            // </editor-fold>
     
            // int nbreTotColo = getColumnCount();
            // The fireXxx() methods simply instruct the view to query the model via getValueAt(). 
            // fireTableStructureChanged();     // Plutôt dans 'miseÀJourModèleEtStructTable()'
            return nbreRangées;
        }
    }
    - GestionnObjetsDUneRangée (123 l.)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
     
    package xyextenstable_sscce;
     
    import java.beans.PropertyChangeSupport;    // PropertyChangeEvent
    import java.beans.PropertyChangeListener;
    import java.util.ArrayList;
    import java.util.List;
    import static xyextenstable_sscce.MonModTable.NBRE_FIXE_COL_AVANT;
    import static xyextenstable_sscce.MonModTable.NBRE_FIXE_COL_APRES;
     
     
    /**  Bean that support bound properties. (All Swing components are also beans)
     Manages a list of listeners and dispatches PropertyChangeEvents to them. 
     Ce n'est pas que des boutons-radio (RenduEditeur_CellBoutRad) ont plusieurs 
     guêteurs, mais que tous les boutons-radios ont un même guêteur. 
     http://docs.oracle.com/javase/7/docs/api/java/beans/PropertyChangeSupport.html
     A bound property notifies listeners when its value changes. This has two implications:
     1. This bean class includes 'addPropertyChangeListener()' [and 'removePropertyChangeListener()'] 
       methods for managing the bean's listener(s) (here only 'MonModTable').
     2. When a bound property is changed (dans 'GestionnObjetsDUneRangée gestionObjDUneRang' 
     et dans chaque 'MonObjet objet' créé dans 'MonModTable'), 
     ainsi qu'à l'exécution de 'propertyChangeSupport.firePropertyChange()', 
     the bean sends a PropertyChangeEvent to its registered listeners.
     Getter method but no setter : read-only property.
     La liste ... mais plutôt 
     la partie centrale de l''List<ArrayList<Object>> objetsTteLaTable' dans
     'MonModTable', c à d les colonnes après 'NBRE_FIXE_COL_AVANT' 
     et avant 'NBRE_FIXE_COL_APRES'. Les deux dimensions de 'donnéesTteLaTable' sont 
     variables.
     */
       class GestionnObjetsDUneRangée
    {  
       /* Chaque rangée (objetsDUneRangée) contient des 'BigDecimal' et  
        * un nombre variable de 'MonObjet' (contenant un 'JRadioButton'). */
        private List<Object> objetsDUneRangée;
        private final PropertyChangeSupport suppChanProp_GestRang;
        /* Propriété en fait oommune à 'objetsDUneRangée', c à d qui ne devrait être 
        * instanciée que dans l'entité englobant tous les 'gestionObjDUneRangée'.
        * Existe aussi dans 'monModTable'.           */
        private final byte nbreColÀBR1;
     
     
        GestionnObjetsDUneRangée(byte nbreColÀBR2)
        {   // 'nbreColÀBR1' rien que pour 'miseàjourTousBR_Rangée()'.
            this.nbreColÀBR1 = nbreColÀBR2;
            objetsDUneRangée = new ArrayList<>(
                    NBRE_FIXE_COL_AVANT + nbreColÀBR1 + NBRE_FIXE_COL_APRES);
            suppChanProp_GestRang = new PropertyChangeSupport(this);
        }
     
     
        public List<Object> getObjetsDUneRangée()
        {
            return objetsDUneRangée;
        }
     
     
        // Appelée par ?
        void setObjetsDUneRangée(List<Object> objetsDUneRangée2)
        {
            List<Object> ancienObjetsDUneRangée = objetsDUneRangée;
            this.objetsDUneRangée = objetsDUneRangée2;
            suppChanProp_GestRang.firePropertyChange("objectenVanEenRij",
                                     ancienObjetsDUneRangée, objetsDUneRangée);
        }
     
     
       /* Méthode appelée 
        * - dans 'MonModTable' > 'élaborTtsRangéesTable()' 
        * > 'Composer la base des rangées de données de la table'
        * Chaque rangée (objetsDUneRangée) contient des 'BigDecimal' et 
        * un nombre variable de 'MonObjet' (contenant un 'RadioButton').  */
        void ajoutObjetÀRangée(Object objet)
        {   
            objetsDUneRangée.add(objet);
     
            if (objet != null)
            {   // Met dans chaque objet la référence du gestionnaire d'objets qui les contient.
                ((MonObjet) objet).setGestionnObjetsDUneRangée(this);
                suppChanProp_GestRang.firePropertyChange("objectBijgevoegd", null, (MonObjet) objet);
            }
        }
     
     
       /** Appelée par 'setSélectionné()' dans 'monObjet' et appelle 'setSélectionné()'
        * c à d fonction réentrante.
        * Met tous les boutons-radios d'une rangée dans leurs états adéquats.
        * 'MonObjet' ne dispose pas de 'nbreColÀBR'; 'GestionObjDUneRang' bien.
        * @param objetActionné
        * @param idxDernColÀBoutRadio4
        */
        void miseàjourTousBR_Rangée(MonObjet objetActionné, byte idxDernColÀBoutRadio4)
        {   
            MonObjet objEnMire;
     
            for (byte idxColÀBoutRadio = NBRE_FIXE_COL_AVANT ;
                      idxColÀBoutRadio <= idxDernColÀBoutRadio4 ;
                      idxColÀBoutRadio++)
            {   
                objEnMire = ((MonObjet) objetsDUneRangée.get(idxColÀBoutRadio));
                objEnMire.setSélectionné(objetActionné == objEnMire , idxColÀBoutRadio);
            }
        }
     
     
        PropertyChangeSupport getSuppoChangePropri_GestRang()
        {
            return suppChanProp_GestRang;
        }
     
     
        // guêteur = objet MonModèleTable implements PropertyChangeListener
        void ajoutGuêteurChangtPropri_GestRang(PropertyChangeListener guêteur)
        {
            suppChanProp_GestRang.addPropertyChangeListener(guêteur);
        }
     
     
        void retraitGuêteurChangePropri_GestRang(PropertyChangeListener guêteur)
        {
            suppChanProp_GestRang.removePropertyChangeListener(guêteur);
        }
    }
    - MonObjet (116 l.)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
     
    package xyextenstable_sscce;
     
    import java.beans.PropertyChangeListener;
    import java.beans.PropertyChangeSupport;
     
     
    /** Bean supporting bound properties.
     * La seule classe guêteuse (Listener) de 'MonObjet' est 'MonModTable'.
     */
        class MonObjet
    {  /* Delegating to 'suppChanProp_Obj' the management of a list of listeners 
        * and the dispatching of 'PropertyChangeEvents' ?        */ 
        private boolean brStatutAction_t;
        private GestionnObjetsDUneRangée gestionObjDUneRang;
        private final PropertyChangeSupport suppChanProp_Obj;
     
     
        public MonObjet(short idxRang, short idxColo)
        {   
            suppChanProp_Obj = new PropertyChangeSupport(this);
        }
     
     
        // The accessor method to be defined uses 'is...' instead of 'get...' because boolean.
        boolean isSélectionné()
        {
            return brStatutAction_t;
        }
     
     
       /** Appelée par
        * - le constructeur de 'MonModTable()'.
        * - 'setValueAt()' dans 'MonModTable()'.
        * - 'miseàjourTousBR_Rangée()' dans 'GestionnObjetsDUneRangée').
        * 
        * @override setValueAt() > 
        * getValeurÀ(idxRang2, idxColo2)).setSélectionné() > 
        * gestionObjDUneRang2.miseàjourTousBR_Rangée() > 
        * obj.setSélectionné()
        * miseàjourTousBR_Rangée()
        * 
        * @param brEnMireEstLeBrQuiVientDEtreActionné
        * @param idxDernColÀBoutRadio2
        */
        void setSélectionné(boolean brEnMireEstLeBrQuiVientDEtreActionné, byte idxDernColÀBoutRadio2)
        {   
            if (this.brStatutAction_t != brEnMireEstLeBrQuiVientDEtreActionné)
            {  /* Ne passe par ici,  
                * - si le bout.radio en mire est le bouton-radio qui vient d'être 
                *   actionné (c à d si brEnMireEstLeBrQuiVientDEtreActionné = true), 
                *   que si brStatutAction_t = false.
                *   C'est le bouton-radio qui vient d'être actionné; Son statut 
                *   d'actionnement doit passer à 'true'.
                * - si le bout.radio en mire n'est pas le bouton-radio qui vient d'être 
                *   actionné (c à d si brEnMireEstLeBrQuiVientDEtreActionné = false), 
                *   que si brStatutAction_t = true.
                *   C'est le bouton-radio qu'on quitte; son statut d'actionnement doit 
                *   passer à 'false'.
                */
                this.brStatutAction_t = brEnMireEstLeBrQuiVientDEtreActionné;
                if (brStatutAction_t == true)
                {  /* Ne passe par ici que si le nouveau statut du bouton-radio est 'true'.
                    * C'est le bouton-radio qui vient d'être actionné et qu'il s'agit de mettre à 'true'.
                    */
                   /* 'gestionObjDUneRang' est devenu une référence concrète (non nulle) dans 
                    * le constructeur de 'MonModTable' par l'appel de 'gestionObjetsDUneRangée.
                    * ajoutObjetÀRangée()' qui contient l'appel de 'objet.setGestionnObjetsDUneRangée()'.
                    */
                    gestionObjDUneRang.miseàjourTousBR_Rangée(this, idxDernColÀBoutRadio2);
                }
                suppChanProp_Obj.firePropertyChange("juistVeranderd", !brStatutAction_t, brStatutAction_t);
            }
        }
     
     
        // Méthode appelée par 'ajoutObjetÀRangée(Object objet)' dans 'GestionnObjetsDUneRangée'.
        void setGestionnObjetsDUneRangée(GestionnObjetsDUneRangée gestionnObjetsDUneRangée2)
        {   
            GestionnObjetsDUneRangée ancienGestionnObjetsDUneRangée = gestionObjDUneRang;
     
           /* Une référence au conteneur de l'objet (gestionObjDUneRang) est placée 
            * dans chaque objet afin ...
            */
            this.gestionObjDUneRang = gestionnObjetsDUneRangée2;
     
            suppChanProp_Obj.firePropertyChange("ObjMgrDezeRijIsBijgevoegd",
                                    ancienGestionnObjetsDUneRangée, gestionnObjetsDUneRangée2);
        }
     
     
        // Méthode appelée par 'ajoutObjetÀRangée(MonObjet objet)' dans 'GestionnObjetsDUneRangée'.
        GestionnObjetsDUneRangée getGestionnObjetsDUneRangée()
        {
            return gestionObjDUneRang;
        }
     
     
        PropertyChangeSupport getSupportChangtPropri_Obj()
        {
            return suppChanProp_Obj;
        }
     
     
        // Ici, le 'guêteur' (= 'listener') est 'monModTable'
        void ajoutGuêteurChangtPropri_Obj(PropertyChangeListener guêteur)
        {
            suppChanProp_Obj.addPropertyChangeListener(guêteur);
        }
     
     
        void retraitGuêteurChangePropri_Obj(PropertyChangeListener guêteur)
        {
            suppChanProp_Obj.removePropertyChangeListener(guêteur);
        }
    }
    Cut, paste and run. Cela devrait fonctionner.
    Facile, les JTables incluant des composants avancés ?
    En espérant aider quelques-uns ...
    Je suis intéressé par vos réactions, conseils d'amélioration, ... Je clôturerai cette discussion vers le 20 juin.
    Salut.
    "Ah oui ! Juste encore cette toute dernière petite question ..." (Columbo - Peter Falk)

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 0
    Dernier message: 16/01/2017, 08h38
  2. [XL-2007] Macro copier/coller colonnes de dimensions variables
    Par wamkey dans le forum Macros et VBA Excel
    Réponses: 3
    Dernier message: 19/01/2014, 04h06
  3. [JTable] Dimension de colonne
    Par MeDReD dans le forum Composants
    Réponses: 1
    Dernier message: 15/07/2008, 12h05
  4. [JTable] dimension
    Par Ikki dans le forum Agents de placement/Fenêtres
    Réponses: 4
    Dernier message: 08/12/2004, 12h45
  5. [JTable] Dimension des colonnes
    Par Katyucha dans le forum Composants
    Réponses: 4
    Dernier message: 04/11/2004, 16h53

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo