使用秋千渲染放置位置
Posted
技术标签:
【中文标题】使用秋千渲染放置位置【英文标题】:Drop location rendering with swing 【发布时间】:2014-09-02 22:37:19 【问题描述】:我想为组件(例如 JPanel 或 JLabel)实现自定义放置位置渲染。我的目标是当用户悬停带有可放置项目的组件时显示蓝色边框,并在离开组件时再次移除边框。
摇摆教程提供了这个:Drop Location Rendering
这对我一点帮助都没有。我错过了enterComponent(Event e)
和exitComponent(Event e)
之类的内容,或者此页面所指的内容与我预期的不同?
所以我搜索了万维网并找到了一些示例(主要来自this blog)。过了一会儿,我可以完成我需要的工作示例(来源如下)。但是我发现所有示例都使用了 java.awt 中的类来呈现放置位置。因为我还没有完全了解所有这些拖放类,所以我没有找到一种方法来仅使用 swing 类来实现自定义放置位置渲染。
所以我的问题是:是否可以仅使用 swing 类在 "enterComponent"
和 "exitComponent"
事件上呈现 JComponent?
补充问题:
如果我删除new DropTarget(label, dropListener);
行,ListItemTransfereHandler.importData(...)
将被调用,否则不会被调用。这是什么原因?我真的很感激一些关于 dnd 课程的课程和/或行动图表(这不是答案所必需的)。
(请不要回答 JavaFX)
工作示例的源代码(带有 AWT 类)。很抱歉代码行太多,但是 java dnd 需要很大的空间。
public class DnDTransferableTest
public static void main(String[] args)
EventQueue.invokeLater(new Runnable()
@Override
public void run()
new DnDTransferableTest();
);
public DnDTransferableTest()
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300, 300);
frame.setLocationRelativeTo(null);
// JPanel panel = new JPanel(new MigLayout("wrap 2, fill", "fill, grow", "align center"));
JPanel panel = new JPanel(new GridLayout(1,2));
JList<ListItem> list = new JList<ListItem>();
JLabel label = new JLabel();
// Initializing list
list.setDragEnabled(true);
list.setTransferHandler(new ListTransferHandler());
DefaultListModel<ListItem> model = new DefaultListModel<ListItem>();
for (int index = 0; index < 10; index++)
model.addElement(new ListItem("Item", index));
list.setModel(model);
// Initializing label and its drop listener
label.setText("Drag on me...");
label.setTransferHandler(new ListTransferHandler());
DropTargetListener dropListener = new JLabelDropTargetListener();
new DropTarget(label, dropListener);
// Adding the components to the panel
panel.add(new JScrollPane(list), "sg test");
panel.add(label, "sg test");
frame.setContentPane(panel);
frame.setVisible(true);
class ListItem implements Transferable
public static final DataFlavor LIST_ITEM_DATA_FLAVOR = new DataFlavor(ListItem.class, ListItem.class.getName());
private String text;
private int number;
public ListItem(String text, int number)
this.text = text;
this.number = number;
public String getText()
return text;
public int getNumber()
return this.number;
@Override
public String toString()
return this.getText() + ": " + this.getNumber();
@Override
public DataFlavor[] getTransferDataFlavors()
return new DataFlavor[] LIST_ITEM_DATA_FLAVOR ;
@Override
public boolean isDataFlavorSupported(DataFlavor flavor)
return flavor.equals(LIST_ITEM_DATA_FLAVOR);
@Override
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException
return this;
class ListTransferHandler extends TransferHandler
private static final long serialVersionUID = 1L;
@Override
public boolean canImport(TransferSupport support)
return (support.getComponent() instanceof JLabel) && support.isDataFlavorSupported(ListItem.LIST_ITEM_DATA_FLAVOR);
@Override
public boolean importData(TransferSupport support)
boolean accept = false;
if (canImport(support))
try
Transferable t = support.getTransferable();
Object value = t.getTransferData(ListItem.LIST_ITEM_DATA_FLAVOR);
if (value instanceof ListItem)
Component component = support.getComponent();
if (component instanceof JLabel)
((JLabel) component).setText(((ListItem) value).getNumber() + ". " + ((ListItem) value).getText());
catch (Exception exp)
exp.printStackTrace();
System.out.println("import data " + accept);
return accept;
@Override
public int getSourceActions(JComponent c)
return COPY;
@Override
protected Transferable createTransferable(JComponent c)
if (c instanceof JList<?>)
JList<?> list = (JList<?>) c;
Object value = list.getSelectedValue();
if (value instanceof ListItem)
return (ListItem) value;
return null;
@Override
protected void exportDone(JComponent source, Transferable data, int action)
System.out.println("ExportDone");
// Here you need to decide how to handle the completion of the
// transfer,
// should you remove the item from the list or not...
class JLabelDropTargetListener implements DropTargetListener
private int thickness = 2;
private Border blueBorder = BorderFactory.createLineBorder(Color.BLUE, thickness);
private Border emptyBorder = BorderFactory.createEmptyBorder(thickness, thickness, thickness, thickness);
@Override
public void dragEnter(DropTargetDragEvent dtde)
System.out.println("dragEnter");
Object src = dtde.getDropTargetContext().getComponent();
if (src instanceof JLabel)
JLabel label = (JLabel) src;
label.setForeground(Color.RED);
label.setBorder(blueBorder);
else
System.out.println(src.getClass().getName());
System.out.println(dtde.getDropTargetContext().getComponent());
@Override
public void dragOver(DropTargetDragEvent dtde)
@Override
public void dropActionChanged(DropTargetDragEvent dtde)
@Override
public void dragExit(DropTargetEvent dte)
System.out.println("dragExit");
Component src = dte.getDropTargetContext().getComponent();
if (src instanceof JLabel)
JLabel label = (JLabel) src;
label.setForeground(null);
label.setBorder(emptyBorder);
@Override
public void drop(DropTargetDropEvent dtde)
System.out.println("drop");
Component src = dtde.getDropTargetContext().getComponent();
if (src instanceof JLabel)
JLabel label = (JLabel) src;
label.setForeground(null);
label.setBorder(emptyBorder);
Transferable t = dtde.getTransferable();
Object value;
try
value = t.getTransferData(ListItem.LIST_ITEM_DATA_FLAVOR);
if (value instanceof ListItem)
ListItem li = (ListItem) value;
label.setText(li.getNumber() + ". " + li.getText());
catch (UnsupportedFlavorException e)
e.printStackTrace();
catch (IOException e)
e.printStackTrace();
【问题讨论】:
【参考方案1】:我深入查看了swing的TransferHandler
的源代码,发现JComponent
的setDropLocation(TransferHandler.DropLocation location, Object state, boolean forDrop)
TransferHandler
在每个 DragEvent 上调用它(TransferHandler 有自己的私有 DropTargetListener 类)。首先我想在我自己的 JLabel 扩展类中覆盖它,但后来发现它是受保护的。
有了这些新信息,我偶然发现了这两个帖子:
http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6448332http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6500024
所以答案是:这是一个错误,或者更确切地说是一个尚未实现的功能(我不知道它是否在 java 8 中可用,但我认为 JavaFX 比 Swing 做得更好。不幸的是,切换到 JavaFX 有点现在很晚了)。
【讨论】:
【参考方案2】:我能够弄清楚如何做到这一点。 根据您的代码:
DropTarget dropTarget = label.getDropTarget();
try
dropTarget.addDropTargetListener(dropListener);
catch (TooManyListenersException e)
e.printStackTrace();
像往常一样设置传输处理程序。这将处理实际的拖放功能,而您的拖放侦听器处理标签的绘制。 不要创建自己的放置目标对象。
【讨论】:
以上是关于使用秋千渲染放置位置的主要内容,如果未能解决你的问题,请参考以下文章