How to paint the Dropline of a RowHeader-JTable on the Main-JTable during a DragAndDrop?

后端 未结 4 1458
盖世英雄少女心
盖世英雄少女心 2021-02-07 15:16

I\'m using a second JTable in the Viewport of a JScrollPane to build a RowHeader for a main table. DragAndDrop on the main table is disabled. On the rowheader table DnD is enabl

4条回答
  •  独厮守ぢ
    2021-02-07 15:47

    Thanks to the great contributions from naugler, Xeon and Boro, I'm now using a combination of their 3 examples. It looks like this:

    the final table dropline

    And here's the code:

    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(r.x, r.y - (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

提交回复
热议问题