在过去的一周中,我试图解决此问题,但似乎无法找到解决方案。关于此主题的信息不多,因此很难找到示例或代码。

我这里有一个JList,它使用一个自定义TransferHandler创建一个自定义Transferable,这是所涉及类的代码供参考:

可转让:

package org.dinhware.swing.special;

import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;

/**
 * Created by: Niklas
 * Date: 20.10.2017
 * Alias: Dinh
 * Time: 20:03
 */

public class GenericTransferable<T> implements Transferable {
    static DataFlavor FLAVOR;
    private T object;

    GenericTransferable(T object) {
        GenericTransferable.FLAVOR = new DataFlavor(object.getClass(), object.getClass().getCanonicalName());
        this.object = object;
    }

    @Override
    public DataFlavor[] getTransferDataFlavors() {
        return new DataFlavor[]{FLAVOR};
    }

    @Override
    public boolean isDataFlavorSupported(DataFlavor flavor) {
        return flavor.equals(FLAVOR);
    }

    @Override
    public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
        return object;
    }
}


TransferHandler:

package org.dinhware.swing.special;

import javax.swing.*;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;

/**
 * Created by: Niklas
 * Date: 19.10.2017
 * Alias: Dinh
 * Time: 18:54
 */

@SuppressWarnings("unchecked")
public class HListItemTransferHandler<T> extends TransferHandler {

    @Override
    protected Transferable createTransferable(JComponent component) {
        JList<T> list = (JList<T>) component;
        index = list.getSelectedIndex();
        T transferredObject = list.getSelectedValue();
        return new GenericTransferable<>(transferredObject);
    }

    @Override
    public boolean canImport(TransferSupport info) {
        return info.isDataFlavorSupported(GenericTransferable.FLAVOR);
    }

    @Override
    public int getSourceActions(JComponent c) {
        return MOVE;
    }

    @Override
    public boolean importData(TransferSupport info) {
        if (!canImport(info)) {
            return false;
        }

        JList<Object> target = (JList<Object>) info.getComponent();
        JList.DropLocation dl = (JList.DropLocation) info.getDropLocation();
        DefaultListModel<Object> listModel = (DefaultListModel<Object>) target.getModel();
        int index = dl.getIndex();
        int max = listModel.getSize();

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

        addIndex = index;

        try {
            Object object = info.getTransferable().getTransferData(GenericTransferable.FLAVOR);
            listModel.add(index, object);
            target.addSelectionInterval(index, index);
            return moveAllowed = true;
        } catch (UnsupportedFlavorException | IOException e) {
            e.printStackTrace();
        }
        return false;
    }

    @Override
    protected void exportDone(JComponent c, Transferable data, int action) {
        if (moveAllowed)
            cleanup(c, action == MOVE, false);

    }

    private void cleanup(JComponent component, boolean remove, boolean bin) {
        if (remove && index != -1) {
            JList<T> source = (JList<T>) component;
            DefaultListModel<T> model = (DefaultListModel<T>) source.getModel();
            int removeAt = index > addIndex ? index + 1 : index;
            model.remove(bin ? removeAt - 1 : removeAt);
        }

        index = -1;
        addIndex = -1;
        moveAllowed = false;
    }

    private int index = -1;
    private int addIndex = -1;
    private boolean moveAllowed = false;
}


乙肝素

package org.dinhware.swing.child;

import org.dinhware.swing.special.HListItemTransferHandler;

import javax.swing.*;

/**
 * Created by: Niklas
 * Date: 20.10.2017
 * Alias: Dinh
 * Time: 19:57
 */

public class HBin<T> extends HImageLabel {
    public HBin(String text, Icon image, int distance) {
        super(text, image, distance);
        setTransferHandler(new HListItemTransferHandler<T>());
    }
}


以及可视化的工作方式,可悲的是,即使未拖动到HBin容器上,该容器也会始终消失。我一直认为它一直在工作,直到我不小心将它移出了Frame之外,但它仍然消失了。上面的代码仅允许在想要的列表内拖放。

java - JList上的Transferable和Transferhandler的自定义删除容器-LMLPHP

我的问题是如何添加功能,以使容器仅在拖动到HBin容器时才消失

我使用的代码的第一部分是

@Override
protected void exportDone(JComponent c, Transferable data, int action) {
    if (moveAllowed) cleanup(c, action == MOVE, false);
    else try {
        if (data.getTransferData(GenericTransferable.FLAVOR) instanceof RewardItem) {
            cleanup(c, true, true);
        }
    } catch (UnsupportedFlavorException | IOException e) {
        e.printStackTrace();
    }
}


我背后的逻辑是List和HBin共享一个可以比较的Type(RewardItem),后来我意识到(在对该方法进行了更通用的版本之后),数据将始终为RewardItem类型,并且始终会导致在清理电话中。这导致了我目前仍然面临的错误。

我今天早些时候采取的方法确实使我产生疑问,也使我撰写了这篇文章。我在TransferHandler中添加了一个布尔值,称为bin,默认情况下为false。在canImport签入importData之后,我添加了bin = info.getComponent() instanceof HBin,我认为应该可以。但是这个领域总是站不住脚的。我继续并为其添加了日志

System.out.println("IMPORT");
if (info.getComponent() instanceof HBin) {
    System.out.println("bin");
    return bin = true;
}


最终打印IMPORT,然后打印bin。在调用importData exportData之后,我在其中记录了bin的值,无论出于何种原因,现在都再次将其设为false。同时,moveAllowed字段似乎发生了变化。

这是我完整修改过的TransferHandler

package org.dinhware.swing.special;

import org.dinhware.swing.child.HBin;

import javax.swing.*;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;

/**
 * Created by: Niklas Date: 19.10.2017 Alias: Dinh Time: 18:54
 */

@SuppressWarnings("unchecked")
public class HListItemTransferHandler<T> extends TransferHandler {

    @Override
    protected Transferable createTransferable(JComponent component) {
        System.out.println("CREATE");
        JList<T> list = (JList<T>) component;
        index = list.getSelectedIndex();
        T transferredObject = list.getSelectedValue();
        return new GenericTransferable<>(transferredObject);
    }

    @Override
    public boolean canImport(TransferSupport info) {
        return info.isDataFlavorSupported(GenericTransferable.FLAVOR);
    }

    @Override
    public int getSourceActions(JComponent c) {
        System.out.println("ACTION");
        return MOVE;
    }

    @Override
    public boolean importData(TransferSupport info) {
        System.out.println("IMPORT");
        if (!canImport(info)) {
            return false;
        }
        if (info.getComponent() instanceof HBin) {
            System.out.println("bin");
            return bin = true;
        }

        JList<Object> target = (JList<Object>) info.getComponent();
        JList.DropLocation dl = (JList.DropLocation) info.getDropLocation();
        DefaultListModel<Object> listModel = (DefaultListModel<Object>) target.getModel();
        int index = dl.getIndex();
        int max = listModel.getSize();

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

        addIndex = index;

        try {
            Object object = info.getTransferable().getTransferData(GenericTransferable.FLAVOR);
            listModel.add(index, object);
            target.addSelectionInterval(index, index);
            return moveAllowed = true;
        } catch (UnsupportedFlavorException | IOException e) {
            e.printStackTrace();
        }
        return false;
    }

    @Override
    protected void exportDone(JComponent c, Transferable data, int action) {
        System.out.println("EXPORT " + moveAllowed + "/" + bin);
        if (moveAllowed)
            cleanup(c, action == MOVE, false);
        else
            cleanup(c, true, true);

    }

    private void cleanup(JComponent component, boolean remove, boolean bin) {
        System.out.println("CLEAN");
        if (remove && index != -1) {
            JList<T> source = (JList<T>) component;
            DefaultListModel<T> model = (DefaultListModel<T>) source.getModel();
            int removeAt = index > addIndex ? index + 1 : index;
            model.remove(bin ? removeAt - 1 : removeAt);
        }

        index = -1;
        addIndex = -1;
        moveAllowed = false;
    }

    private int index = -1;
    private int addIndex = -1;
    private boolean moveAllowed = false, bin = false;
}


在列表内移动时,一切正常(打印)

ACTION
CREATE
IMPORT
EXPORT true/false
CLEAN


但是当放到HBin容器上时,我无法解释发生了什么(打印)

ACTION
CREATE
IMPORT
bin
EXPORT false/false


我敢肯定,它应该为假/真

现在,我被困住了,无法使容器仅在放到HBin上时消失,而对于字段值明确记录为true时字段值没有改变感到困惑。

请帮忙...

最佳答案

Drag'n'Drop很复杂,并且至少有两种方法可以解决这一问题。

D'n'D围绕着将对象“包装”在“可转移”包装中的想法,可以通过多种不同方式(即DataFlavor)“导入”

因此,在此示例中,我仅专注于从JList删除项目,为此,我创建了一个Trash对象,该对象实际上维护了对要删除的项目的引用(我还创建了ListTrash演示至少一种可以传递更多信息的方式)

然后在TrashTransferable上发生拖动时,将该对象包装在JList

拥有Trash对象的主要原因是,它允许DataFlavor标准化。 “垃圾箱”仅关心Trash对象,没有其他问题。如果您有更多TransferHandler执行更多操作,则这尤其重要

我要做的另一件事是创建两个TransferHandler。一个用于“垃圾箱”,另一个用于JList,其主要原因是,它隔离了每个处理程序要执行的功能并降低了复杂性,因为您也不想确定哪个对象正在尝试执行哪个操作。

该示例还有另一个根本没有做很多事情的组件,因此它可以拒绝放置操作。

如果您还有其他使用TransferHandler的组件,则这些组件需要拒绝TrashTransferable.FLAVOR

import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.io.IOException;
import javax.swing.DefaultListModel;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.TransferHandler;
import javax.swing.TransferHandler.TransferSupport;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame("Test");
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        public TestPane() {
            setLayout(new GridBagLayout());
            DefaultListModel<String> model = new DefaultListModel<>();
            model.addElement("Cooks_Assistant");
            model.addElement("Romeo_and_Juliet");
            model.addElement("Sheep_Shearer");

            JList list = new JList(model);
            list.setTransferHandler(new HListItemTransferHandler());
            list.setDragEnabled(true);

            JLabel noDrop = new JLabel("No drop here", JLabel.CENTER);
            JLabel trash = new JLabel("All your trash belong to us", JLabel.CENTER);
            trash.setTransferHandler(new BinTransferHandler());

            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridx = 0;
            gbc.gridy = 0;
            gbc.weightx = 0.5;
            gbc.weighty = 1;
            gbc.fill = GridBagConstraints.BOTH;
            gbc.insets = new Insets(4, 4, 4, 4);

            add(new JScrollPane(list), gbc);

            gbc.gridx++;
            add(noDrop, gbc);

            gbc.gridx = 0;
            gbc.gridy++;
            gbc.gridwidth = GridBagConstraints.REMAINDER;
            add(trash, gbc);

        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(300, 300);
        }

    }

    public class BinTransferHandler extends TransferHandler {

        @Override
        public boolean canImport(TransferSupport info) {
            return info.isDataFlavorSupported(TrashTransferable.FLAVOR);
        }

        @Override
        public int getSourceActions(JComponent c) {
            System.out.println("ACTION");
            return DnDConstants.ACTION_MOVE;
        }

        @Override
        public boolean importData(TransferSupport support) {
            if (!canImport(support)) {
                return false;
            }
            // Check target component
            Transferable transferable = support.getTransferable();
            try {
                Trash trash = (Trash) transferable.getTransferData(TrashTransferable.FLAVOR);
                Object item = trash.getItem();
                System.out.println(">> Trash " + item);

                return true;
            } catch (UnsupportedFlavorException | IOException ex) {
                ex.printStackTrace();
            }
            return false;
        }

    }

    public class HListItemTransferHandler<T> extends TransferHandler {

        @Override
        protected Transferable createTransferable(JComponent component) {
            System.out.println("createTransferable");
            JList<T> list = (JList<T>) component;
            int index = list.getSelectedIndex();
            T transferredObject = list.getSelectedValue();
            return new TrashTransferable(new ListTrash<>(list, index, transferredObject));
        }

        @Override
        public boolean canImport(TransferSupport info) {
            return info.isDataFlavorSupported(TrashTransferable.FLAVOR);
        }

        @Override
        public int getSourceActions(JComponent c) {
            return DnDConstants.ACTION_MOVE;
        }

        @Override
        public boolean importData(TransferSupport info) {
            JList<Object> target = (JList<Object>) info.getComponent();
            JList.DropLocation dl = (JList.DropLocation) info.getDropLocation();
            DefaultListModel<Object> listModel = (DefaultListModel<Object>) target.getModel();
            int index = dl.getIndex();
            int max = listModel.getSize();

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

            try {
                Object object = info.getTransferable().getTransferData(DataFlavor.stringFlavor);
                listModel.add(index, object);
                target.addSelectionInterval(index, index);
                return true;
            } catch (UnsupportedFlavorException | IOException e) {
                e.printStackTrace();
            }
            return false;
        }

        @Override
        protected void exportDone(JComponent c, Transferable data, int action) {
            System.out.println("Export data");
            try {
                if (action != MOVE) {
                    return;
                }
                if (!(c instanceof JList)) {
                    return;
                }
                JList list = (JList) c;
                if (!(list.getModel() instanceof DefaultListModel)) {
                    return;
                }
                DefaultListModel model = (DefaultListModel) list.getModel();
                if (!(data instanceof TrashTransferable)) {
                    return;
                }
                Object transferData = data.getTransferData(TrashTransferable.FLAVOR);
                if (transferData == null || !(transferData instanceof Trash)) {
                    return;
                }
                Trash trash = (Trash) transferData;
                Object item = trash.item;
                int index = model.indexOf(item);
                if (index == -1) {
                    return;
                }
                model.remove(index);
            } catch (UnsupportedFlavorException | IOException ex) {
                ex.printStackTrace();
            }
        }
    }

    public static class ListTrash<T> extends Trash<T> {

        private JList list;
        private int index;

        public ListTrash(JList list, int index, T item) {
            super(item);
            this.list = list;
            this.index = index;
        }

        public JList getList() {
            return list;
        }

        public int getIndex() {
            return index;
        }

    }

    public static class Trash<T> {

        private T item;

        public Trash(T item) {
            this.item = item;
        }

        public T getItem() {
            return item;
        }

    }

    public static class TrashTransferable<T> implements Transferable {

        public static final DataFlavor FLAVOR = new DataFlavor(Trash.class, "Trash");

        private Trash<T> trash;

        TrashTransferable(Trash<T> object) {
            trash = object;
        }

        @Override
        public DataFlavor[] getTransferDataFlavors() {
            return new DataFlavor[]{FLAVOR};
        }

        @Override
        public boolean isDataFlavorSupported(DataFlavor flavor) {
            return flavor.equals(flavor);
        }

        @Override
        public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
            return trash;
        }
    }
}

09-15 16:18