提问者:小点点

摆动模糊拖动图像


我有一个简单的任务来实现,它工作得很好,但是我面临着一个非常棘手的问题,关于在Swing中自定义拖动图像。任务背后的想法只是允许用户在列表组件和文本组件之间执行一些DND,但是在拖动操作期间,在鼠标后面显示与列表内渲染器完全相同的绘图。

为此,我使用列表中选定元素的单元格渲染器,并将其绘制在临时图像上。然后将此图像发送到TransferHandler,一切都很好。当我修改整体组件的大小并使其更大时,问题就很明显了。在一定程度上,绘制的图片不再显示正确,而是应用了一些渐变,使内容难以阅读。以下是重现问题的代码片段:

import java.awt.Color;
import java.awt.Component;
import java.awt.GridLayout;

import javax.swing.DefaultListCellRenderer;
import javax.swing.DefaultListModel;
import javax.swing.DropMode;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;

public class BasicTextListDND {

    private JList<String> makeList() {
        DefaultListModel<String> m = new DefaultListModel<String>();
        for(int i = 0; i<10; i++) {
            m.addElement("Element "+i);            
        }
        JList<String> list = new JList<String>(m);

        list.setTransferHandler(new BasicListTransferHandler());
        list.setDropMode(DropMode.ON_OR_INSERT);
        list.setDragEnabled(true);
        list.setCellRenderer(new DefaultListCellRenderer() {

            /**
             * Comment for <code>serialVersionUID</code>
             */
            private static final long serialVersionUID = 1L;

            /** {@inheritDoc} */
            public Component getListCellRendererComponent(JList<?> list, Object value, int index,
                    boolean isSelected, boolean cellHasFocus) {
                Component listCellRendererComponent = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
                if (cellHasFocus == false && isSelected == false) { 
                    if (index % 2 == 0) {
                        listCellRendererComponent.setBackground(Color.RED);
                    } else if (index % 3==0) {
                        listCellRendererComponent.setBackground(Color.GREEN);
                    } else {
                        listCellRendererComponent.setBackground(Color.BLUE);
                    }
                }
                return listCellRendererComponent;
            }

        });
        return list;
    }

    private JTextArea makeTextArea() {
        JTextArea textArea = new JTextArea("Drag here from JList!");
        return textArea;
    }

    public JComponent makeUI() {
        JPanel panel = new JPanel(new GridLayout(2,1));
        panel.add(new JScrollPane(makeTextArea()));
        panel.add(new JScrollPane(makeList()));
        return panel;
    }

    private static void createAndShowGUI() {
        JFrame f = new JFrame("BasicDnD");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        BasicTextListDND app = new BasicTextListDND();
        JComponent appContent = app.makeUI();
        f.setContentPane(appContent);
        f.setSize(600, 320);
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override 
            public void run() { 
                createAndShowGUI(); 
            }
        });
    }
}


/**
 * 
 */
public class BasicListTransferHandler extends TransferHandler {

    /**
     * Comment for <code>serialVersionUID</code>
     */
    private static final long serialVersionUID = 1L;

    @Override 
    public boolean canImport(TransferHandler.TransferSupport info) {
        if (!info.isDataFlavorSupported(DataFlavor.stringFlavor)) {
            return false;
        }
        JList.DropLocation dl = (JList.DropLocation)info.getDropLocation();
        if (dl.getIndex() == -1) {
            return false;
        }
        return true;
    }

    @Override 
    public int getSourceActions(JComponent c) {
        BufferedImage dragImage = makeImageFromString(c);
        if (dragImage != null) {
            setDragImage(dragImage);            
            Point mousePosition = c.getMousePosition();
            if (mousePosition != null) {
                setDragImageOffset(mousePosition);
            }
        }
        return COPY;
    }

    private final JPanel tempDrawPanel = new JPanel();

    private BufferedImage createDragImage(JList<String> list) {
        int width = 0;
        int height = 0;
        int[] selectedIndices = list.getSelectedIndices();
        for(int i =0; i<selectedIndices.length; i++){
            int idx = selectedIndices[i];
            Rectangle cellBounds = list.getCellBounds(idx, idx);
            height += cellBounds.height;
            width = Math.max(width, cellBounds.width); // we want to create a drag image as big as the largest cell
        }
        BufferedImage br = null;
        if (width > 0 && height > 0) {
            br = list.getGraphicsConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);
        }
        return br;
    }

    private BufferedImage makeImageFromString(JComponent src) {
        JList<String> sourceList = (JList<String>)src;
        BufferedImage  br = createDragImage(sourceList);
        if (br != null) {
            int[] selectedIndices = sourceList.getSelectedIndices();            
            int yD = 0;
            Graphics g = br.getGraphics();
            try{
                for(int idx: selectedIndices) {
                    ListCellRenderer<? super String> cellRenderer = sourceList.getCellRenderer();
                    String valueAt = sourceList.getModel().getElementAt(idx);
                    Component c = cellRenderer.getListCellRendererComponent(sourceList, valueAt, idx, false, false);
                    Rectangle itemCellBounds = sourceList.getCellBounds(idx, idx);                
                    SwingUtilities.paintComponent(g, c, tempDrawPanel, itemCellBounds.x, yD, itemCellBounds.width, itemCellBounds.height);
                    yD = itemCellBounds.y+itemCellBounds.height;
                }
            }finally {
                g.dispose();
            }
            br.coerceData(true);
        }
        return br;
    }

    @Override 
    protected Transferable createTransferable(JComponent c) {
        JList<String> list = (JList<String>)c;
        List<String> selectedValuesList = list.getSelectedValuesList();                
        StringBuffer buff = new StringBuffer();
        for (int i = 0; i < selectedValuesList.size(); i++) {
            String val = selectedValuesList.get(i);
            buff.append(val == null ? "" : val.toString());
            if (i != selectedValuesList.size()- 1) {
                buff.append("\n");
            }
        }
        return new StringSelection(buff.toString());
    }
}

问题在于,我认为,在makeImageFromString方法的某个地方,但经过2天的Swing/AWT库挖掘并了解拖动图像是如何绘制的,我仍然无法解决这个问题。底线问题:如果拖动图像超过一定大小,AWT中是否有任何模糊的逻辑应用此渐变?

任何帮助都将不胜感激!马吕斯。


共1个答案

匿名用户

如何翻译图形上下文的起源:

//SwingUtilities.paintComponent(g, c, tempDrawPanel, itemCellBounds.x, yD, itemCellBounds.width, itemCellBounds.height);
//yD = itemCellBounds.y+itemCellBounds.height;
SwingUtilities.paintComponent(g, c, tempDrawPanel, 0, 0, itemCellBounds.width, itemCellBounds.height);
g.translate(0, itemCellBounds.height);

编辑:

@user3619696:我误会了。

我猜“模糊拖动图像”的不透明度取决于Windows桌面主题。因此尝试使用半透明的JWindow而不是TransferHandler#setDragImage(…)

import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.dnd.*;
import java.awt.image.*;
import java.util.List;
import javax.swing.*;

public class BasicTextListDND2 {

    private JList<String> makeList() {
        DefaultListModel<String> m = new DefaultListModel<String>();
        for(int i = 0; i<10; i++) {
            m.addElement("Element "+i);
        }
        JList<String> list = new JList<String>(m);

        list.setTransferHandler(new BasicListTransferHandler());
        list.setDropMode(DropMode.ON_OR_INSERT);
        list.setDragEnabled(true);
        list.setCellRenderer(new DefaultListCellRenderer() {

            /**
             * Comment for <code>serialVersionUID</code>
             */
            private static final long serialVersionUID = 1L;

            /** {@inheritDoc} */
            public Component getListCellRendererComponent(JList<?> list, Object value, int index,
                    boolean isSelected, boolean cellHasFocus) {
                Component listCellRendererComponent = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
                if (cellHasFocus == false && isSelected == false) { 
                    if (index % 2 == 0) {
                        listCellRendererComponent.setBackground(Color.RED);
                    } else if (index % 3==0) {
                        listCellRendererComponent.setBackground(Color.GREEN);
                    } else {
                        listCellRendererComponent.setBackground(Color.BLUE);
                    }
                }
                return listCellRendererComponent;
            }

        });
        return list;
    }

    private JTextArea makeTextArea() {
        JTextArea textArea = new JTextArea("Drag here from JList!");
        return textArea;
    }

    public JComponent makeUI() {
        JPanel panel = new JPanel(new GridLayout(2,1));
        panel.add(new JScrollPane(makeTextArea()));
        panel.add(new JScrollPane(makeList()));
        return panel;
    }

    private static void createAndShowGUI() {
        JFrame f = new JFrame("BasicDnD");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        BasicTextListDND2 app = new BasicTextListDND2();
        JComponent appContent = app.makeUI();
        f.setContentPane(appContent);
        f.setSize(600, 320);
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override 
            public void run() { 
                createAndShowGUI(); 
            }
        });
    }
}


/**
 * 
 */
class BasicListTransferHandler extends TransferHandler {

    /**
     * Comment for <code>serialVersionUID</code>
     */
    private static final long serialVersionUID = 1L;

    private final JLabel label = new JLabel() {
        @Override public boolean contains(int x, int y) {
            return false;
        }
    };
    private final JWindow window = new JWindow();
    public BasicListTransferHandler() {
        super();
        window.add(label);
        //window.setBackground(new Color(0, true));
        window.setOpacity(.8f);
        DragSource.getDefaultDragSource().addDragSourceMotionListener(new DragSourceMotionListener() {
            @Override public void dragMouseMoved(DragSourceDragEvent dsde) {
                Point pt = dsde.getLocation();
                pt.translate(10, 10); // offset
                if (!window.isVisible()) {
                    window.setVisible(true);
                }
                window.setLocation(pt);
            }
        });
    }
    @Override protected void exportDone(JComponent c, Transferable data, int action) {
        super.exportDone(c, data, action);
        window.setVisible(false);
    }

    @Override 
    public boolean canImport(TransferHandler.TransferSupport info) {
        if (!info.isDataFlavorSupported(DataFlavor.stringFlavor)) {
            return false;
        }
        JList.DropLocation dl = (JList.DropLocation)info.getDropLocation();
        if (dl.getIndex() == -1) {
            return false;
        }
        return true;
    }

    @Override 
    public int getSourceActions(JComponent c) {
        BufferedImage dragImage = makeImageFromString(c);
        if (dragImage != null) {
            //setDragImage(dragImage);            
            //Point mousePosition = c.getMousePosition();
            //if (mousePosition != null) {
            //    setDragImageOffset(mousePosition);
            //}
            label.setIcon(new ImageIcon(dragImage));
            window.setLocation(-2000, -2000);
            window.pack();
        }
        return COPY;
    }

    private final JPanel tempDrawPanel = new JPanel();

    private BufferedImage createDragImage(JList<String> list) {
        int width = 0;
        int height = 0;
        int[] selectedIndices = list.getSelectedIndices();
        for(int i =0; i<selectedIndices.length; i++){
            int idx = selectedIndices[i];
            Rectangle cellBounds = list.getCellBounds(idx, idx);
            height += cellBounds.height;
            width = Math.max(width, cellBounds.width); // we want to create a drag image as big as the largest cell
        }
        BufferedImage br = null;
        if (width > 0 && height > 0) {
            br = list.getGraphicsConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);
        }
        return br;
    }

    private BufferedImage makeImageFromString(JComponent src) {
        JList<String> sourceList = (JList<String>)src;
        BufferedImage br = createDragImage(sourceList);
        if (br != null) {
            int[] selectedIndices = sourceList.getSelectedIndices();
            int yD = 0;
            Graphics g = br.getGraphics();
            try{
                for(int idx: selectedIndices) {
                    ListCellRenderer<? super String> cellRenderer = sourceList.getCellRenderer();
                    String valueAt = sourceList.getModel().getElementAt(idx);
                    Component c = cellRenderer.getListCellRendererComponent(sourceList, valueAt, idx, false, false);
                    Rectangle itemCellBounds = sourceList.getCellBounds(idx, idx);
                    //SwingUtilities.paintComponent(g, c, tempDrawPanel, itemCellBounds.x, itemCellBounds.y + yD, itemCellBounds.width, itemCellBounds.height);
                    //yD = itemCellBounds.y+itemCellBounds.height;
                    SwingUtilities.paintComponent(g, c, tempDrawPanel, 0, 0, itemCellBounds.width, itemCellBounds.height);
                    g.translate(0, itemCellBounds.height);
                }
            }finally {
                g.dispose();
            }
            br.coerceData(true);
        }
        return br;
    }

    @Override 
    protected Transferable createTransferable(JComponent c) {
        JList<String> list = (JList<String>)c;
        List<String> selectedValuesList = list.getSelectedValuesList();
        StringBuffer buff = new StringBuffer();
        for (int i = 0; i < selectedValuesList.size(); i++) {
            String val = selectedValuesList.get(i);
            buff.append(val == null ? "" : val.toString());
            if (i != selectedValuesList.size()- 1) {
                buff.append("\n");
            }
        }
        return new StringSelection(buff.toString());
    }
}