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

标签 java swing jlist transferable

过去一周我试图解决这个问题,但不知何故我似乎找不到解决方案。关于这个主题的信息并不多,因此很难找到示例或代码来查看。

我这里有一个 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;
    }
}

传输处理程序:

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;
}

HBin

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 容器上,容器也总是消失。我以为它一直在工作,直到我不小心将它移到我的框架之外,它仍然消失了。上面的代码只允许在预期的列表内拖/放。

enter image description here

我的问题是如何添加功能以正确地使容器仅在拖动到 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 共享相同的类型(RewardItem),我可以比较它,后来我意识到(在制作该方法的更通用版本之后)数据将始终是 RewardItem 类型,并且总是会导致清理调用。这导致了我目前仍然面临的错误。

我今天早些时候采取的方法确实让我质疑自己的想法,也让我写了这篇文章。我向 TransferHandler 添加了一个名为 bin 的 boolean 值,默认情况下为 false。在 importData 中进行 canImport 检查后,我添加了 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 时未更改感到困惑。

请..帮助...

最佳答案

拖放很复杂,而且至少有两种方法可以做到这一点,这也无济于事。

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

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

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

拥有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;
        }
    }
}

关于java - JList 上 Transferable 和 Transferhandler 的自定义删除容器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47253790/

相关文章:

java - gui 中重画的问题

java - Java 中使用 jnlp.jar 进行文件关联,没有完整的 JWS?

java - 用Java创建一个漂亮的 ListView

java - JList 中单行中的多种字体颜色

java.math.BigInteger pow(exponent) 问题

java - org.springframework.web.servlet.DispatcherServlet noHandlerFound

java - 使用 JavaScript 更改 JSP 自定义标记的 JSP 自定义属性值

Java如何处理一个类型的对象

java - 限制 JScrollPane 仅显示其部分内容

java - 如何设置优先鼠标监听器