如何在DragAndDrop期间在Main-JTable上绘制RowHeader-JTable的Dropline?

我在JScrollPane的Viewport中使用第二个JTable来为主表构建RowHeader。 主表上的DragAndDrop已禁用。 在rowheader表上启用DnD。

如果用户启动了rowheader上的Drag,我想在主表上扩展绘制的rowheader下划线(图像中的黑线)(如图中的绿线)。

维护人员的下线

有人对我有建议吗?
这是SSCCE:

import java.awt.Component; import java.awt.EventQueue; import java.awt.Font; import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.Transferable; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import javax.swing.DropMode; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.JViewport; import javax.swing.TransferHandler; import javax.swing.UIManager; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.JTableHeader; import javax.swing.table.TableColumn; public class DNDLinePainterExampleMain extends JFrame { public DNDLinePainterExampleMain() { JTable mainTable = new JTable(4, 3); mainTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); JTable rowTable = new RowHeaderTable(mainTable); rowTable.setAutoscrolls(true); rowTable.setDragEnabled(true); rowTable.setTransferHandler(new RowHeaderTransferHandler()); rowTable.setDropMode(DropMode.INSERT_ROWS); JScrollPane scrollPane = new JScrollPane(mainTable); scrollPane.setRowHeaderView(rowTable); scrollPane.setCorner(JScrollPane.UPPER_LEFT_CORNER, rowTable.getTableHeader()); this.add(scrollPane); } public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { JFrame f = new DNDLinePainterExampleMain(); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.pack(); f.setVisible(true); } }); } /* * 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. from: * http://tips4java.wordpress.com/2008/11/18/row-number-table/ */ public class RowHeaderTable extends JTable implements ChangeListener, PropertyChangeListener { private final JTable table; public RowHeaderTable(JTable table) { this.table = table; table.addPropertyChangeListener(this); setFocusable(false); setAutoCreateColumnsFromModel(false); updateRowHeight(); updateModel(); updateSelectionModel(); TableColumn column = new TableColumn(); column.setHeaderValue(""); addColumn(column); column.setCellRenderer(new RowNumberRenderer()); getColumnModel().getColumn(0).setPreferredWidth(50); setPreferredScrollableViewportSize(getPreferredSize()); getTableHeader().setReorderingAllowed(false); } @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 table.getRowCount(); } @Override public int getRowHeight(int row) { return table.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; } // implements ChangeListener @Override 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); } // implements PropertyChangeListener @Override public void propertyChange(PropertyChangeEvent e) { // Keep the row table in sync with the main table if ("rowHeight".equals(e.getPropertyName())) updateRowHeight(); if ("selectionModel".equals(e.getPropertyName())) updateSelectionModel(); if ("model".equals(e.getPropertyName())) updateModel(); } private void updateRowHeight() { setRowHeight(table.getRowHeight()); } private void updateModel() { setModel(table.getModel()); } private void updateSelectionModel() { setSelectionModel(table.getSelectionModel()); } /* * Borrow the renderer from JDK1.4.2 table header */ private class RowNumberRenderer extends DefaultTableCellRenderer { public RowNumberRenderer() { 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) ? "" : value.toString()); setBorder(UIManager.getBorder("TableHeader.cellBorder")); return this; } }//class RowNumberRenderer }//class RowHeaderTable public class RowHeaderTransferHandler extends TransferHandler { @Override public int getSourceActions(JComponent c) { return COPY_OR_MOVE; } @Override protected Transferable createTransferable(JComponent c) { return new StringSelection(c.getName()); } @Override public boolean canImport(TransferSupport supp) { return true; } }//class RowHeaderTransferHandler }//class DNDLinePainterExampleMain 

我建议你这样做(使用Swing的拖放function):

  RowHeaderTable rowTable = new RowHeaderTable(mainTable); rowTable.setAutoscrolls(true); rowTable.setDragEnabled(true); rowTable.setTransferHandler(new RowHeaderTransferHandler(mainTable)); rowTable.setDropMode(DropMode.INSERT_ROWS); try { DropTarget dropTarget = rowTable.getDropTarget(); dropTarget.addDropTargetListener(rowTable); } catch(TooManyListenersException e) { e.printStackTrace(); } 

现在你必须实现DropTargetListener

 public class RowHeaderTable extends JTable implements ChangeListener, PropertyChangeListener, DropTargetListener { 

这些是您应该感兴趣的两种方法:

  @Override public void dragEnter(DropTargetDragEvent dtde) { shouldPaint = true } @Override public void dragExit(DropTargetEvent dte) { shouldPaint = false } 

现在你应该使用mainTable操纵你的mainTable (画出这条“绿线”):

  @Override public boolean canImport(TransferSupport supp) { DropLocation location = supp.getDropLocation(); if(location instanceof javax.swing.JTable.DropLocation && shouldPaint) { javax.swing.JTable.DropLocation tableLocation = (javax.swing.JTable.DropLocation) location; int rowToInsert = tableLocation.getRow(); //paint somehow the "green line" //remember that canImport is invoked when you move your mouse } return true; } 

基本上你应该实现自己的行渲染器或类似的东西(比如@naugler的答案)。 你应该以某种方式为你的TransferHandler提供布尔值的shouldPaint 。 但这不是一件很重要的事情。

希望这可以帮助。

好的。 忏悔时间。 这是一些粗糙,钝的乐器代码。 我为无法提供更优雅或更干净的解决方案而道歉。 我是一个在深黑暗的地牢中的1级游客。

此自定义代码属于快速和肮脏的类别。 它不适用于列,我不确定所有边缘情况,并且我不知道在绘制例程中间窃取另一个组件图形上下文的规则。 底线:这是一个例子,而不是一个完整的解决方案。

因为您使用单独的表进行拖放(您的rowTable),所以该表没有很好的方法可以绘制到mainTable。 应该注意的是,使用PropertyChangeListeners可能会有一个光滑而有光泽的路径或者某个事件来向mainTable发送事件,但我没有管理它。 这是BasicTableUI的自定义扩展:

 public class ExtendedDropLineTableUI extends BasicTableUI { private JTable drawTable; private Integer oldRow; //We give this UI instance a reference to a separate table for drawing public ExtendedDropLineTableUI(JTable drawTable) { this.drawTable = drawTable; } @Override public void paint(Graphics g, JComponent c) { super.paint(g, c); paintExtendedDropLine(); } private void paintExtendedDropLine() { JTable.DropLocation loc = table.getDropLocation(); if (loc == null) { drawTable.repaint(); return; } //get the correct line color. no color? no line! Color color = UIManager.getColor("Table.dropLineColor"); if (color == null) { return; } //try to repaint the draw table only if the row changes if (oldRow != null && oldRow != loc.getRow()) { drawTable.repaint(); } oldRow = loc.getRow(); //get location of cell rectangle int row = loc.getRow(); int col = loc.getColumn(); if (col >= table.getColumnCount()) { col--; } Rectangle rect = table.getCellRect(row, col, true); //adjust rectangle to fit between the cells if (rect.y == 0) { rect.y = -1; } else { rect.y -= 2; } //what's a line but a really thin rectangle? rect.height = 3; //extend the rectangle to the width of the drawing table rect.width = drawTable.getWidth(); //draw the rectangle Graphics g = drawTable.getGraphics(); g.setColor(color); g.fillRect(rect.x, rect.y, rect.width, rect.height); } } } 

您需要将此UI提供给rowTable,以便它可以绘制到mainTable,如下所示:

 rowTable.setUI(new ExtendedDropLineTableUI(mainTable)); 

我严重依赖实际的源代码来劫持掉线的绘制,因为有关这些东西的方法都是私有的。

编辑我忘了提一下,我担心你可能会试图通过拖动rowTable中的单元格来重新排列mainTable中的整行。 这个解决方案也没有考虑到这一点,它只是画线。 为此,你真的需要一个更优雅的解决方案,或者一个检查,它使mainTable中的行与rowTable保持同步。

我的解决方案使用了稍微不同的方法,因为我们想在现有组件之上添加一些自定义绘图。 因此,我选择使用GlassPane并在那里进行绘画。 这样,突出显示可以是您可以想象的全部,并且其大小不限于单元大小,因为它可以使用例如单元渲染器的方法。

要运行此示例,您需要从此站点下载FinalGlassPane 。 这是必需的,因为我们使用其捕获事件的能力(将使用常规GlassPane )。 如果您知道在拖动最终结束时捕获事件的不同方式,则可以一起避免。 如果你知道一个,请分享。 对我来说,这个效果最好,而且我喜欢有一个GlassPane可以捕获事件并且不会全部消耗它们。

 import java.awt.*; import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.Transferable; import java.awt.dnd.*; import java.awt.event.AWTEventListener; import java.awt.event.MouseEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import javax.swing.*; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.JTableHeader; import javax.swing.table.TableColumn; public class DNDLinePainterExampleMain extends JFrame { public int x = -1; public int y = -1; private boolean isDragged = false; public FinalGlassPane glassPane; private boolean isOutsideTable = false; public DNDLinePainterExampleMain() { final JTable mainTable = new JTable(4, 3); mainTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); final JTable rowTable = new RowHeaderTable(mainTable); rowTable.setAutoscrolls(true); rowTable.setDragEnabled(true); rowTable.setTransferHandler(new RowHeaderTransferHandler()); rowTable.setDropMode(DropMode.INSERT_ROWS); final JScrollPane scrollPane = new JScrollPane(mainTable); scrollPane.setRowHeaderView(rowTable); scrollPane.setCorner(JScrollPane.UPPER_LEFT_CORNER, rowTable.getTableHeader()); final JPanel panel = new JPanel(); DragSourceMotionListener dsml = new DragSourceMotionListener() { @Override public void dragMouseMoved(DragSourceDragEvent dsde) { isDragged = true; isOutsideTable = true; Component source = dsde.getDragSourceContext().getComponent(); //the coordinates of the drag event in screen coords Point toConvert = new Point(dsde.getX(), dsde.getY()); //convert to source coords SwingUtilities.convertPointFromScreen(toConvert, source); int rowMargin = rowTable.getRowMargin(); Point toTest = new Point(toConvert.x, toConvert.y); for (int i = 0; i < rowTable.getRowCount(); i++) { Rectangle bounds = rowTable.getCellRect(i, 0, true); boolean isIn = bounds.contains(toTest); // System.out.println("bounds = "+bounds+"; rowMargin = "+rowMargin+"; i = "+i+"; isIn = "+isIn); if (isIn) { isOutsideTable = false; int hHalf = bounds.height / 2; int hIn = toTest.y - bounds.y; boolean isTop = false; if (hIn < hHalf) { isTop = true; } x = bounds.width; y = bounds.y - rowMargin; if (!isTop) { y += bounds.height; } //now convert the point to the glass pane coordinates Point c = SwingUtilities.convertPoint(rowTable, x, y, glassPane); x = cx; y = cy; // System.out.println("hIn = "+hIn+"; isTop = "+isTop + ""); } } glassPane.repaint(); } }; DragSource ds = new DragSource(); ds.addDragSourceMotionListener(dsml); this.setContentPane(panel); panel.add(new JButton("Oi for testing")); panel.add(scrollPane); DragSource dragSource = DragSource.getDefaultDragSource(); dragSource.addDragSourceMotionListener(dsml); glassPane = new FinalGlassPane(this) { @Override public void eventDispatched(AWTEvent event) { super.eventDispatched(event); if (event instanceof MouseEvent) { //after drag is relesed we are back here with mouse entered event if (isDragged) { isDragged = false; repaint(); } } } @Override protected void paintComponent(Graphics g) { Graphics2D g2 = (Graphics2D) g; if (isDragged && !isOutsideTable) { g2.setPaint(Color.GREEN); g2.drawLine(x + 2, y + 1, x + mainTable.getWidth() - 4, y + 1); g2.drawLine(x, y, x + mainTable.getWidth(), y); g2.drawLine(x + 2, y - 1, x + mainTable.getWidth() - 4, y - 1); } } }; AWTEventListener al = (AWTEventListener) glassPane; Toolkit.getDefaultToolkit().addAWTEventListener(al, AWTEvent.MOUSE_MOTION_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK); this.setGlassPane(glassPane); glassPane.setVisible(true); } public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { JFrame f = new DNDLinePainterExampleMain(); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.pack(); f.setVisible(true); } }); } /* * 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. from: * http://tips4java.wordpress.com/2008/11/18/row-number-table/ */ public class RowHeaderTable extends JTable implements ChangeListener, PropertyChangeListener { private final JTable table; public RowHeaderTable(JTable table) { this.table = table; table.addPropertyChangeListener(this); setFocusable(false); setAutoCreateColumnsFromModel(false); updateRowHeight(); updateModel(); updateSelectionModel(); TableColumn column = new TableColumn(); column.setHeaderValue(""); addColumn(column); column.setCellRenderer(new RowNumberRenderer()); getColumnModel().getColumn(0).setPreferredWidth(50); setPreferredScrollableViewportSize(getPreferredSize()); getTableHeader().setReorderingAllowed(false); } @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 table.getRowCount(); } @Override public int getRowHeight(int row) { return table.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; } // implements ChangeListener @Override 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); } // implements PropertyChangeListener @Override public void propertyChange(PropertyChangeEvent e) { // Keep the row table in sync with the main table if ("rowHeight".equals(e.getPropertyName())) { updateRowHeight(); } if ("selectionModel".equals(e.getPropertyName())) { updateSelectionModel(); } if ("model".equals(e.getPropertyName())) { updateModel(); } } private void updateRowHeight() { setRowHeight(table.getRowHeight()); } private void updateModel() { setModel(table.getModel()); } private void updateSelectionModel() { setSelectionModel(table.getSelectionModel()); } /* * Borrow the renderer from JDK1.4.2 table header */ private class RowNumberRenderer extends DefaultTableCellRenderer { public RowNumberRenderer() { 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) ? "" : value.toString()); setBorder(UIManager.getBorder("TableHeader.cellBorder")); return this; } }//class RowNumberRenderer }//class RowHeaderTable public class RowHeaderTransferHandler extends TransferHandler { @Override public int getSourceActions(JComponent c) { return COPY_OR_MOVE; } @Override protected Transferable createTransferable(JComponent c) { return new StringSelection(c.getName()); } @Override public boolean canImport(TransferSupport supp) { return true; } }//class RowHeaderTransferHandler } 

感谢naugler,Xeon和Boro的巨大贡献,我现在正在使用他们的3个例子的组合。 它看起来像这样:

决赛桌下线

这是代码:

 import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Component; import java.awt.EventQueue; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.Transferable; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import javax.swing.DropMode; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.JTable.DropLocation; import javax.swing.JViewport; import javax.swing.RootPaneContainer; import javax.swing.SwingUtilities; import javax.swing.TransferHandler; import javax.swing.UIManager; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.JTableHeader; import javax.swing.table.TableColumn; public class DNDLinePainterSolutionMain { public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { JFrame f = new MainFrame(); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.pack(); f.setVisible(true); } }); } }//public class DNDLinePainterSolutionMain class MainFrame extends JFrame { public MainFrame() { JTable mainTable = new JTable(4, 3); mainTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); JTable rowTable = new RowHeaderTable(mainTable); rowTable.setAutoscrolls(true); rowTable.setDragEnabled(true); rowTable.setTransferHandler(new RowHeaderTransferHandler()); rowTable.setDropMode(DropMode.INSERT_ROWS); //install the DropLocation-Extension: rowTable.addPropertyChangeListener("dropLocation", new DropLocationRepainter(this)); JScrollPane scrollPane = new JScrollPane(mainTable); scrollPane.setRowHeaderView(rowTable); scrollPane.setCorner(JScrollPane.UPPER_LEFT_CORNER, rowTable.getTableHeader()); this.add(scrollPane); } }//class MainFrame class RowHeaderTransferHandler extends TransferHandler { @Override public int getSourceActions(JComponent c) { return COPY_OR_MOVE; } @Override protected Transferable createTransferable(JComponent c) { return new StringSelection(c.getName()); } @Override public boolean canImport(TransferSupport supp) { return true; } }//class RowHeaderTransferHandler /** * Listens to a dropLocation-PropertyChange and repaints the DropLine. */ class DropLocationRepainter implements PropertyChangeListener { private static RowHeaderDropLineGlassPane glassPane; private final RootPaneContainer rootPaneContainer; public DropLocationRepainter(RootPaneContainer dndLinePainterSolutionMain) { this.rootPaneContainer = dndLinePainterSolutionMain; } @Override public void propertyChange(PropertyChangeEvent pce) { String propertyName = pce.getPropertyName(); if ("dropLocation".equals(propertyName) && pce.getNewValue() != null) { if (glassPane == null) { rootPaneContainer.getRootPane().setGlassPane( glassPane = new RowHeaderDropLineGlassPane((RowHeaderTable) pce .getSource())); } rootPaneContainer.getRootPane().getGlassPane().setVisible(true); repaintDropLocation(((JTable) pce.getSource()).getDropLocation()); } else if ("dropLocation".equals(propertyName)) rootPaneContainer.getRootPane().getGlassPane().setVisible(false); } private void repaintDropLocation(DropLocation loc) { Component c = rootPaneContainer.getRootPane().getGlassPane(); if (c instanceof RowHeaderDropLineGlassPane) { RowHeaderDropLineGlassPane glassPane = (RowHeaderDropLineGlassPane) c; glassPane.repaint(); } } }//class DropLocationRepainter class RowHeaderDropLineGlassPane extends JPanel { private RowHeaderTable table; public RowHeaderDropLineGlassPane(RowHeaderTable table) { this.table = table; setOpaque(false); } @Override public void paintComponent(Graphics g) { Graphics2D g2 = (Graphics2D) g; g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.3f)); Rectangle rect = table.getDropLineRect(); if (rect != null) { Rectangle r = SwingUtilities.convertRectangle(table, rect, this); g2.setColor(new Color(40, 80, 0)); g2.fill(r); } } }//class RowHeaderDropLineGlassPane /** * 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. From: http://tips4java.wordpress.com/2008/11/18/row-number-table/ * 

* Added {@code getDropLineRect()} for DropLine extension over the maintable. *

*/ class RowHeaderTable extends JTable implements ChangeListener, PropertyChangeListener { private final JTable mainTable; public RowHeaderTable(JTable mainTable) { this.mainTable = mainTable; mainTable.addPropertyChangeListener(this); setFocusable(false); setAutoCreateColumnsFromModel(false); updateRowHeight(); updateModel(); updateSelectionModel(); TableColumn column = new TableColumn(); column.setHeaderValue(""); addColumn(column); column.setCellRenderer(new RowNumberRenderer()); getColumnModel().getColumn(0).setPreferredWidth(50); setPreferredScrollableViewportSize(getPreferredSize()); getTableHeader().setReorderingAllowed(false); } /* * called from the class RowHeaderDropLineGlassPane */ public Rectangle getDropLineRect() { DropLocation loc = getDropLocation(); if (loc == null /* || !loc.isDropable() */) return null; final Rectangle lineRect = new Rectangle(); int index = loc.getRow(); if (index < 0) { lineRect.setRect(0, 0, 0, 0); return null; } Rectangle r = getCellRect(index, 0, true); if (index == getRowCount()) r.height = getCellRect(index - 1, 0, true).height;//if the last line is the DropTarget a height of 0 (of a non-existing Cell after the last row) is returned. lineRect.setRect(rx, ry - (r.height / 4d), getVisibleRect().width + mainTable.getWidth(), r.height / 2d - 1); return lineRect; } @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 mainTable.getRowCount(); } @Override public int getRowHeight(int row) { return mainTable.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; } // implements ChangeListener @Override 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); } // implements PropertyChangeListener @Override public void propertyChange(PropertyChangeEvent e) { // Keep the row table in sync with the main table if ("rowHeight".equals(e.getPropertyName())) updateRowHeight(); if ("selectionModel".equals(e.getPropertyName())) updateSelectionModel(); if ("model".equals(e.getPropertyName())) updateModel(); } private void updateRowHeight() { setRowHeight(mainTable.getRowHeight()); } private void updateModel() { setModel(mainTable.getModel()); } private void updateSelectionModel() { setSelectionModel(mainTable.getSelectionModel()); } /* * Borrow the renderer from JDK1.4.2 table header */ private class RowNumberRenderer extends DefaultTableCellRenderer { public RowNumberRenderer() { 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) ? "" : value.toString()); setBorder(UIManager.getBorder("TableHeader.cellBorder")); return this; } }//class RowNumberRenderer }//class RowHeaderTable