Bonjour,

Je cherchais à comprendre comment personnaliser les JTables, et du coup, j'ai commencé à faire un petit projet simpliste pour comprendre comment ça marchait. Seulement, tant que je fais des choses relativement simples, tout marche plutôt bien, mais dès que je commence à personnaliser des trucs, je commence à avoir pas mal de comportements un peu bizarre. J'aurais besoin de votre aide pour m'aider à y voir plus clair. Merci.

Voici le mini-projet, que vous pouvez tester :

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
 
public class Tableau extends JFrame {
 
    public Tableau() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(800,600);
        setContentPane(new Panel());
        setVisible(true);
    }
 
    public static void main(String[] args) {
        new Tableau();
    }
    private static class Panel extends JPanel {
        private JTable table = new Table(4, 4);
        public Panel() {
            add(table);
        }
    }
 
    private static class Table extends JTable {
        private Renderer randerer = new Renderer(this);
        public Table(int row, int col) {
            super(new Model(row, col));
            putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
            setAutoResizeMode(AUTO_RESIZE_OFF);
            setDefaultRenderer(String.class, randerer);
            setDefaultEditor(String.class, randerer);
        }
 
        private class Renderer extends JTextPane implements TableCellRenderer, TableCellEditor {
            String previousContent = "";
            JTable table;
            public Renderer(JTable table) {
                super();setOpaque(true);this.table = table;
            }
            @Override
            public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
                setText((String)value);
                setBorder(hasFocus ? BorderFactory.createLineBorder(Color.RED) : null);
                setBackground(isSelected ? Color.PINK : Color.GREEN);
                FontMetrics fm = getFontMetrics(getFont());
                if(((String)value).length()!=0) {setPreferredSize(new Dimension(fm.stringWidth((String)value),fm.getHeight()));}
                else {setPreferredSize(new Dimension(20, fm.getHeight()));}
                return this;
            }
 
            @Override
            public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
                setText((String)value);
                setBackground(isSelected ? table.getSelectionBackground() : Color.MAGENTA);
                if(isSelected) {selectAll();}
                previousContent = (String)value;
                return this;
            }
 
            @Override
            public Object getCellEditorValue() {
                return getText();
            }
 
            @Override
            public boolean isCellEditable(EventObject anEvent) {
                return true;
            }
 
            @Override
            public boolean shouldSelectCell(EventObject anEvent) {
                return true;
            }
 
            @Override
            public boolean stopCellEditing() {
                fireEditingStoppedEvent();
                return true;
            }
 
            @Override
            public void cancelCellEditing() {
                setText(previousContent);
                fireEditingCanceledEvent();
            }
 
            protected void fireEditingStoppedEvent() {
                for(CellEditorListener l : listeners) {l.editingStopped(new ChangeEvent(this));}
            }
 
            protected void fireEditingCanceledEvent() {
                for(CellEditorListener l : listeners) {l.editingCanceled(new ChangeEvent(this));}
            }
 
            private List<CellEditorListener> listeners = new LinkedList<CellEditorListener>();
            @Override
            public void addCellEditorListener(CellEditorListener l) {
                listeners.add(l);
            }
 
            @Override
            public void removeCellEditorListener(CellEditorListener l) {
                listeners.remove(l);
            }
 
        }
    }
 
    private static class Model implements TableModel {
//        private ColumnModel colonnes = new ColumnModel();
        private List<List<Cellule>> colonnes = new LinkedList<List<Cellule>>();
        private int rowCount = 0;
//        private List<Cellule> cellules = new LinkedList<Cellule>();
 
        private static class Cellule {
            public Cellule() {}
            public Cellule(Object content) {this.content = content;}
            private Object content;
            private Object getContent() {return content;}
            private void setContent(Object newContent) {content = newContent;}
        }
 
        public Model(int row, int col) {
            for(int j=0; j<col; j++) {
                insertColumn(j);
            }
            for(int i=0; i<row; i++) {
                insertRow(i);
            }
        }
 
        @Override
        public int getRowCount() {
            return colonnes.isEmpty() ? rowCount : colonnes.get(0).size();
        }
 
        @Override
        public int getColumnCount() {
            return colonnes.size();
        }
 
        @Override
        public String getColumnName(int columnIndex) {
            String result = "";
            for (; columnIndex >= 0; columnIndex = columnIndex / 26 - 1) {
                result = (char)((char)(columnIndex%26)+'A') + result;
            }
            return result;
        }
 
        @Override
        public Class<?> getColumnClass(int columnIndex) {
            return colonnes.isEmpty() ? Object.class : getValueAt(columnIndex, 0).getClass();
        }
 
        @Override
        public boolean isCellEditable(int rowIndex, int columnIndex) {
            return true;
        }
 
        @Override
        public Object getValueAt(int rowIndex, int columnIndex) {
            return colonnes.get(columnIndex).get(rowIndex).getContent();
        }
 
        @Override
        public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
            colonnes.get(columnIndex).get(rowIndex).setContent(aValue);
            fireTableChanged(new TableModelEvent(this, rowIndex, rowIndex, columnIndex, TableModelEvent.UPDATE));
        }
 
        protected void fireTableChanged(TableModelEvent e) {
            for(TableModelListener l : listeners) {
                l.tableChanged(null);
            }
        }
 
        public void insertColumn(int index) {
            List<Cellule> newColumn = new LinkedList<Cellule>();
            for(int i = 0; i<getRowCount(); i++) {
                Cellule newCell = new Cellule("");
                newColumn.add(newCell);
            }
            colonnes.add(index, newColumn);
            fireTableChanged(new TableModelEvent(this, 0, getRowCount(), index, TableModelEvent.INSERT));
        }
 
        public void insertRow(int index) {
            for(List<Cellule> colonne : colonnes) {
                Cellule newCell = new Cellule("");
                colonne.add(index, newCell);
            }
            rowCount++;
            fireTableChanged(new TableModelEvent(this, index, index, TableModelEvent.ALL_COLUMNS, TableModelEvent.INSERT));
        }
 
        public void removeColumn(int index) {
            colonnes.remove(index);
            fireTableChanged(new TableModelEvent(this, 0, getRowCount(), index, TableModelEvent.DELETE));
        }
 
        public void removeRow(int index) {
            for(List<Cellule> row : colonnes) {
                row.remove(index);
            }
            rowCount--;
            fireTableChanged(new TableModelEvent(this, index, index, TableModelEvent.ALL_COLUMNS, TableModelEvent.DELETE));
        }
 
        private List<TableModelListener> listeners = new LinkedList<TableModelListener>();
        @Override
        public void addTableModelListener(TableModelListener l) {
            listeners.add(l);
        }
 
        @Override
        public void removeTableModelListener(TableModelListener l) {
            listeners.remove(l);
        }
 
    }
 
}
En gros, on a un JFrame (la classe Tableau), qui contient un JPanel (la classe Panel), qui contient une JTable (la classe Table). Cette table utilise un TableModel (la classe Model) et un JTextPane (classe Renderer) qui va se charger d'afficher et d'éditer le contenu du tableau.

Voici la liste de mes questions :

1) Pourquoi, quelque soit la cellule sélectionnée, toute la ligne se met en surbrillance (j'ai choisi des couleurs bien flashy pour mettre en avant les problèmes ^^) ?

2) Pourquoi les cellules ne se redimensionnent pas selon la taille du texte, comme je l'espérais en spécifiant le setPreferredSize du Renderer ?

3) Si je clique sur une case, elle se met en blanc (ce qui me parait pourtant impossible d'après le code du renderer). Si je tape quelque chose sur mon clavier, rien n'apparait à priori. Mais quand je déplace le focus, le texte apparait à ce moment là...

4) Si je clique puis que j'utilise les flèches, là la sélection semble se faire correctement. J'ai bien mon cadre rouge, etc. Comment ça se fait que je doive cliquer puis me déplacer pour pouvoir sélectionner correctement une case ?

Un grand merci à ceux qui prendront le temps de m'aider !