你如何禁止两个JTable之间的拖放?

时间:2014-02-09 13:05:41

标签: java swing drag-and-drop jtable

我有两个JTable,它们被设置为可以拖放每个JTable中的行。问题是它允许我从一个JTable拖动一行到另一个JTable,我试图弄清楚如何阻止它。我只希望用户能够在同一个JTable中拖放一行。

换句话说,当我在当前表格外拖动一行并将鼠标悬停在空面板空间上时,鼠标光标会显示一个带有对角线的圆圈,这就是我想要的。 然而,当我将鼠标拖到另一个表上时,它会显示一个小的矩形“drop”图标,这就是我想阻止的。当用户尝试将此行拖动到其他表格的顶部时,我希望出现带有对角线的小圆圈。

这是一个展示问题的工作示例:

import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.DragSource;
import java.util.ArrayList;
import java.util.List;

import javax.activation.ActivationDataFlavor;
import javax.activation.DataHandler;
import javax.swing.DropMode;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.TransferHandler;
import javax.swing.table.AbstractTableModel;

public class JTableDnD extends JFrame 
{

    private JTable tableA;
    private JTable tableB;

    public JTableDnD() 
    {           
        // *** Create First Table ***

        List<Object[]> dataA = new ArrayList<Object[]>();
        dataA.add(new Object[] {"A1", "A1"});
        dataA.add(new Object[] {"A2", "A2"});
        dataA.add(new Object[] {"A3", "A3"});

        List<String> columnsA = new ArrayList<String>();
        columnsA.add("Column 1");
        columnsA.add("Column 2");

        tableA = new JTable(new TableModel(columnsA, dataA));

        tableA.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        tableA.setDragEnabled(true);
        tableA.setDropMode(DropMode.INSERT_ROWS);
        tableA.setFillsViewportHeight(true);
        tableA.setTransferHandler(new TableTransferHandler(tableA));

        JScrollPane scrollPaneA = new JScrollPane(tableA);

        // *** Create Second Table ***

        List<Object[]> dataB = new ArrayList<Object[]>();
        dataB.add(new Object[] {"B1", "B1"});
        dataB.add(new Object[] {"B2", "B2"});
        dataB.add(new Object[] {"B3", "B3"});

        List<String> columnsB = new ArrayList<String>();
        columnsB.add("Column 1");
        columnsB.add("Column 2");

        tableB = new JTable(new TableModel(columnsB, dataB));

        tableB.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        tableB.setDragEnabled(true);
        tableB.setDropMode(DropMode.INSERT_ROWS);
        tableB.setFillsViewportHeight(true);
        tableB.setTransferHandler(new TableTransferHandler(tableB));

        JScrollPane scrollPaneB = new JScrollPane(tableB);

        // *** Add ScrollPanes to Panel ***

        this.getContentPane().setLayout(new FlowLayout());

        add(scrollPaneA);

        JPanel emptyPanel = new JPanel();
        emptyPanel.setPreferredSize(new Dimension(100, 200));
        add(emptyPanel);

        add(scrollPaneB);

    }  // end JTableDnD constructor

    private static void createAndShowGUI() 
    {
        JFrame frame = new JTableDnD();
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }

    public static void main(String[] args) 
    {
        javax.swing.SwingUtilities.invokeLater(new Runnable() {

            public void run() {

                createAndShowGUI(); 

            }

        });
    }
}


interface Reorderable 
{
    public void reorder(int from, int to);
}


class TableModel extends AbstractTableModel implements Reorderable
{

    private List<String> columnNames;
    private List<Object[]> data;

    public TableModel(List<String> columnNames, List<Object[]> data) 
    {
        super();
        this.columnNames = columnNames;
        this.data = data;
    }

    @Override
    public void reorder(int from, int to) 
    {
        if (from < to)
        {
            to--;
        }

        Object[] row = data.remove(from);

        data.add(to, row);

        fireTableDataChanged();
    }

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

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

    @Override
    public String getColumnName(int column) 
    {
        return columnNames.get(column);
    }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex) 
    {
        return data.get(rowIndex)[columnIndex];
    }

}  // end TableModel


class TableTransferHandler extends TransferHandler 
{
    private final DataFlavor localObjectFlavor = new ActivationDataFlavor(Integer.class, DataFlavor.javaJVMLocalObjectMimeType, "Integer Row Index");

    private JTable table = null;


    public TableTransferHandler(JTable table) 
    {
        this.table = table;
    }


    @Override
    protected Transferable createTransferable(JComponent component) 
    {
        return new DataHandler(new Integer(table.getSelectedRow()), localObjectFlavor.getMimeType());
    }


    @Override
    public boolean canImport(TransferHandler.TransferSupport support) 
    {
        boolean b = support.getComponent() == table && 
                    support.isDrop() && 
                    support.isDataFlavorSupported(localObjectFlavor);

        table.setCursor(b ? DragSource.DefaultMoveDrop : DragSource.DefaultMoveNoDrop);

        return b;
    }


    @Override
    public int getSourceActions(JComponent component) 
    {
        return TransferHandler.COPY_OR_MOVE;
    }


    @Override
    public boolean importData(TransferHandler.TransferSupport support) 
    {
        JTable target = (JTable) support.getComponent();

        JTable.DropLocation dropLocation = (JTable.DropLocation) support.getDropLocation();

        int index = dropLocation.getRow();

        int max = table.getModel().getRowCount();

        if (index < 0 || index > max)
        {
            index = max;
        }

        target.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));

        try 
        {
            Integer rowFrom = (Integer) support.getTransferable().getTransferData(localObjectFlavor);

            if (rowFrom != -1 && rowFrom != index) 
            {
                ((Reorderable) table.getModel()).reorder(rowFrom, index);

                if (index > rowFrom)
                {
                    index--;
                }

                target.getSelectionModel().addSelectionInterval(index, index);

                return true;
            }
        } 
        catch (Exception e) 
        {
            e.printStackTrace();
        }

        return false;
    }


    @Override
    protected void exportDone(JComponent component, Transferable transferable, int action) 
    {
        if (action == TransferHandler.MOVE) 
        {
            table.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
        }
    }
}  // end TableTransferHandler

我相信我需要为canImport()方法添加一些额外的逻辑,以确保被删除的行来自同一个表,但我似乎无法弄明白。我已经尝试检查TransferSupport对象中传递给canImport()的数据,但它似乎没有任何返回确切JTable对象源的函数。

1 个答案:

答案 0 :(得分:1)

在简化的Swing DnD中,不支持获取可转让的来源。 TransferSupport`返回的组件是drop的目标,即以某种方式处理相关translable的组件。因此,如果您使用特定表配置了每个组件的TransferHandler,则支持的组件将始终是同一个表实例,并且您的检查将是非常简单的。

如果要根据发件人启用/禁用删除,则必须按每个拖动提供:拖动在exportAsDrag中开始并以exportDone结尾,因此您可以在这些中设置/清除发件人

@Override
protected void exportDone(JComponent component,
        Transferable transferable, int action) {
    table = null;
}

@Override
public void exportAsDrag(JComponent comp, InputEvent e, int action) {
    table = (JTable) comp;
    super.exportAsDrag(comp, e, action);
}

现在,您可以在表的多个实例中重用相同的处理程序实例:

TableTransferHandler handler = new TableTransferHandler();
tableA.setTransferHandler(handler);
tableB.setTransferHandler(handler);

顺便说一句:我不会弄乱游标,他们可以完全控制dnd子系统。