我有一个简单的任务来实现,它工作得很好,但是我面临着一个非常棘手的问题,关于在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中是否有任何模糊的逻辑应用此渐变?
任何帮助都将不胜感激!马吕斯。
如何翻译图形上下文的起源:
//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());
}
}