如何将 JLabel 中的图标(拖放)移动到另一个 JLabel 而不是复制它?
Posted
技术标签:
【中文标题】如何将 JLabel 中的图标(拖放)移动到另一个 JLabel 而不是复制它?【英文标题】:How do I move an icon (drag & drop) inside a JLabel to another JLabel instead of copying it? 【发布时间】:2020-01-15 09:37:57 【问题描述】:我正在为一个国际象棋游戏做一个学校项目,我目前被困在棋子的 DnD 操作中。
在代码中,我在 exportAsDrag() 中传递了 TransferHandler.MOVE 参数,使其成为 MOVE 操作。但是,当从 JLabels 拖放图标时,TransferHandler 的行为仍然是 COPY 而不是 MOVE。
我尝试在 TransferHandler 匿名类的 exportDone() 中将源 JLabel 的图标设置为 null,但如果 DnD 操作的源和目标相同,该图标将消失。如果有更多我应该覆盖/添加的方法或任何其他方式来完成相同的事情,请告诉我。
MouseListener listener = new MouseAdapter()
@Override
public void mousePressed(MouseEvent e)
ChessTiles c = (ChessTiles) e.getSource();
TransferHandler handler = c.getTransferHandler();
handler.exportAsDrag(c, e, TransferHandler.MOVE)
;
private static TransferHandler handler = new TransferHandler("icon")
@Override
public int getSourceActions (JComponent c)
return MOVE;
;
tileArray[x][y].addMouseListener(listener);
tileArray[x][y].setTransferHandler(handler);
【问题讨论】:
【参考方案1】:考虑documentation of TransferHandler.exportDone
:
数据导出后调用。如果操作为
MOVE
,则此方法应删除传输的数据。
这应该回答这两个问题。首先,您确实有责任实现移动语义,其次,您应该只在action
的值为MOVE
时执行此操作。除了不适用于您的场景的其他传输类型的可能性,因为您不支持它们,它可能会以零操作调用,以允许在中止传输后进行清理。当不满足先决条件时,这甚至可能在 exportAsDrag
方法中发生。
如果您不想支持拖动到自身上,您可以暂时禁用放置目标,使用exportDone
方法重置属性。
例如
public class DragAndDropExample
public static void main(String[] args)
EventQueue.invokeLater(DragAndDropExample::init);
private static void init()
try
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
catch(ReflectiveOperationException|UnsupportedLookAndFeelException ex)
try
BufferedImage img = ImageIO.read(
new URL("https://cdn.sstatic.net/img/favicons-sprite32.png"));
img = img.getSubimage(0, 11844, 32, 32);
ICON = new ImageIcon(img);
catch(IOException ex)
ICON = UIManager.getIcon("OptionPane.errorIcon");
JFrame frame = new JFrame("Test");
Container c = frame.getContentPane();
final int gridWidth = 4, gridHeight = 4;
c.setLayout(new GridLayout(gridHeight, gridWidth, 4, 4));
for(int y = 0; y < gridHeight; y++)
for(int x = 0; x < gridWidth; x++)
create(x, y, c);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
static Icon ICON;
static final MouseAdapter DRAG_INIT = new MouseAdapter()
@Override public void mousePressed(MouseEvent e)
var c = (JComponent) e.getSource();
var handler = c.getTransferHandler();
handler.exportAsDrag(c, e, TransferHandler.MOVE);
;
static final TransferHandler ICON_TRANSFER = new TransferHandler( "icon" )
@Override public void exportAsDrag(JComponent comp, InputEvent e, int action)
comp.getDropTarget().setActive(false);
super.exportAsDrag(comp, e, action);
@Override public int getSourceActions(JComponent c)
return MOVE;
@Override protected void exportDone(
JComponent source, Transferable data, int action)
source.getDropTarget().setActive(true);
if (action == MOVE)
((JLabel)source).setIcon(null);
;
private static void create(int x, int y, Container c)
JLabel l = new JLabel("\u00a0");
if(x == 0 && y == 0) l.setIcon(ICON);
l.setBorder(BorderFactory.createLineBorder(Color.lightGray, 1));
l.setTransferHandler(ICON_TRANSFER);
l.addMouseListener(DRAG_INIT);
c.add(l);
如果你不想禁用它,你可以存储组件,检查源和目标是否相同,如this answer,但你应该在exportDone
方法中将记住的组件设置为null
, 以确保没有内存泄漏。
【讨论】:
(1+) 我知道必须有更好的方法来防止组件自行掉落。使用setActive(...)
方法对我来说比跨方法跟踪源/目标组件更有意义。【参考方案2】:
我尝试在 TransferHandler 匿名类的 exportDone() 中将源 JLabel 的图标设置为 null,但如果 DnD 操作的源和目标相同,该图标将消失。
是的,需要这样做。
此外,通过覆盖importData(...)
方法,您可以保存“目标”组件,以便检查源/目标是否是相同的组件:
TransferHandler iconHandler = new TransferHandler( "icon" )
Component target;
@Override
public int getSourceActions(JComponent c)
return MOVE;
@Override
public boolean importData(TransferSupport info)
target = info.getComponent();
return super.importData( info );
@Override
protected void exportDone(JComponent source, Transferable data, int action)
if (action == MOVE
&& source != target)
((JLabel)source).setIcon(null);
;
【讨论】:
【参考方案3】:有人建议我像这样重写 TransferHandler 的 exportDone(...) 函数,
@Override
protected void exportDone(JComponent source, Transferable data, int action)
if (action == MOVE)
((JLabel)source).setIcon(null);
//((JLabel)source).setIcon(null);
如果没有 if 语句,如果我将图标设置为 null,则无论 importData(...) 返回的布尔值如何,图标都会消失。有了它,如果 importData(...) 的返回值为 false,图标将保持不变。那么可以假设在调用 importData(...) 之后只调用 exportDone(...) 吗?
它可以工作,但现在我很好奇 TransferHandler 内部函数调用的顺序,在调用 handler.exportAsDrag(...) 之后。
【讨论】:
它可以工作,但现在 - 我想我提出了这个建议,然后删除了它,因为它不能正常工作。我发现如果我单击并释放鼠标而不拖动到另一个组件,则图标被删除。我提供了一个新建议,适用于我的简单测试。 既然要检查的非法动作不止一个,那么让 exportDone(...) 实现保持这样并检查 importData(...) 中的动作不是更好吗?通过使用 legalMove 标志来决定在 importData(...) 函数中是返回 super.importData(...) 还是返回 false(如果标志为 false)。非常感谢您解决了我的困惑,伙计。 因为有不止一个非法动作要检查 - 这是一个更复杂的问题,因为 TransferHandler 现在需要知道你的应用程序的逻辑和你的当前状态应用。在这种情况下,我建议您覆盖 canImport(...) 方法。当您将图标从一个标签拖到另一个标签时,拖动的图标将保持为“有一条线穿过它的圆圈”,直到您将鼠标悬停在可以接受传输的标签上,此时它会变为“移动”图标。跨度>以上是关于如何将 JLabel 中的图标(拖放)移动到另一个 JLabel 而不是复制它?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 dragover/dragenter HTML 5 拖放期间更改图标
如何将图标从 JLabel 转换为 BufferedImage?