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

AWT/Swing Java Discussion :

Entourer une cellule en rouge après l'exécution d'une action


Sujet :

AWT/Swing Java

  1. #1
    Membre du Club
    Profil pro
    Inscrit en
    Janvier 2006
    Messages
    200
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2006
    Messages : 200
    Points : 66
    Points
    66
    Par défaut Entourer une cellule en rouge après l'exécution d'une action
    Bonjour,

    j'aimerais simplement entourer la bordure d'une cellule d'une JTable en rouge après l'action d'un bouton.

    Pour la création de la Jtable, j'ai le bout de code suivant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
       this.table_phases = new JTable();
       this.table_phases.setDefaultRenderer(Object.class, new CheckDataCellRenderer());
            this.table_phases.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
            this.table_phases.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
                public void valueChanged(ListSelectionEvent e) {
                    if (e.getValueIsAdjusting()) {
                         // d'autres action...
                    }
                }
            }
       }
    Et pour la gestion de l'affichage des cellules de la Jtable, j'ai le bout de code suivant :

    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
    public class CheckDataCellRenderer extends DefaultTableCellRenderer {
     
            @Override
            public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
     
                Component cell = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
     
                if (cell instanceof JLabel) {
                    JLabel label = (JLabel) cell;
     
                    if (label.getText().isEmpty()) {
                        label.setBorder(BorderFactory.createLineBorder(Color.red));
                    } else {
                        label.setBorder(Constants.DEFAULT_BORDER);
                    }
                }
     
                return cell;
            }
        }
    Pour le moment, lorsqu'une cellule est vide elle est entourée en rouge.
    J'aimerais juste appliquer cette action lors d'un click sur un bouton par exemple.

    J'avais pensé à initialiser lors de mon click une map pour les cellules à contrôler et à inclure cette map dans le renderer CheckDataCellRenderer.

    Pensez-vous que cette solution est propre ou avez-vous une solution plus propre ?

  2. #2
    Modérateur
    Avatar de joel.drigo
    Homme Profil pro
    Ingénieur R&D - Développeur Java
    Inscrit en
    Septembre 2009
    Messages
    12 430
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur R&D - Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2009
    Messages : 12 430
    Points : 29 131
    Points
    29 131
    Billets dans le blog
    2
    Par défaut
    Salut,

    Pour moi une Map est une bonne solution, si les valeurs sont uniques. Si tu utilises un modèle métier (au lieu d'un tableau/vecteur de propriété), tu peux wrapper ton objet dans un objet qui dispose d'une propriété booléenne et l'utiliser pour déterminer si la cellule doit être bordée ou pas, et ainsi ne pas avoir de problématique de gestion de doublon. Tu peux aussi faire un modèle qui wrappe un tableau qui a une colonne de plus avec l'information, mais ça oblige le renderer à avoir une référence sur ce tableau, ce qui est moins élégant.
    Tu peux déléguer la gestion de la bordure à un renderer spécifique (décorateur) ce qui te permet plus facilement le branchement/débranchement et évite de mettre en dur la gestion de la bordure dans le renderer de colonne (si tu en as plusieurs, ça évite aussi la duplication de code).
    L'expression "ça marche pas" ne veut rien dire. Indiquez l'erreur, et/ou les comportements attendus et obtenus, et donnez un Exemple Complet Minimal qui permet de reproduire le problème.
    La plupart des réponses à vos questions sont déjà dans les FAQs ou les Tutoriels, ou peut-être dans une autre discussion : utilisez la recherche interne.
    Des questions sur Java : consultez le Forum Java. Des questions sur l'EDI Eclipse ou la plateforme Eclipse RCP : consultez le Forum Eclipse.
    Une question correctement posée et rédigée et vous aurez plus de chances de réponses adaptées et rapides.
    N'oubliez pas de mettre vos extraits de code entre balises CODE (Voir Mode d'emploi de l'éditeur de messages).
    Nouveau sur le forum ? Consultez Les Règles du Club.

  3. #3
    Membre du Club
    Profil pro
    Inscrit en
    Janvier 2006
    Messages
    200
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2006
    Messages : 200
    Points : 66
    Points
    66
    Par défaut
    merci de ta réponse joel, et désolé pour le petit délai de réponse je reviens de weekend

    L'idée d'un modèle métier avec une colonne de plus pour gérer si la cellule doit être bordée ou pas me semble être la meilleure solution, mais dans mon cas il y aurait trop de travail pour mettre en place ce modèle puisque je gère ma Jtable par des colonnes classiques.

    Ce qui ramène à ta deuxième proposition avec le renderer qui contient une référence sur ce tableau. Ca semble un peu moins élégant mais ça convient quand même.
    Par rapport à cette deuxième proposition, il faudrait que je surcharge le modèle de la jtable pour introduire cette nouvelle colonne.
    Mais, est-ce que ça ne reviendrait pas au même de vérifier directement dans le renderer en ajoutant ces 3 conditions : "si ma cellule courante est vide OU que l'une des autres de ma ligne du tableau est vide ALORS je borde ma cellule courante" ?

    Concernant ta dernière remarque joel, pourrais tu me donner une piste pour la gestion de la bordure via un renderer spécifique ? je ne vois pas comment mettre cela en place.

  4. #4
    Modérateur
    Avatar de joel.drigo
    Homme Profil pro
    Ingénieur R&D - Développeur Java
    Inscrit en
    Septembre 2009
    Messages
    12 430
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur R&D - Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2009
    Messages : 12 430
    Points : 29 131
    Points
    29 131
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par bernidupont Voir le message
    Ce qui ramène à ta deuxième proposition avec le renderer qui contient une référence sur ce tableau. Ca semble un peu moins élégant mais ça convient quand même.
    Par rapport à cette deuxième proposition, il faudrait que je surcharge le modèle de la jtable pour introduire cette nouvelle colonne.
    oui

    Citation Envoyé par bernidupont Voir le message
    Mais, est-ce que ça ne reviendrait pas au même de vérifier directement dans le renderer en ajoutant ces 3 conditions : "si ma cellule courante est vide OU que l'une des autres de ma ligne du tableau est vide ALORS je borde ma cellule courante" ?
    Oui, si telle est ta formule (je parlais dans un cas général : stocker l'état évite de recalculer à chaque fois qu'il est nécessaire de faire le rendu, et cela peut être intéressant selon que le calcul soit long ou pas)
    Citation Envoyé par bernidupont Voir le message
    Concernant ta dernière remarque joel, pourrais tu me donner une piste pour la gestion de la bordure via un renderer spécifique ? je ne vois pas comment mettre cela en place.
    Exemple :
    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
    public class JTableRendererDemo {
     
    	public static void main(String[] args) {
     
     
    		JFrame frame = new JFrame("Démo");
    		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
     
     
    		frame.add(new JScrollPane(createTable()));
     
    		frame.setSize(600, 400);
    		frame.setLocationRelativeTo(null);
    		frame.setVisible(true);
    	}
     
    	private static JTable createTable() {
     
     
    		TableModel model = new AbstractTableModel() {
     
    			Object[][] data = { 
    						            { 1, "abc", "def", "ghi" },
    						            { 2, null, null, null },
    						            { 3, null, "def", "ghi" }
    							  };
    			String[] columns = { "id", "A", "B", "C" };
     
     
    			@Override
    			public String getColumnName(int col) {
    				return columns[col];
    			}
     
    			@Override
    			public boolean isCellEditable(int row, int col) {
    				return col>0;
    			}
     
    			@Override
    			public Class<?> getColumnClass(int col) { 
    				switch(col) {
    				case 0:
    					return Integer.class;
    				default:
    					return String.class;
    				}
    			}
     
    			@Override
    			public int getColumnCount() {
    				return columns.length;
    			}
     
    			@Override
    			public int getRowCount() {
    				return data.length;
    			}
     
    			@Override
    			public Object getValueAt(int row, int col) {
    				return data[row][col];
    			} 
     
    			@Override
    			public void setValueAt(Object value, int row, int col) {
    				data[row][col]=value; 
    			}
     
     
    		};
     
    		JTable table = new JTable(model);
     
    		TableColumn column = table.getColumnModel().getColumn(2);
    		column.setCellRenderer(new BorderTableCellRenderer(table.getDefaultRenderer(String.class)));
     
    		return table;
    	}
     
    	private static class BorderTableCellRenderer implements TableCellRenderer {
     
    		private final static Border BORDER = BorderFactory.createLineBorder(Color.RED, 2);
     
    		private TableCellRenderer renderer;
     
    		public BorderTableCellRenderer(TableCellRenderer renderer) {
    			this.renderer=renderer;
    		}
     
    		@Override
    		public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row,
    				int column) {
    			JComponent component = (JComponent)renderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
    			boolean isnull=true;
    			for(int i=1; i<4; i++) {
    				String valueCol = (String) table.getValueAt(row, i);
    				if ( valueCol!=null && !valueCol.trim().isEmpty() ) {
    					isnull=false;
    					break;
    				}
    			}
    			if ( isnull ) {
    				if ( !hasFocus ) {
    					component.setBorder(BORDER);
    				}
    				else {
    					component.setBorder(BorderFactory.createCompoundBorder(component.getBorder(), BORDER));
    				}
    			}
    			return component;
    		}
     
    	} 
     
    }
    Tu peux même faire
    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
    @Override
    		public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row,
    				int column) {
    			JComponent component = (JComponent)renderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
    			boolean isnull=true;
    			for(int i=1; i<4; i++) {
    				String valueCol = (String) table.getValueAt(row, i);
    				if ( valueCol!=null && !valueCol.trim().isEmpty() ) {
    					isnull=false;
    					break;
    				}
    			}
    			if ( isnull ) {
    				component.setBorder(BorderFactory.createCompoundBorder(component.getBorder(), BORDER));
    			}
    			return component;
    		}
    J'hésite entre aimer et ne pas aimer d'un côté, on a pas l'effet de changement/superposition sur la partie bordée due au focus, d'un autre on a une bordure blanche qui fait un peu bizarre, peut être avec un autre type de ligne spéciale, ça passerait mieux, genre comme ça :

    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
    private static class BorderTableCellRenderer implements TableCellRenderer {
     
     
    		private final static BasicStroke STROKE = new BasicStroke(1.0f,
                    BasicStroke.CAP_ROUND,
                    BasicStroke.JOIN_ROUND,
                    0.0f, new float[]{2f,4f}, 0.0f);
    		private final static Border BORDER = BorderFactory.createStrokeBorder(STROKE, Color.RED);
     
    		private TableCellRenderer renderer;
     
    		public BorderTableCellRenderer(TableCellRenderer renderer) {
    			this.renderer=renderer;
    		}
     
    		@Override
    		public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row,
    				int column) {
    			JComponent component = (JComponent)renderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
    			boolean isnull=true;
    			for(int i=1; i<4; i++) {
    				String valueCol = (String) table.getValueAt(row, i);
    				if ( valueCol!=null && !valueCol.trim().isEmpty() ) {
    					isnull=false;
    					break;
    				}
    			}
    			if ( isnull ) {
    				component.setBorder(BorderFactory.createCompoundBorder(component.getBorder(), BORDER));
    			}
    			return component;
    		}
     
    	}
    L'expression "ça marche pas" ne veut rien dire. Indiquez l'erreur, et/ou les comportements attendus et obtenus, et donnez un Exemple Complet Minimal qui permet de reproduire le problème.
    La plupart des réponses à vos questions sont déjà dans les FAQs ou les Tutoriels, ou peut-être dans une autre discussion : utilisez la recherche interne.
    Des questions sur Java : consultez le Forum Java. Des questions sur l'EDI Eclipse ou la plateforme Eclipse RCP : consultez le Forum Eclipse.
    Une question correctement posée et rédigée et vous aurez plus de chances de réponses adaptées et rapides.
    N'oubliez pas de mettre vos extraits de code entre balises CODE (Voir Mode d'emploi de l'éditeur de messages).
    Nouveau sur le forum ? Consultez Les Règles du Club.

  5. #5
    Membre du Club
    Profil pro
    Inscrit en
    Janvier 2006
    Messages
    200
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2006
    Messages : 200
    Points : 66
    Points
    66
    Par défaut
    Merci pour ta solution joel, elle est un peu plus propre que celle définit initialement. On se rapproche du purisme là !

    Et par rapport à ma problématique de base, est-ce possible d'activer temporairement le renderer (getTableCellRendererComponent) à la suite d'une action ?
    Par exemple, j'ajoute une ligne dans ma jtable via un bouton "Ajouter". Je saisis les 2 premières colonnes sur les 3 colonnes de ma nouvelle ligne puis je clique sur un bouton "Valider".
    J'aimerais qu'à ce moment là, le renderer s'active et que la 3ème colonne de ma nouvelle ligne, qui est vide, s'affiche avec un border rouge.
    Le renderer (getTableCellRendererComponent) se désactive alors, jusqu'à la prochaine action sur le bouton "Valider".

  6. #6
    Modérateur
    Avatar de joel.drigo
    Homme Profil pro
    Ingénieur R&D - Développeur Java
    Inscrit en
    Septembre 2009
    Messages
    12 430
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur R&D - Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2009
    Messages : 12 430
    Points : 29 131
    Points
    29 131
    Billets dans le blog
    2
    Par défaut
    Par exemple avec un booléen :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    @Override
    		public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row,
    				int column) {
    			JComponent component = (JComponent)renderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
                            if ( disabled ) { // si disabled est true, on retourne directement le renderer standard, sans passer par la partie qui met un cadre rouge...
                                return component;
                            }
    			boolean isnull=true;
    Il te suffit d'avoir une méthode setDisabled() sur la classe BorderTableCellRenderer pour changer la valeur de ce booléen, d'avoir accès à la référence de l'instance pour pouvoir appeler la méthode et activer ou pas le renderer sur l'action que tu veux. Il faudra juste forcer un rafraichissement de la JTable (un updateUI() devrait suffire ou un repaint()), parce qu'elle ne peut pas savoir quand tu changes la valeur ce booléen.

    Une autre solution serait de d'alterner entre les 2 renderers (le BorderTableCelleRenderer et l'instance d'origine) avec l'appel de la méthode column.setCellRenderer();.
    L'expression "ça marche pas" ne veut rien dire. Indiquez l'erreur, et/ou les comportements attendus et obtenus, et donnez un Exemple Complet Minimal qui permet de reproduire le problème.
    La plupart des réponses à vos questions sont déjà dans les FAQs ou les Tutoriels, ou peut-être dans une autre discussion : utilisez la recherche interne.
    Des questions sur Java : consultez le Forum Java. Des questions sur l'EDI Eclipse ou la plateforme Eclipse RCP : consultez le Forum Eclipse.
    Une question correctement posée et rédigée et vous aurez plus de chances de réponses adaptées et rapides.
    N'oubliez pas de mettre vos extraits de code entre balises CODE (Voir Mode d'emploi de l'éditeur de messages).
    Nouveau sur le forum ? Consultez Les Règles du Club.

  7. #7
    Membre du Club
    Profil pro
    Inscrit en
    Janvier 2006
    Messages
    200
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2006
    Messages : 200
    Points : 66
    Points
    66
    Par défaut
    J'ai cherché sur différents forums, mais la dernière solution que tu proposes ne fonctionne pas car getTableCellRendererComponent() n'est pas appelé.
    Pour faire simple, j'ai fait un mix du code entre mon code de base et les différentes solutions que tu m'as proposées joel.

    Voici mon code du moment, pour créer la JTable :

    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
           // Define JTable property
     
            this.table_phases = new JTable() {
     
                @Override
                public boolean isCellEditable(int row, int column) {
     
                    if (column == 1) return true;
                    return false;
                }
            };
     
            // Define JTable model
            DefaultTableModel model_phases = new DefaultTableModel(
                    new String[]{
                            "N° phase",
                            "Libellé",
                            "Référence 1",
                            "Référence 2"
                    }, 0);
            this.table_phases.setModel(model_phases);
            this.borderCellRenderer = new BorderTableCellRenderer();
            this.table_phases.setDefaultRenderer(Object.class, borderCellRenderer);
    - le code du getTableCellRendererComponent :

    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
     
    public class BorderTableCellRenderer extends DefaultTableCellRenderer {
     
            boolean isEnabled = false;
     
            public void setEnabled(boolean isEnabled) {
                this.isEnabled = isEnabled;
            }
     
            @Override
            public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
     
                Component cell = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
     
                if (isEnabled && cell instanceof JLabel && column==1) {
                    JLabel label = (JLabel) cell;
                    if (label.getText().isEmpty()) {
                        label.setBorder(BorderFactory.createLineBorder(Color.red));
                    } else {
                        label.setBorder(null);
                    }
                }
     
                return cell;
            }
        }
    - et le code de mon action qui doit mettre à jour les border lorsqu'une cellule est vide :

    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
     
        private boolean isDataValid() {
     
            boolean is_data_valid = true;
            this.borderCellRenderer.setEnabled(true);
     
            int i = 0;
            while (i < this.table_phases.getRowCount()) {
     
                String phase_id = (String) this.table_phases.getValueAt(i, 0);
     
               //Pour tester le déclenchement : this.table_phases.setValueAt(this.table_phases.getValueAt(i, 1), i, 1);
     
                if (((String) this.table_phases.getValueAt(i, 1)).isEmpty()) {
                    is_data_valid = false;
                }
     
                i = i + 1;
            }
     
            DefaultTableModel model = (DefaultTableModel)this.table_phases.getSelectionModel();
            model.fireTableDataChanged();
     
            this.table_phases.updateUI();
            this.table_phases.repaint();
            this.table_phases.revalidate();
     
            this.borderCellRenderer.setEnabled(false);
     
            return is_data_valid;
        }
    Comme tu peux le voir, j'ai même ajouté un fireTableDataChanged() pour accéder à la fonction getTableCellRendererComponent() mais cela ne le fait pas.

    Lorsque je passe du code vers l'écran de mon application, getTableCellRendererComponent() est bien appelé mais c'est trop tard car "this.borderCellRenderer.setEnabled(false);" a déjà eu lieu.

    Aurais-tu une idée du problème ?

  8. #8
    Modérateur
    Avatar de joel.drigo
    Homme Profil pro
    Ingénieur R&D - Développeur Java
    Inscrit en
    Septembre 2009
    Messages
    12 430
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur R&D - Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2009
    Messages : 12 430
    Points : 29 131
    Points
    29 131
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par bernidupont Voir le message
    J'ai cherché sur différents forums, mais la dernière solution que tu proposes ne fonctionne pas
    POC pour montrer que ça fonctionne :
    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
    public class JTableRendererDemo {
     
    	public static void main(String[] args) {
     
     
    		JFrame frame = new JFrame("Démo");
    		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
     
    		JCheckBox renderingButton = new JCheckBox("Activée");
     
    		JTable table = createTable();
    		TableColumn column = table.getColumnModel().getColumn(2);
    		BorderTableCellRenderer cellRenderer = new BorderTableCellRenderer(table.getDefaultRenderer(String.class));
    		column.setCellRenderer(cellRenderer);
     
    		renderingButton.setSelected(true);
    		renderingButton.addActionListener(e-> setRenderingEnabled(renderingButton.isSelected(), table, cellRenderer));
     
    		frame.add(new JScrollPane(table));
     
    		frame.add(renderingButton, BorderLayout.SOUTH);
     
     
    		frame.setSize(600, 400);
    		frame.setLocationRelativeTo(null);
    		frame.setVisible(true);
    	}
     
    	private static void setRenderingEnabled(boolean selected, JTable table, BorderTableCellRenderer cellRenderer) {
    		if ( cellRenderer.isEnabled()!=selected ) {
    			cellRenderer.setEnabled(selected);
    			table.updateUI();
    		}
    	}
     
    	private static JTable createTable() {
     
     
    		TableModel model = new AbstractTableModel() {
     
    			Object[][] data = { 
    						            { 1, "abc", "def", "ghi" },
    						            { 2, null, null, null },
    						            { 3, null, "def", "ghi" }
    							  };
    			String[] columns = { "id", "A", "B", "C" };
     
     
    			@Override
    			public String getColumnName(int col) {
    				return columns[col];
    			}
     
    			@Override
    			public boolean isCellEditable(int row, int col) {
    				return col>0;
    			}
     
    			@Override
    			public Class<?> getColumnClass(int col) { 
    				switch(col) {
    				case 0:
    					return Integer.class;
    				default:
    					return String.class;
    				}
    			}
     
    			@Override
    			public int getColumnCount() {
    				return columns.length;
    			}
     
    			@Override
    			public int getRowCount() {
    				return data.length;
    			}
     
    			@Override
    			public Object getValueAt(int row, int col) {
    				return data[row][col];
    			} 
     
    			@Override
    			public void setValueAt(Object value, int row, int col) {
    				data[row][col]=value; 
    			}
     
     
    		};
     
    		JTable table = new JTable(model);
    		return table;
     
    	}
     
    	private static class BorderTableCellRenderer implements TableCellRenderer {
     
    		private final static BasicStroke STROKE = new BasicStroke(1.0f,
                    BasicStroke.CAP_ROUND,
                    BasicStroke.JOIN_ROUND,
                    0.0f, new float[]{2f,4f}, 0.0f);
    		private final static Border BORDER = BorderFactory.createStrokeBorder(STROKE, Color.RED);
     
    		private TableCellRenderer renderer;
     
    		private boolean enabled;
     
    		public BorderTableCellRenderer(TableCellRenderer renderer) {
    			this.renderer=renderer;
    			this.enabled=true;
    		}
     
    		public void setEnabled(boolean enabled) {
    			this.enabled=enabled;
    		}
     
    		public boolean isEnabled() {
    			return enabled;
    		}
     
    		@Override
    		public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row,
    				int column) {
    			JComponent component = (JComponent)renderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
    			if ( enabled /*&& isSelected*/ ) {
    				boolean isnull=true;
    				for(int i=1; i<4; i++) {
    					String valueCol = (String) table.getValueAt(row, i);
    					if ( valueCol!=null && !valueCol.trim().isEmpty() ) {
    						isnull=false;
    						break;
    					}
    				}
    				if ( isnull ) {
    					component.setBorder(BorderFactory.createCompoundBorder(component.getBorder(), BORDER));
    				}
    			}
    			return component;
    		}
     
    	}  
     
    }
    Citation Envoyé par bernidupont Voir le message
    car getTableCellRendererComponent() n'est pas appelé.
    Dis comme ça, ça ne veut un peu rien dire... Laquelle, pas appelée par quoi ? Quand ?
    La méthode getTableCellRendererComponent() d'une instance TableCellRenderer est appelée lorsque la JTable (ou un composant lié) estime qu'elle doit remettre à jour son affichage...
    En réalité, on n'a pas à s'en préoccuper de qui fait quoi et quand c'est appelé ou pas : le contrat de cette méthode dit que la la méthode doit retourner le composant à afficher pour la cellule en fonction des paramètres reçus. Le reste c'est la cuisine interne de la JTable.


    Aurais-tu une idée du problème ?[/QUOTE]

    Euh, attention :
    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
     private boolean isDataValid() {
     
            boolean is_data_valid = true;
            this.borderCellRenderer.setEnabled(true);
     
            int i = 0;
            while (i < this.table_phases.getRowCount()) {
     
                String phase_id = (String) this.table_phases.getValueAt(i, 0);
     
               //Pour tester le déclenchement : this.table_phases.setValueAt(this.table_phases.getValueAt(i, 1), i, 1);
     
                if (((String) this.table_phases.getValueAt(i, 1)).isEmpty()) {
                    is_data_valid = false;
                }
     
                i = i + 1;
            }
     
            DefaultTableModel model = (DefaultTableModel)this.table_phases.getSelectionModel();
            model.fireTableDataChanged();
     
            this.table_phases.updateUI();
            this.table_phases.repaint();
            this.table_phases.revalidate();
     
            this.borderCellRenderer.setEnabled(false);
     
            return is_data_valid;
        }
    C'est exécuté dans le thread Swing ça ? Le thread Swing, comme n'importe quel thread ne peut exécuter qu'une seule chose à la fois, et comme c'est lui qui s'occupe de l'affichage : donc tu peux faire tout ce que tu veux 36 fois : on ne verra sur ce qui est affiché (visible) que l'effet de la dernière chose effectuée ! Et la dernière chose effectuée c'est borderCellRender.setEnabled(false).
    Quand au repaint() que tu fais, il est différé, donc s'exécutera toujours après la dernière instruction exécutée dans ta méthode. Le revalidate() ne sert à rien, il sert au layout.

    Je n'arrive pas trop à comprendre ce que tu cherches à faire :
    J'aimerais qu'à ce moment là, le renderer s'active et que la 3ème colonne de ma nouvelle ligne, qui est vide, s'affiche avec un border rouge.
    Le renderer (getTableCellRendererComponent) se désactive alors, jusqu'à la prochaine action sur le bouton "Valider".
    Si je comprends bien tu veux que la cellule s'entoure en rouge lorsqu'on clique sur le bouton "valider" et que certaines conditions sont réunies.
    Donc ton bouton "Valider" va faire quelque chose de similaire à la checkbox de mon POC :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    bouton.addActionListener(e-> setRenderingEnabled(renderingButton.isSelected(), !isDataValid(table) , cellRenderer)
    Dans isDataValid, il n'y a que l'établissement des conditions (le parcourt des données, les tests, etc., et rien d'autre.

    Je ne comprends pas le "alors" de ta seconde phrase : pour désactiver le renderer si on l'active. Alors quand ? Sur un évenement particulier (genre on saisit une valeur)? Sur une action sur le même bouton "valider" ? Au bout de 1 seconde ?

    En revanche, dans ta phrase du parle de troisème colonne vide, or le test dans ta méthode isDataList fait un test sur la colonne 1 (donc la deuxième).
    L'expression "ça marche pas" ne veut rien dire. Indiquez l'erreur, et/ou les comportements attendus et obtenus, et donnez un Exemple Complet Minimal qui permet de reproduire le problème.
    La plupart des réponses à vos questions sont déjà dans les FAQs ou les Tutoriels, ou peut-être dans une autre discussion : utilisez la recherche interne.
    Des questions sur Java : consultez le Forum Java. Des questions sur l'EDI Eclipse ou la plateforme Eclipse RCP : consultez le Forum Eclipse.
    Une question correctement posée et rédigée et vous aurez plus de chances de réponses adaptées et rapides.
    N'oubliez pas de mettre vos extraits de code entre balises CODE (Voir Mode d'emploi de l'éditeur de messages).
    Nouveau sur le forum ? Consultez Les Règles du Club.

  9. #9
    Membre du Club
    Profil pro
    Inscrit en
    Janvier 2006
    Messages
    200
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2006
    Messages : 200
    Points : 66
    Points
    66
    Par défaut
    La méthode getTableCellRendererComponent() d'une instance TableCellRenderer est appelée lorsque la JTable (ou un composant lié) estime qu'elle doit remettre à jour son affichage
    Je crois que c'est ça le fond de mon problème joel.
    Je me suis peut être mal expliqué : en gros, j'aimerais qu'après avoir cliqué sur un bouton de validation, les cellules vides de ma Jtable s'entourent avec un border rouge. Mais, après cette validation, si l'utilisateur remplit ces cellules vides entourées avec un border rouge, je ne souhaite pas que le border rouge de ces cellules change jusqu'à ce que l'utilisateur ait à nouveau cliqué sur le bouton de validation.
    En gros, j'aimerais que l'affichage de ces border soit activé temporairement au moment du clique sur le bouton de validation.

    Avec ton exemple en utilisant une checkbox, cela fonctionne mais pas exactement comme cela.
    C'est pour cela que je te parlais d'une map. Je crois qu'il n'y a pas le choix ici, puisqu'il faut enregistrer l'état d'une cellule au moment du clic et en fonction de cet état, gérer la couleur du border. Donc au lieu d'utiliser le booléen enabled, je pense qu'un tableau de booléen serait plus adapté ici.

    J'ai modifié un peu le code et j'obtiens bien le résultat souhaité.

    Voici mon renderer final :

    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
    public static class BorderTableCellRenderer implements TableCellRenderer {
     
            private TableCellRenderer renderer;
            private HashMap<String, Boolean> cellInValidationByRowAndColumn = new HashMap<>();
     
            public BorderTableCellRenderer(TableCellRenderer renderer) {
                this.renderer=renderer;
            }
     
            public void setCellInValidation(int row, int column, boolean inValidation) {
                cellInValidationByRowAndColumn.put(row +"_"+column, inValidation);
            }
     
            public boolean isCellInValidation(int row, int column) {
     
                if (cellInValidationByRowAndColumn.containsKey(row +"_"+column)) {
                    return cellInValidationByRowAndColumn.get(row +"_"+column);
                }
                return false;
            }
     
            @Override
            public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
     
                Component cell = renderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
     
                if (cell instanceof JLabel) {
     
                    JLabel label = (JLabel) cell;
     
                    if (isCellInValidation(row, column)) {
                        label.setBorder(BorderFactory.createLineBorder(Color.red));
                    }
                }
     
                return cell;
            }
        }
    Je sauvegarde l'état de chaque cellule contrôlée via la variable cellInValidationByRowAndColumn.

    Merci ton aide joel, on a finalement trouvé une bonne solution
    A bientôt.

  10. #10
    Modérateur
    Avatar de joel.drigo
    Homme Profil pro
    Ingénieur R&D - Développeur Java
    Inscrit en
    Septembre 2009
    Messages
    12 430
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur R&D - Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2009
    Messages : 12 430
    Points : 29 131
    Points
    29 131
    Billets dans le blog
    2
    Par défaut
    Tu peux désactiver l'état avec un CellEditorListener. Sinon au lieu d'une Map<,Boolean>, tu pourrais utiliser un set et faire juste un contains().
    L'expression "ça marche pas" ne veut rien dire. Indiquez l'erreur, et/ou les comportements attendus et obtenus, et donnez un Exemple Complet Minimal qui permet de reproduire le problème.
    La plupart des réponses à vos questions sont déjà dans les FAQs ou les Tutoriels, ou peut-être dans une autre discussion : utilisez la recherche interne.
    Des questions sur Java : consultez le Forum Java. Des questions sur l'EDI Eclipse ou la plateforme Eclipse RCP : consultez le Forum Eclipse.
    Une question correctement posée et rédigée et vous aurez plus de chances de réponses adaptées et rapides.
    N'oubliez pas de mettre vos extraits de code entre balises CODE (Voir Mode d'emploi de l'éditeur de messages).
    Nouveau sur le forum ? Consultez Les Règles du Club.

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

Discussions similaires

  1. [XL-2007] exécuter une macro automatiquement après la saisie dans une cellule
    Par Merryy dans le forum Macros et VBA Excel
    Réponses: 2
    Dernier message: 25/07/2015, 23h40
  2. Réponses: 13
    Dernier message: 04/03/2009, 23h05
  3. Réponses: 5
    Dernier message: 03/02/2009, 13h43
  4. Réponses: 2
    Dernier message: 17/11/2008, 12h40
  5. Lancement d'une macro après mise à jour d'une cellule
    Par Mukade dans le forum Macros et VBA Excel
    Réponses: 6
    Dernier message: 17/10/2007, 11h52

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