2013-03-19 20 views
6

Attualmente sto creando un piccolo JTable e voglio evidenziare l'intestazione di colonna (e le intestazioni di riga - la parte di intestazione di riga è effettivamente funzionante) quando viene selezionata una cella per rendere più facile trova i nomi associati con questa cella. Ecco una foto:Evidenziando un'intestazione di colonna di un JTable

enter image description here

Ho già provato la commutazione fuori il renderer per l'intestazione con questo:

table.getTableHeader().setDefaultRenderer(new ColumnHeaderRenderer()); 

Ma è chiamato solo quando scatto sopra l'intestazione e dice sempre isSelected è falso .

Questo è il codice che uso per la riga-nomi, tra cui l'evidenziazione all'interno del renderer - codice non è da me, ho appena modificato un po ':

/* 
* Use a JTable as a renderer for row numbers of a given main table. 
* This table must be added to the row header of the scrollpane that 
* contains the main table. 
*/ 
public class RowNameTable extends JTable 
     implements ChangeListener, PropertyChangeListener { 

    private JTable main; 

    public RowNameTable(JTable table) { 
     main = table; 
     main.addPropertyChangeListener(this); 

     setFocusable(false); 
     setAutoCreateColumnsFromModel(false); 
     setModel(main.getModel()); 
     setSelectionModel(main.getSelectionModel()); 

     TableColumn column = new TableColumn(); 
     column.setHeaderValue(" "); 
     addColumn(column); 
     column.setCellRenderer(new RowNameRenderer(main)); 

     getColumnModel().getColumn(0).setPreferredWidth(table.getColumnModel().getColumn(0).getPreferredWidth()); 
     setPreferredScrollableViewportSize(getPreferredSize()); 
    } 

    @Override 
    public void addNotify() { 
     super.addNotify(); 

     Component c = getParent(); 

     // Keep scrolling of the row table in sync with the main table. 

     if (c instanceof JViewport) { 
      JViewport viewport = (JViewport) c; 
      viewport.addChangeListener(this); 
     } 
    } 

    /* 
    * Delegate method to main table 
    */ 
    @Override 
    public int getRowCount() { 
     return main.getRowCount(); 
    } 

    @Override 
    public int getRowHeight(int row) { 
     return main.getRowHeight(row); 
    } 

    /* 
    * This table does not use any data from the main TableModel, 
    * so just return a value based on the row parameter. 
    */ 
    @Override 
    public Object getValueAt(int row, int column) { 
     return Integer.toString(row + 1); 
    } 

    /* 
    * Don't edit data in the main TableModel by mistake 
    */ 
    @Override 
    public boolean isCellEditable(int row, int column) { 
     return false; 
    } 
// 
// Implement the ChangeListener 
// 

    public void stateChanged(ChangeEvent e) { 
     // Keep the scrolling of the row table in sync with main table 

     JViewport viewport = (JViewport) e.getSource(); 
     JScrollPane scrollPane = (JScrollPane) viewport.getParent(); 
     scrollPane.getVerticalScrollBar().setValue(viewport.getViewPosition().y); 
    } 
// 
// Implement the PropertyChangeListener 
// 

    public void propertyChange(PropertyChangeEvent e) { 
     // Keep the row table in sync with the main table 

     if ("selectionModel".equals(e.getPropertyName())) { 
      setSelectionModel(main.getSelectionModel()); 
     } 

     if ("model".equals(e.getPropertyName())) { 
      setModel(main.getModel()); 
     } 
    } 

    /* 
    * Borrow the renderer from JDK1.4.2 table header 
    */ 
    private static class RowNameRenderer extends DefaultTableCellRenderer { 

     private JTable main; 

     public RowNameRenderer(JTable main) { 
      this.main = main; 
      setHorizontalAlignment(JLabel.CENTER); 
     } 

     @Override 
     public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { 
      if (table != null) { 
       JTableHeader header = table.getTableHeader(); 

       if (header != null) { 
        setForeground(header.getForeground()); 
        setBackground(header.getBackground()); 
        setFont(header.getFont()); 
       } 
      } 

      if (isSelected) { 
       setFont(getFont().deriveFont(Font.BOLD)); 
      } 

      setText((value == null) ? "" : main.getColumnName(row)); 
      setBorder(UIManager.getBorder("TableHeader.cellBorder")); 

      return this; 
     } 
    } 
} 

E qui abbiamo la parte pertinente per creare la tabella:

costTableModel = new CostTableModel(costCalc); 
    table = new JTable(costTableModel); 
    table.setPreferredScrollableViewportSize(table.getPreferredSize()); 
    table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); 
    table.setCellSelectionEnabled(true); 

    scrollPane = new JScrollPane(table); 

    RowNameTable nameTable = new RowNameTable(table); 
    scrollPane.setRowHeaderView(nameTable); 

E il costTableModel di classe, solo per completezza:

public class CostTableModel extends AbstractTableModel { 
    private CostCalculator costCalc; 

    public CostTableModel(CostCalculator costCalc) { 
     this.costCalc = costCalc; 
    } 

    @Override 
    public int getRowCount() { 
     return costCalc.getPersonsList().size(); 
    } 

    @Override 
    public int getColumnCount() { 
     return costCalc.getPersonsList().size(); 
    } 

    @Override 
    public String getColumnName(int col) { 
     return costCalc.getPersonsList().get(col).getName(); 
    } 

    @Override 
    public Object getValueAt(int rowIndex, int columnIndex) { 
     Person debtor = costCalc.getPersonsList().get(rowIndex); 
     Person debtee = costCalc.getPersonsList().get(columnIndex); 

     return costCalc.getAmountOwed(debtor, debtee); 
    } 

    @Override 
    public Class getColumnClass(int c) { 
     return getValueAt(0, c).getClass(); 

    } 
} 

Grazie per il vostro aiuto in anticipo!

risposta

4

Il problema di base che avevo era che non c'era alcuna connessione tra l'intestazione della tabella e la modifica della selezione. In effetti, l'intestazione è davvero intelligente con i suoi ripetitori ...

Alla fine ho fornito la mia intestazione, che collegava un listener al modello di selezione della tabella e ridipinto l'intestazione della selezione modificata.

import java.awt.BorderLayout; 
import java.awt.Color; 
import java.awt.Component; 
import java.awt.EventQueue; 
import java.util.List; 
import javax.swing.Icon; 
import javax.swing.JFrame; 
import javax.swing.JScrollPane; 
import javax.swing.JTable; 
import javax.swing.RowSorter; 
import javax.swing.RowSorter.SortKey; 
import static javax.swing.SortOrder.ASCENDING; 
import static javax.swing.SortOrder.DESCENDING; 
import javax.swing.UIManager; 
import javax.swing.UnsupportedLookAndFeelException; 
import javax.swing.event.ListSelectionEvent; 
import javax.swing.event.ListSelectionListener; 
import javax.swing.table.DefaultTableCellRenderer; 
import javax.swing.table.DefaultTableModel; 
import javax.swing.table.JTableHeader; 

public class TestColumnHighlight { 

    public static void main(String[] args) { 
     new TestColumnHighlight(); 
    } 

    public TestColumnHighlight() { 
     EventQueue.invokeLater(new Runnable() { 
      @Override 
      public void run() { 
       try { 
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 
       } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { 
       } 

       JTable table = new JTable(); 
       DefaultTableModel model = new DefaultTableModel(
           new Object[]{"abc", "def", "ghi", "jkl"}, 
           0); 

       model.addRow(new Object[]{0, 0, 0, 0}); 
       model.addRow(new Object[]{0, 0, 0, 0}); 
       model.addRow(new Object[]{0, 0, 0, 0}); 
       model.addRow(new Object[]{0, 0, 0, 0}); 
       model.addRow(new Object[]{0, 0, 0, 0}); 

       table.setModel(model); 
       table.setTableHeader(new CustomTableHeader(table)); 
       table.getTableHeader().setDefaultRenderer(new ColumnHeaderRenderer()); 

       JFrame frame = new JFrame("Testing"); 
       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
       frame.setLayout(new BorderLayout()); 
       frame.add(new JScrollPane(table)); 
       frame.pack(); 
       frame.setLocationRelativeTo(null); 
       frame.setVisible(true); 
      } 
     }); 
    } 

    public class CustomTableHeader extends JTableHeader { 

     public CustomTableHeader(JTable table) { 
      super(); 
      setColumnModel(table.getColumnModel()); 
      table.getColumnModel().getSelectionModel().addListSelectionListener(new ListSelectionListener() { 
       @Override 
       public void valueChanged(ListSelectionEvent e) { 
        repaint(); 
       } 
      }); 
     } 

     @Override 
     public void columnSelectionChanged(ListSelectionEvent e) { 
      repaint(); 
     } 

    } 

    public class ColumnHeaderRenderer extends DefaultTableHeaderCellRenderer { 

     @Override 
     public Component getTableCellRendererComponent(JTable table, Object value, boolean selected, boolean focused, int row, int column) { 
      super.getTableCellRendererComponent(table, value, selected, focused, row, column); 

      int selectedColumn = table.getSelectedColumn(); 
      System.out.println("Selected " + selectedColumn + "-" + column); 
      if (selectedColumn == column) { 
       Color bg = table.getSelectionBackground(); 
       setBackground(bg); 
       setOpaque(true); 
      } else { 
       setOpaque(false); 
      } 

      return this; 
     } 

    } 

    public class DefaultTableHeaderCellRenderer extends DefaultTableCellRenderer { 

     public DefaultTableHeaderCellRenderer() { 
      setHorizontalAlignment(CENTER); 
      setHorizontalTextPosition(LEFT); 
      setVerticalAlignment(BOTTOM); 
      setOpaque(false); 
     } 

     @Override 
     public Component getTableCellRendererComponent(JTable table, Object value, 
         boolean isSelected, boolean hasFocus, int row, int column) { 
      super.getTableCellRendererComponent(table, value, 
          isSelected, hasFocus, row, column); 
      JTableHeader tableHeader = table.getTableHeader(); 
      if (tableHeader != null) { 
       setForeground(tableHeader.getForeground()); 
      } 
      setIcon(getIcon(table, column)); 
      setBorder(UIManager.getBorder("TableHeader.cellBorder")); 
      return this; 
     } 

     protected Icon getIcon(JTable table, int column) { 
      SortKey sortKey = getSortKey(table, column); 
      if (sortKey != null && table.convertColumnIndexToView(sortKey.getColumn()) == column) { 
       switch (sortKey.getSortOrder()) { 
        case ASCENDING: 
         return UIManager.getIcon("Table.ascendingSortIcon"); 
        case DESCENDING: 
         return UIManager.getIcon("Table.descendingSortIcon"); 
       } 
      } 
      return null; 
     } 

     protected SortKey getSortKey(JTable table, int column) { 
      RowSorter rowSorter = table.getRowSorter(); 
      if (rowSorter == null) { 
       return null; 
      } 

      List sortedColumns = rowSorter.getSortKeys(); 
      if (sortedColumns.size() > 0) { 
       return (SortKey) sortedColumns.get(0); 
      } 
      return null; 
     } 
    } 
} 
+0

Prima di tutto, grazie! Mi sembra proprio quello di cui ho bisogno, al momento non ho il tempo di implementarlo nel mio codice, ma lo farò stasera e riferirò qui. – wlfbck

+0

Va bene, ora lo costruisco! Funziona perfettamente, tranne che per una piccola cosa (segnando diverse intestazioni di colonne), ma cercherò di capirlo da solo e di saperne di più su JTables :) – wlfbck

+0

hmm ... non capisco come ascoltare la selezione _row_ aiuta a ridipingere su _column_ cambiamenti di selezione. In realtà, non lo fa: seleziona una cella, quindi sposta la selezione nella stessa riga e l'evidenziazione della colonna non viene aggiornata. – kleopatra

8

Una leggera variante: come ho letto la domanda, il problema principale è l'intestazione non aggiorna il colonna cambio della selezione. Avere un'intestazione personalizzata ascolta riga le modifiche alla selezione non sono di grande aiuto per tale scenario.

In effetti, una JTableHeader già è in ascolto su ColumnModel e la notifica di modifica del modello include le modifiche alla selezione. il metodo columnSelectionChange è volutamente implementato solo di non fare nulla:

// --Redrawing the header is slow in cell selection mode. 
// --Since header selection is ugly and it is always clear from the 
// --view which columns are selected, don't redraw the header. 

Un intestazione personalizzata può semplicemente implementare per ridisegnare (qui pigro me lo fa nel metodo factory del tavolo solo per risparmiarmi il cablaggio al tavolo, si può facilmente rendilo un corso indipendente :-).

final JTable table = new JTable(new AncientSwingTeam()) { 

    @Override 
    protected JTableHeader createDefaultTableHeader() { 
     // subclassing to take advantage of super's auto-wiring 
     // as ColumnModelListener 
     JTableHeader header = new JTableHeader(getColumnModel()) { 

      @Override 
      public void columnSelectionChanged(ListSelectionEvent e) { 
       repaint(); 
      } 

     }; 
     return header; 
    } 

}; 
table.setCellSelectionEnabled(true); 
table.getTableHeader().setDefaultRenderer(new ColumnHeaderRenderer()); 

anche ottimizzato renderer di Mad un po ', utilizzando la tabella api:

/** 
* Slightly adjusted compared to @Mad 
* - use table's selectionBackground 
* - use table's isColumnSelected to decide on highlight 
*/ 
public static class ColumnHeaderRenderer extends DefaultTableCellHeaderRenderer { 

    @Override 
    public Component getTableCellRendererComponent(JTable table, Object value, boolean selected, 
     boolean focused, int row, int column) { 
     super.getTableCellRendererComponent(table, value, selected, focused, row, column); 
     if (table.isColumnSelected(column)) { 
      setBackground(table.getSelectionBackground()); 
     } 
     return this; 
    } 
} 

Per quanto riguarda l'osservazione:

dice sempre isSelected è falso

Il motivo è una leggera stranezza in BasicTableHeaderUI:

ui.selected != columnModel.selected 

uiSelected è la colonna che sarà accessibile alle associazioni di tasti - se il laf lo supporta e l'intestazione è focusOwner. Non ha davvero senso per me, ma definire completamente la semantica della selezione di ui e columnModel è caduto nell'eccitazione per il nuovo pupazzetto, che è stato dimenticato ;-)

+0

bello, funziona. Tuttavia non capisco perché DefaultTableCellHeaderRenderer sia solo sull'importazione sun.swing.table e non javax.swing.table – Edoz

+0

Come hai avuto accesso a 'DefaultTableCellHeaderRenderer'? –