java/swing:两个子窗口的拖放问题

Posted

技术标签:

【中文标题】java/swing:两个子窗口的拖放问题【英文标题】:java/swing: drag and drop problems with two child windows 【发布时间】:2011-01-14 20:39:36 【问题描述】:

我有两个子窗口,我想以两种不同的方式接受拖放对象的拖放。

一个是 JPanel,我想接受一个文件列表。它工作正常。

另一个是我想接受字符串列表的 JTable。如果我不启用 JPanel 作为放置目标,它可以正常工作。

当我尝试同时启用两者时,JPanel 放置目标似乎掩盖了 JTable 放置目标,即使 JTable 不是 JPanel 的子窗口并且两个组件位于不重叠的区域中。

同时包含它们的 JFrame 似乎也有下拉图标...不知道为什么。

有什么建议吗?这很难调试。


下面是类似的示例应用程序,这里我根本无法让 JTable 接受丢弃。 :/

我想了解另一个问题:如果我从“拖动源 1”拖动到“放置目标 1”但不释放鼠标,则每次拖动源 1 标签更改时我的光标都会闪烁(每秒一次在这个测试应用程序中)。 为什么要这样做?有没有办法阻止它闪烁?

package com.example.test.gui;

import java.awt.Component;
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.DragGestureRecognizer;
import java.awt.dnd.DragSource;
import java.awt.dnd.DragSourceDragEvent;
import java.awt.dnd.DragSourceDropEvent;
import java.awt.dnd.DragSourceEvent;
import java.awt.dnd.DragSourceListener;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.event.TableModelListener;
import javax.swing.table.TableModel;

public class DragAndDropQuestion 
    public enum Color  
        RED, ORANGE, YELLOW, GREEN, BLUE, VIOLET, WHITE, BLACK;
        @Override public String toString() 
             return this.name().toLowerCase(); 
        static public Color randomColor(Random r)
        
            Color[] colors = Color.values();
            return colors[r.nextInt(colors.length)];
        
    
    public static class SampleBean
    
        final private Color color;
        final private char letter;
        final static public DataFlavor dataFlavor = 
            new DataFlavor(SampleBean.class, "SampleBean");
        public Color getColor()  return this.color; 
        public char getLetter()  return this.letter; 
        private SampleBean(Color color, char letter)
        
            this.color = color;
            this.letter = letter;
        
        static public SampleBean randomBean(Random r)
        
            String letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
            return new SampleBean(Color.randomColor(r),
                    letters.charAt(r.nextInt(letters.length())));
        
        @Override public String toString() 
            return this.color.toString()+" "+this.letter; 
        
    
    public static class BorderPanel extends JPanel 
        public BorderPanel(String title)
        
            setBorder(BorderFactory.createTitledBorder(title));
        
    
    public static class BeanTransferable implements Transferable
    
        final private SampleBean value;
        static final private List<DataFlavor> flavors = Arrays.asList(
                DataFlavor.stringFlavor,
                SampleBean.dataFlavor
            );
        public BeanTransferable(SampleBean x)  this.value=x; 
        @Override public Object getTransferData(DataFlavor flavor) 
        throws UnsupportedFlavorException, IOException 
            if (flavors.get(0).equals(flavor))
            
                return this.value.toString(); 
            
            else if (flavors.get(1).equals(flavor))
            
                return this.value;
            
            else
            
                throw new UnsupportedFlavorException(flavor);
            
        

        @Override public DataFlavor[] getTransferDataFlavors() 
            return flavors.toArray(new DataFlavor[0]);
        
        @Override public boolean isDataFlavorSupported(DataFlavor flavor) 
            return flavors.contains(flavor);
        
    
    public abstract static class SimpleDropTarget 
       implements DropTargetListener
    
        final private String debugString;
        public SimpleDropTarget(String debugString) 
            this.debugString=debugString; 
        
        @Override public void dragEnter(DropTargetDragEvent event) 
             System.out.println(this.debugString+":dragEnter"); 
        @Override public void dragExit(DropTargetEvent event) 
             System.out.println(this.debugString+":dragExit"); 
        @Override public void dragOver(DropTargetDragEvent event)     
        @Override public void dropActionChanged(DropTargetDragEvent event) 
        public void install(JComponent component) 
            new DropTarget(component, this);
        
    
    public abstract static class SimpleDragSource 
        implements DragSourceListener, DragGestureListener
    
        final private String debugString;
        final private DragSource ds = new DragSource();
        public SimpleDragSource(String debugString)  
            this.debugString=debugString; 
        
        @Override public void dragDropEnd(DragSourceDropEvent event) 
        @Override public void dragEnter(DragSourceDragEvent event) 
         System.out.println(this.debugString+":dragEnter"); 
        @Override public void dragExit(DragSourceEvent event) 
         System.out.println(this.debugString+":dragExit"); 
        @Override public void dragOver(DragSourceDragEvent event) 
        @Override public void dropActionChanged(DragSourceDragEvent event)    
        public void install(JComponent component) 
            DragGestureRecognizer dgr = 
                this.ds.createDefaultDragGestureRecognizer(component,
                        DnDConstants.ACTION_COPY, this);
        
        abstract public Transferable prepareTransferable();
        @Override public void dragGestureRecognized(DragGestureEvent dge)
        
            this.ds.startDrag(dge,
                    DragSource.DefaultCopyDrop, prepareTransferable(), this);
        
    
    public static class BeanListModel implements TableModel 
        final private List<SampleBean> list = new ArrayList<SampleBean>();
        enum ColumnType 
            COLOR(Color.class, "color") 
                @Override public Color extractValue(SampleBean sampleBean) 
                    return sampleBean.getColor();
                
            ,
            LETTER(String.class, "letter") 
                @Override public String extractValue(SampleBean sampleBean) 
                    return Character.toString(sampleBean.getLetter());
                
            ,
            ;
            final private Class<?> cl;
            final private String name;
            public Class<?> getColumnClass()  return this.cl; 
            public String getColumnName()  return this.name; 
            ColumnType(Class<?> cl, String name) 
             this.cl=cl; this.name=name; 
            abstract public Object extractValue(SampleBean sampleBean);
        
        final static private ColumnType[] columns 
            = new ColumnType[]ColumnType.COLOR, ColumnType.LETTER;

        @Override 
            public void addTableModelListener(TableModelListener arg0) 
        @Override 
            public void removeTableModelListener(TableModelListener arg0) 

        @Override public Class<?> getColumnClass(int column) 
            return columns[column].getColumnClass();
        
        @Override public int getColumnCount()  
            return columns.length; 
        
        @Override public String getColumnName(int column) 
            return columns[column].getColumnName();
        
        @Override public int getRowCount()  return list.size(); 
        @Override public Object getValueAt(int row, int column) 
            return columns[column].extractValue(list.get(row));
        

        @Override public boolean isCellEditable(int row, int column) 
            return false;
        

        @Override public void setValueAt(Object obj, int row, int column) 
            throw new UnsupportedOperationException();
               

        public void addBean(SampleBean bean)
        
            this.list.add(bean);
        
    
    public static class BeanTablePanel extends BorderPanel 
        final private JTable table;
        final private BeanListModel tableModel;

        public BeanTablePanel(String title)
        
            super(title);
            this.table = new JTable();
            this.tableModel = new BeanListModel();
            this.table.setModel(this.tableModel);
            add(new JScrollPane(this.table));
            SimpleDropTarget dt = new SimpleDropTarget("BeanTable")
                @Override public void drop(DropTargetDropEvent dtde) 
                    Transferable tr = dtde.getTransferable();
                    if (tr.isDataFlavorSupported(SampleBean.dataFlavor))
                    
                        try 
                            SampleBean b = (SampleBean) 
                                tr.getTransferData(SampleBean.dataFlavor);
                            addBean(b);
                        
                        catch (UnsupportedFlavorException e) 
                            e.printStackTrace();
                        
                        catch (IOException e) 
                            e.printStackTrace();
                        
                    
                
            ;
            dt.install(this.table);
            // !!! This doesn't seem to work...
        
        void addBean(SampleBean b) 
            this.tableModel.addBean(b);         
        
    
    public static class BeanLabelPanel extends BorderPanel 
        final private JLabel label;
        public BeanLabelPanel(String title)
        
            super(title);
            this.label = new JLabel("drop item here");
            add(this.label);
            SimpleDropTarget dt = new SimpleDropTarget("BeanLabel")
                @Override public void drop(DropTargetDropEvent dtde) 
                    Transferable tr = dtde.getTransferable();
                    if (tr.isDataFlavorSupported(DataFlavor.stringFlavor))
                    
                        try 
                            String s = (String) 
                                tr.getTransferData(DataFlavor.stringFlavor);
                            setLabel(s);
                        
                        catch (UnsupportedFlavorException e) 
                            e.printStackTrace();
                        
                        catch (IOException e) 
                            e.printStackTrace();
                        
                    
                
            ;
            dt.install(this.label);
               
        void setLabel(String s)  this.label.setText(s); 
    
    public static class BeanSourcePanel extends BorderPanel 
        final private JLabel label;
        final private Random randomizer;
        private SampleBean x;

        public BeanSourcePanel(
         String title, ScheduledExecutorService scheduler)
        
            super(title);
            this.label = new JLabel(" ");
            this.randomizer = new Random();
            add(this.label);

            scheduler.scheduleAtFixedRate(new Runnable()
             public void run()  changeBean();  , 
            0, 1000, TimeUnit.MILLISECONDS);

            (new SimpleDragSource("RandomBean")
                @Override public Transferable prepareTransferable() 
                    return new BeanTransferable(getBean());
                
            ).install(this.label);
               
        public SampleBean getBean()  return this.x; 
        void changeBean()
        
            this.x = SampleBean.randomBean(this.randomizer);
            this.label.setText(this.x.toString());
               
    

    public static class DNDQFrame extends JFrame
    
        public DNDQFrame(String title, ScheduledExecutorService scheduler)
        
            setTitle(title);
            getContentPane().setLayout(
                    new BoxLayout(getContentPane(), BoxLayout.PAGE_AXIS)
            );
            add(new JLabel("Drag and Drop Question"));
            add(new BeanSourcePanel("drag source 1", scheduler));
            add(new BeanLabelPanel("drop target 1"));
            add(new BeanTablePanel("drop target 2"));
        
        @Override public Component add(Component component)
        
            if (component instanceof JComponent)
                ((JComponent) component).setAlignmentX(0.0f);
            return super.add(component);            
               
    

    static public void main(String[] args)
    
        ScheduledExecutorService scheduler = 
            new ScheduledThreadPoolExecutor(1);
        DNDQFrame frame = new DNDQFrame("DragAndDropQuestion", scheduler);
        frame.pack();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    

【问题讨论】:

你能发布一段简化的代码吗?即使它是您的 DnD 代码的摘录,也会有所帮助。如果代码很多,请使用 pastebin。使用 UI 代码,如果我可以构建并运行它,我可以更快地调试它。 现在正在处理它......(顺便说一句,什么是 pastebin?) 一个网站,您可以在其中粘贴较大的代码摘录,它们会为您提供一个 URL - 例如 mysticpaste.com/new 或 pastebin.com。这样,您的问题在没有 2 页内联代码的情况下仍然可以阅读。 【参考方案1】:

改为将放置侦听器添加到 JScrollPane。您已经嵌套了表格,因此它没有接收事件。此外,在您的 addBean() 方法中,您应该添加一个 table.revalidate() 否则它不会在您的 tablemodel 中显示更新的数据。

【讨论】:

另外,我没有体验到您所看到的闪烁。 谢谢。我改用了包含表格 + 滚动窗格的 JPanel,也从实现 TableModel 切换到扩展 AbstractTableModel 并使用对 fireTableRowsInserted() 的调用。

以上是关于java/swing:两个子窗口的拖放问题的主要内容,如果未能解决你的问题,请参考以下文章

WPF中的拖放文件不起作用

Qt4是不是实现了窗口图标的拖放?

具有子/父功能的拖放列表

C++ GUI 中的拖放事件 (WM_DROPFILES)

不带“file://”的拖放 URL

上传文件时检测到取消的拖放操作