2013-03-19 21 views
6

Ich baue gerade eine kleine JTable, und möchte die Spaltenüberschrift markieren (und Zeilenköpfe - der Zeile-Header-Teil funktioniert tatsächlich), wenn eine Zelle ausgewählt ist, um es einfacher zu machen finde die assoziierten Namen mit dieser Zelle. Hier ist ein Bild:Hervorhebung einer Spaltenüberschrift einer JTable

enter image description here

ich bereits versucht, den Renderer für den Header mit diesem Schalt aus:

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

Aber es heißt nur, wenn ich auf dem Header klicken und sagt immer isSelected falsch ist .

Dies ist der Code i für die Zeilennamen verwenden, einschließlich der Highlight innerhalb des Renderers - Code ist nicht von mir, ich habe es nur ein wenig geändert:

/* 
* 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; 
     } 
    } 
} 

Und hier haben wir den entsprechenden Teil erstellen Sie die Tabelle:

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); 

Und die Klasse costTableModel, nur für Vollständigkeit halber:

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(); 

    } 
} 

Vielen Dank für Ihre Hilfe im Voraus!

Antwort

4

Das grundlegende Problem, das ich hatte, war, gab es keine Verbindung zwischen dem Tabellenheader und der Auswahländerung. In der Tat ist der Header wirklich clever mit seinen Repaints ...

Ich endete mit der Bereitstellung meiner eigenen Header, die einen Listener an das Auswahlmodell der Tabelle angeschlossen und neu lackiert die Überschrift auf der Auswahl geändert.

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

Zunächst einmal danke! Das sieht ziemlich genau so aus, wie ich es brauche, ich habe momentan keine Zeit es in meinen Code zu implementieren, aber ich werde es heute Abend tun und hier berichten. – wlfbck

+0

Okay, ich baue es jetzt ein! Es funktioniert perfekt, bis auf eine kleine Sache (markieren mehrere Spaltenüberschriften), aber ich werde versuchen, das herauszufinden und lernen Sie mehr über JTables :) – wlfbck

+0

hmm ... verstehe nicht, wie die Auswahl _row_ hören hilft beim Neuzeichnen bei _column_ Auswahländerungen. Eigentlich nicht: Markieren Sie eine Zelle und verschieben Sie die Auswahl in die gleiche Zeile. Die Hervorhebung der Spalte wird nicht aktualisiert. – kleopatra

8

Eine leichte Variante: als ich die Frage zu lesen, das Hauptproblem ist der Header auf Spalte Auswahländerung nicht aktualisiert. Mit einem benutzerdefinierten Header hören Sie Zeile Auswahländerungen hilft nicht viel für dieses Szenario.

Tatsächlich ist ein JTableHeader bereits ist hören das ColumnModel und die Änderungsbenachrichtigung des Modells enthält Auswahländerungen. Es wird nur die columnSelectionChange Methode absichtlich nichts tun implementiert:

// --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. 

Ein benutzerdefinierter Header einfach implementieren kann neu streichen (hier faul ich tue es in der Fabrik-Methode der Tabelle nur mir die Verdrahtung an den Tisch zu schonen, können Sie einfach mach es zu einer Stand-Alone-Klasse :-).

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()); 

Auch Mad Renderer ein bisschen gezwickt, Tisch api mit:

/** 
* 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; 
    } 
} 

In Bezug auf die Beobachtung:

immer sagt isSelected falsch ist

Der Grund ist, eine leichte Eigenart in BasicTableHeaderUI:

UiAusgewählt ist die Spalte, die für Tastatureingaben zugänglich ist - wenn die Laf unterstützt und die Kopfzeile FocusOwner ist. Für mich ergibt das eigentlich keinen Sinn, aber die Definition der Semantik von ui und columnModel Auswahl fiel in die Aufregung über das neue Baby fx, das ist vergessen ;-)

+0

nice one, es funktioniert. Allerdings verstehe ich nicht, warum DefaultTableCellHeaderRenderer nur beim Import von sun.swing.table und nicht von javax.swing.table ist. – Edoz

+0

Wie haben Sie auf 'DefaultTableCellHeaderRenderer' zugegriffen? –

Verwandte Themen