2

我有一个 JTable,它支持通过拖放行进行重新排序。这工作得很好。但是,每次在给定行上启动拖动时,鼠标悬停在的第一个后续行都会被选中。

尽管听起来很奇怪,但如果我单击一行(启动拖动)并将鼠标向右或向左移动(而不是向上或向下),这不会发生。在这种情况下(当将鼠标向右或向左移动时)启动拖动的行保持选中状态。

此行为适用于 java (Metal) l&f 以及 linux (GTK+) l&f。

它应该很有趣,我提供了一个SSCCE

package com.tracker.workspace.ui.views.test;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragSource;
import java.io.IOException;
import java.io.Serializable;
import java.util.UUID;
import javax.swing.DropMode;
import javax.swing.JFrame;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.TransferHandler;
import javax.swing.TransferHandler.TransferSupport;
import javax.swing.table.DefaultTableModel;

public class Main {

    private final JTable table;

    public Main() {

        table = new JTable();
        table.setModel(new DefaultTableModel() {

            @Override
            public int getRowCount() {
                return 5;
            }

            @Override
            public int getColumnCount() {
                return 5;
            }

            @Override
            public Object getValueAt(int r, int c) {
                return r * c;
            }
        });

        // Set up drag and drop on table.
        DragSource.getDefaultDragSource().createDefaultDragGestureRecognizer(table, DnDConstants.ACTION_MOVE, new DefaultDragToReorderTableGestureListener(table));
        table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        table.setDragEnabled(true);
        table.setDropMode(DropMode.INSERT_ROWS);
        table.setTransferHandler(new DefaultDragToReorderTableTransferHandler(table) {

            public boolean moveRow(int source, int target) {
                return true;
            }
        });

        // Set up frame.
        JFrame frame = new JFrame("MainFrame");
        frame.setSize(new Dimension(640, 480));
        frame.getContentPane().add(table, BorderLayout.CENTER);
        frame.pack();
        frame.setVisible(true);

    }

    public class DefaultDragToReorderTableGestureListener implements DragGestureListener {

        private final JTable table;
        private final int[] allowedDragColumns;

        public DefaultDragToReorderTableGestureListener(JTable table) {
            this.table = table;
            this.allowedDragColumns = new int[0];
        }

        public DefaultDragToReorderTableGestureListener(JTable table, int[] allowedDragColumns) {
            this.table = table;
            this.allowedDragColumns = allowedDragColumns;
        }

        public void dragGestureRecognized(DragGestureEvent e) {

            boolean canDrag = false;

            //We will only allow people to drag specific columns.
            if (allowedDragColumns.length > 0) {
                for (int i = 0; i < allowedDragColumns.length; i++) {
                    if (table.convertColumnIndexToModel(table.columnAtPoint(e.getDragOrigin())) == allowedDragColumns[i]) {
                        canDrag = true;
                    }
                }

            } else {
                canDrag = true;
            }

            if (!canDrag) {
                return;
            }

            // Get source row.
            //int sourceRow = table.convertRowIndexToModel(table.getSelectedRow());
            int sourceRow = table.rowAtPoint(e.getDragOrigin().getLocation());
            sourceRow = table.convertRowIndexToModel(sourceRow);

            //We will only permit the user to drag rows within the same table,
            //and not to other components. Thus, we do not need to create any
            //transfer object to hold any kind of data.
            e.startDrag(null, new IntegerTransferObject(sourceRow));
        }
    }

    public abstract class DefaultDragToReorderTableTransferHandler extends TransferHandler {

        private final JTable table;
        private final int[] allowedDropColumns;

        public DefaultDragToReorderTableTransferHandler(JTable table) {
            this.table = table;
            this.allowedDropColumns = new int[0];
        }

        public DefaultDragToReorderTableTransferHandler(JTable table, int[] allowedDropColumns) {
            this.table = table;
            this.allowedDropColumns = allowedDropColumns;
        }

        @Override
        public boolean canImport(TransferSupport support) {

            //Do not allow the user to perform any action which is not a drop action.
            if (!support.isDrop()) {
                return false;
            }

            //Do not allow the user to perform any action which involves a data
            //flavor other that an empty flavor.
            if (!support.getTransferable().isDataFlavorSupported(new DataFlavor(Integer.class, "IntegerTransferObject"))) {
                return false;
            }

            //We will only allow people to drag specific columns.
            boolean canDrop = false;
            if (allowedDropColumns.length > 0) {
                for (int i = 0; i < allowedDropColumns.length; i++) {
                    if (table.convertColumnIndexToModel(table.columnAtPoint(support.getDropLocation().getDropPoint())) == allowedDropColumns[i]) {
                        canDrop = true;
                    }
                }

            } else {
                canDrop = true;
            }

            if (!canDrop) {
                return false;
            }

            //Do not allow the user to do any drop action involving data from other components.
            if (support.getComponent() != table) {
                return false;
            }

            // Do not allow drops just above source row or just below source row.
            try {

                // Get source row.
                Integer sourceRow = (Integer) support.getTransferable().getTransferData(new DataFlavor(Integer.class, "IntegerTransferObject"));

                // Get target row.
                int targetRow = table.rowAtPoint(support.getDropLocation().getDropPoint());
                targetRow = table.convertRowIndexToModel(targetRow);

                // Validate that drop row is not adjacent to drag row.
                if (targetRow == sourceRow || sourceRow + 1 == targetRow || sourceRow - 1 == targetRow) {
                    return false;
                }

            } catch (Exception e) {
                return false;
            }

            return true;

        }

        @Override
        public boolean importData(TransferSupport support) {

            //Validate the drop action.
            if (!canImport(support)) {
                return false;
            }

            try {

                //Get source row.
                Integer sourceRow = (Integer) support.getTransferable().getTransferData(new DataFlavor(Integer.class, "IntegerTransferObject"));

                //Get target row.
                int targetRow = table.rowAtPoint(support.getDropLocation().getDropPoint());
                targetRow = table.convertRowIndexToModel(targetRow);

                //Reorder rows based on the user's gestures.
                return moveRow(sourceRow, targetRow);

            } catch (Exception e) {
                return false;
            }
        }

        /**
         * Returns the table which this handler operates on.
         *
         * @return  the <code>JTable</code> which this handler operates on.
         */
        protected JTable getTable() {
            return table;
        }

        /**
         * Moves the row at source position to the target position. This method
         * returns <b>false</b> by default, and is expected to be overridden.
         *
         * @param   source an <code>int</code> being the row which is to be moved.
         * @param   target an <code>int</code> being the row's target position.
         * @return  a <code>boolean</code> indicating wether the move was carried
         *          out successfully.
         */
        public abstract boolean moveRow(int source, int target);
    }

    public class AnonymousSource implements TransferObjectSource {

        public UUID getSourceId() {
            return null;
        }
    }

    public class IntegerTransferObject extends TransferObject {

        private final int integer;
        private final TransferObjectSource source;

        public IntegerTransferObject(int integer) {
            this.integer = integer;
            this.source = new AnonymousSource();
        }

        public IntegerTransferObject(int integer, TransferObjectSource source) {
            this.integer = integer;
            this.source = source;
        }

        public int getInteger() {
            return integer;
        }

        public DataFlavor[] getTransferDataFlavors() {
            return new DataFlavor[]{new DataFlavor(Integer.class, "IntegerTransferObject")};
        }

        public boolean isDataFlavorSupported(DataFlavor dataFlavor) {
            DataFlavor[] dataFlavors = getTransferDataFlavors();
            for (int i = 0; i < dataFlavors.length; i++) {
                if (dataFlavors[i].equals(dataFlavor)) {
                    return true;
                }
            }
            return false;
        }

        public Object getTransferData(DataFlavor dataFlavor) throws UnsupportedFlavorException, IOException {
            if (isDataFlavorSupported(dataFlavor)) {
                return integer;
            } else {
                throw new UnsupportedFlavorException(dataFlavor);
            }
        }

        @Override
        public TransferObjectSource getSource() {
            return source;
        }
    }

    public abstract class TransferObject implements Transferable, Serializable {

        public abstract TransferObjectSource getSource();

    }    

    public interface TransferObjectSource {

        public UUID getSourceId();

    }    

    public static void main(String[] args) {
        Main m = new Main();
    }
}
4

0 回答 0