2 votes

JTable/setDefaultRenderer : plusieurs lignes sélectionnées

J'ai cherché littéralement sur tout l'Internet et je n'ai pas trouvé de solution fonctionnelle à mon problème.

J'ai un JTable à laquelle je veux changer dynamiquement les couleurs d'arrière-plan des éléments suivants plusieurs rangées sans affecter la couleur d'arrière-plan des autres rangées dont les couleurs auraient déjà été modifiées.

Le changement de couleur est déclenché par un actionListener sur un JMenuItem d'un ContextMenu comme le montre l'illustration suivante :

enter image description here

Le code que j'ai essayé jusqu'à présent est le suivant :

    JMenu highlightMenu = new JMenu("Highlight");

    // Add null
    for (Color color : Arrays.asList(Color.RED, Color.ORANGE, Color.YELLOW, Color.GREEN, Color.BLUE, Color.MAGENTA,
        Color.PINK, Color.GRAY)) {
      JMenuItem x = new JMenuItem();
      x.setOpaque(true);
      x.setBackground(color);

      highlightMenu.add(x);

      x.addHierarchyListener(e -> x.setText(tab.getTable()
          .getValueAt(tab.getTable().getSelectedRow(), tab.getTable().getColumn("Server").getModelIndex()).toString()));

      x.addActionListener(e -> IntStream.of(tab.getTable().getSelectedRows())
          .forEach(row -> ((Component) tab.getTable().getModel().getValueAt(row, 0)).setBackground(color)));

      // x.addActionListener(e -> {
      // IntStream.of(tab.getTable().getSelectedRows())
      // .forEach(r -> tab.getTable().setDefaultRenderer(Object.class, new
      // DefaultTableCellRenderer() {
      // @Override
      // public Component getTableCellRendererComponent(JTable table, Object value,
      // boolean isSelected,
      // boolean hasFocus, int row, int column) {
      // Component comp = super.getTableCellRendererComponent(table, value,
      // isSelected, hasFocus, row, column);

      // if (r == row) {
      // comp.setBackground(color);
      // } else {
      // comp.setBackground(null);
      // }

      // return comp;
      // }
      // }));

      // tab.getTable().repaint();
      // });
    }

Si quelqu'un a une solution qui fonctionne, veuillez la partager, ce serait vraiment apprécié !

EDIT 0 : J'ai nettoyé les éditions car elles étaient trop nombreuses, j'ai donc ajouté des chaînes de débogage et cette table ne se comporte pas du tout comme elle le devrait, veuillez voir la capture d'écran suivante :

enter image description here

Note : Je ne sais pas non plus pourquoi, mais il semble que la table soit itérée plusieurs fois (5) comme le montre la sortie ci-dessus, ce qui ne devrait pas être le cas, car chaque tableau de la table a été itéré. JMenuItem a son propre écouteur d'événements... Et il ne doit être déclenché qu'une seule fois, en fonction de la couleur ou de l'élément de menu sélectionné...

Et le tableau qui en résulte :

enter image description here

A partir du code suivant :

    for (Color color : Arrays.asList(Color.RED, Color.ORANGE, Color.YELLOW, Color.GREEN, Color.BLUE, Color.MAGENTA,
        Color.PINK, Color.GRAY)) {
      JMenuItem x = new JMenuItem();
      x.setOpaque(true);
      x.setBackground(color);
      x.setForeground(Color.BLACK);

      highlightMenu.add(x);

      x.addHierarchyListener(e -> x.setText(tab.getTable()
          .getValueAt(tab.getTable().getSelectedRow(), tab.getTable().getColumn("Server").getModelIndex()).toString()));

      x.addActionListener(e -> {
        IntStream.of(tab.getTable().getSelectedRows()).forEach(row -> this.highlightedRows.put(row, color)
        // this.highlightedRows.put(row, Arrays.asList(Color.BLACK, color)
        );

        tab.getTable().setDefaultRenderer(Object.class, new DefaultTableCellRenderer() {
          @Override
          public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
              boolean hasFocus, int row, int column) {
            Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);

            stdOut.println(String.format("%s -> %s", row, highlightedRows.get(row)));

            if (highlightedRows.get(row) != null) {
              stdOut.println("XXXXXXX");
              component.setBackground(highlightedRows.get(row));
            }

            // if (!isSelected && highlightedRows.containsKey(row)) {
            // component.setForeground(highlightedRows.get(row).get(0));
            // component.setBackground(highlightedRows.get(row).get(1));
            // }

            return component;
          }
        });
      });
    }

Comme vous le voyez, quelque chose ne tourne pas rond ici...

EDIT N : Presque résolu ce problème avec le code suivant :

    for (Color color : Arrays.asList(Color.RED, Color.ORANGE, Color.YELLOW, Color.GREEN, Color.BLUE, Color.MAGENTA,
        Color.PINK, Color.GRAY)) {
      final JMenuItem x = new JMenuItem();
      x.setOpaque(true);
      x.setBackground(color);
      x.setForeground(Color.BLACK);

      highlightMenu.add(x);

      x.addHierarchyListener(e -> x.setText(tab.getTable()
          .getValueAt(tab.getTable().getSelectedRow(), tab.getTable().getColumn("Server").getModelIndex()).toString()));

      x.addActionListener(e -> {
        IntStream.of(tab.getTable().getSelectedRows())
            .forEach(row -> this.highlightedRows.put(row, Arrays.asList(Color.BLACK, color)));
        // row -> this.highlightedRows.put(row, color)

        tab.getTable().setDefaultRenderer(Object.class, new DefaultTableCellRenderer() {
          @Override
          public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
              boolean hasFocus, int row, int column) {
            final Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row,
                column);

            stdOut.println(String.format("%s -> %s", row, highlightedRows.get(row)));

            if (highlightedRows.containsKey(row)) {
              component.setForeground(highlightedRows.get(row).get(0));
              component.setBackground(highlightedRows.get(row).get(1));
            } else {
              if (row % 2 == 0) {
                component.setBackground(javax.swing.UIManager.getLookAndFeelDefaults().getColor("Table.background"));
              } else {
                component
                    .setBackground(javax.swing.UIManager.getLookAndFeelDefaults().getColor("Table.alternateRowColor"));
              }

              component.setForeground(javax.swing.UIManager.getLookAndFeelDefaults().getColor("Table.foreground"));
            }

            if (isSelected) {
              component
                  .setForeground(javax.swing.UIManager.getLookAndFeelDefaults().getColor("Table.selectionForeground"));
              component
                  .setBackground(javax.swing.UIManager.getLookAndFeelDefaults().getColor("Table.selectionBackground"));
            }

            return component;
          }
        });
      });
    }

La seule chose qui se passe maintenant est que l'écouteur d'événement est appelé plus d'une fois, voir l'EDIT précédent, si cela est résolu alors ce serait 100% résolu, merci à tous !

2voto

majusebetter Points 126

Vous pourriez conserver une carte qui associe les indices de ligne aux couleurs et l'utiliser dans votre moteur de rendu par défaut. Dans l'écouteur d'action, il vous suffit de placer l'indice de ligne avec la couleur assignée dans cette carte.

final Map<Integer, Color> highlightedRows = new HashMap<>();
highlightedRows.put(1, Color.GREEN);
highlightedRows.put(2, Color.YELLOW);

table.setDefaultRenderer(Object.class, new DefaultTableCellRenderer() {

    @Override
    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
        final var cmp = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
        final var color = highlightedRows.get(row);
        if (color != null && !isSelected) {
            cmp.setBackground(color);
        }
        return cmp;
    }
});

Résultat

enter image description here

EDIT : Exemple entièrement fonctionnel

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.util.HashMap;
import java.util.Map;

import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableCellRenderer;

public class TableRowHighlightExample {

    private final Map<Integer, Color> highlightedRows = new HashMap<>();

    private JTable table;

    private JPopupMenu createHighlightMenu() {
        final JPopupMenu highlightMenu = new JPopupMenu();

        final Color[] colors = { 
                Color.RED, 
                Color.ORANGE, 
                Color.YELLOW, 
                Color.GREEN, 
                Color.BLUE, 
                Color.MAGENTA,
                Color.PINK, 
                Color.GRAY 
        };

        // Add null
        for (final Color color : colors) {
            final JMenuItem x = new JMenuItem();
            x.setOpaque(true);
            x.setBackground(color);

            highlightMenu.add(x);

            x.addActionListener(e -> {
                final int[] selectedRows = this.table.getSelectedRows();
                for (final int row : selectedRows) {
                    highlightedRows.put(row, color);
                }
            });
        }

        return highlightMenu;
    }

    private JTable createTable() {
        final String[] colNames = { "Column 0" };
        final Object[][] data = { { "Row 0" }, { "Row 1" }, { "Row 2" }, { "Row 3" } };

        final var table = new JTable(data, colNames);
        table.setPreferredSize(new Dimension(500, 200));
        table.setComponentPopupMenu(this.createHighlightMenu());

        table.setDefaultRenderer(Object.class, new DefaultTableCellRenderer() {

            @Override
            public Component getTableCellRendererComponent(
                    JTable table, Object value, boolean isSelected,
                    boolean hasFocus, int row, int column) {

                final var cmp = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
                cmp.setBackground(highlightedRows.get(row));
                return cmp;
            }
        });

        return table;
    }

    public void run() {
        final var frame = new JFrame();
        this.table = this.createTable();

        frame.add(new JScrollPane(this.table));
        frame.setSize(500, 200);        
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);     
    }

    public static void main(String[] args) {
        new TableRowHighlightExample().run();
    }
}

enter image description here

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X