Java图形图像处理【Swing图像拖拽】

    • 18.3.3 Swing图像对象拖拽功能

18.3.3 Swing图像对象拖拽功能

上文讨论的是java.awt.dnd包中提供的拖拽API接口,也可称之为AWT组件的拖拽功能。下面我们要讨论的是Swing框架的拖拽功能:Swing组件也提供了对拖拽功能的支持,它是建立在AWT拖拽API接口之上的。

在Swing中,TransferHandler是一个专门用于简化拖放操作(Drag and Drop)和数据传输的类。它提供了对文本、图像、文件等数据类型的拖放支持,并且可以轻松地与Swing组件(如JTextField、JLabel、JList等)集成。
从Java SE1.4版本开始,多种Swing组件已经内置了对拖拽功能的数据传递的支持。

既能作为拖拽源(Drag Source)组件,又能作为拖放目标(Drop Target)组件的常用Swing组件主要有颜色选择器JColorChooser和文本组件的子类。JTextComponent文本组件的子类有JTextField、JTextArea、JTextPane、JEditorPane等都支持拖放功能。
只能作为拖拽源的Swing组件有JList、JTable、JTree和JFileChoose。

默认情况支持拖拽功能的Swing组件都不能直接作为拖拽源使用,必须调用组件自带的setDragEnabled(true)方法来开启此功能,如JTextArea组件也是如此。

Swing组件拖拽功能(Drag and Drop)主要涉及到三个部分:拖拽源(Drag Source)拖放目标(Drop Target)传输对象(Transferable)。 它们分别表示从哪个组件drag(拖出), drop(拖入)到哪个组件, 以及Transferable(传输对象,即拖拽的内容)。

Swing包中有一个 TransferHandler类,该类用于处理Transferable对象在Swing组件中的传输,支持剪贴板和拖拽功能。
TransferHandler类定义了四个静态常量表示拖拽动作:
(1) TransferHandler.COPY 表示“复制”
(2) TransferHandler.MOVE 表示“移动”
(3) TransferHandler.COPY_OR_MOVE 表示“复制或移动”
(4) TransferHandler.LINK 表示“链接”

TransferHandler是Swing中实现文本图像对象拖拽功能的推荐方式,它简化了数据传输和拖放逻辑。通过重写createTransferable、canImport和importData方法,可以轻松实现文本、图像等数据类型的拖放。
TransferHandler类有二个静态内部类:TransferHandler.DropLocation,表示要插入拖拽数据对象的位置;TransferHandler.TransferSupport,该类封装了与剪贴板和拖拽功能的细节,允许自定义各种特性。

除了前文介绍的已经内置了支持拖拽功能的Swing组件外,还有一些Swing组件可以通过自定义来支持拖拽功能。对于不支持拖拽功能的组件,如JLabel、JButton,没有setDragEnabled(true)方法,要想作为拖拽源,就需要自己定制来实现拖拽功能。通常需要做二方面的工作:自定义传输控制器类(TransferHandler)给组件添加MouseMotionListener

JComponent及其子类都可以作为拖放目标(Drop Target),其它的Swing类不行。像JLabel组件,若同时还要作为拖放目标(Drop Target),另外还要在合适的地方给组件设置TransferHandler,并对一些方法进行重载。

定制JLabel组件支持拖拽功能:
JLabel组件没有setDragEnabled(true)方法。
下面代码展示了如何定制JLabel组件支持拖拽功能,成为拖拽源:(最简方式,甚至不用定义一个传输处理器,用new TransferHandler(“text”)代替)。JButton组件也可如此处理。

		label.addMouseListener( new MouseAdapter() { //最简方式开启JLabel拖拽功能@Overridepublic void mousePressed(MouseEvent e) {JComponent c = (JComponent)e.getSource();c.setTransferHandler(new TransferHandler("text")); //设置处理器TransferHandler handler = c.getTransferHandler(); //取得处理器int actionModel = TransferHandler.COPY;	//设置拖拽方式handler.exportAsDrag(c, e, actionModel); //导出拖拽数据对象}	});      //label.addMouseListener()结束。标签JButton也可如此处理。

对于不支持拖拽功能组件,中规中矩的开启方法是:

  • 先定义一个传输处理器(与上例的JLabel组件的最简方式开启方法比较):
class LabelTransHandler  extends TransferHandler{ //定义传输处理器实现类/***下面二个方法是拖拽源必须重写实现的***/@Override  //产生拖放数据对象方法,拖放源必须实现重写。protected Transferable createTransferable(JComponent src) {String txt = ((JLabel)src).getText();return new StringSelection(txt);}@Override  //获取拖出动作方式方法,拖放源必须实现重写public int getSourceActions(JComponent src) { return COPY ; } 
}		//定义传输处理器实现类结束。
  • 然后,使用这个新定义的传输处理器。把上面JLabel组件最简方式开启支持拖拽功能的代码中对应二行进行更新(其他代码不变):
	c.setTransferHandler(new LabelTransHandler()); //设置处理器int actionModel = handler.getSourceActions(c);	//设置拖拽方式

【例程18-18】 最简拖拽测试例程DragLabelFrm

我们一起来探究一个JLabel组件开启拖拽功能的“最简拖拽测试”例程。其实这个例程是从后文一个比较复杂的演示例程“DragDropFrm.java”简化而来的,但却遇到了问题,无法得到预期结果,开始一直无法拖拽到JTextArea中,这是什么原因呢?

这个例程窗体的布局管理器使用网格布局管理器(GridLayout),左右分为二个网格,网格上各放置一个JPanel面板,左边JPanel面板放置一个标签组件,右边JPanel面板放置组件JTextArea。例程的功能可把左边标签组件中的文本拖拽复制到右边的文本区域组件JTextArea中,演示效果图如下所示。
测试效果图:
在这里插入图片描述
下面的例程开始没有加“rightPan.setLayout(new GridLayout(1, 1));”这行,一直无法得到预期结果。请你把这行注释掉,编译后测试体验一下。

经过调试才知道原来与布局管理器有关,在JPanel组件的默认布局管理器情况下,无内容的JTextArea它是不展开的,所以拖拽测试时鼠标选不中JTextArea(程序检测不到),就拖不过去。后来我加上一行“area.setText(“这里是拖拽测试area区”);” 组件JTextArea有文本内容,组件JTextArea展开了,程序就检测到了,能拖拽成功了。当JPanel组件使用GridLayout布局管理器,无内容的JTextArea它也是展开的,所以本例程能得到预期结果。

请自行对本例程进行反复调试,体验其中三味。实际上这个例程中两个JPanel面板是多余的,去掉程序更简明。
最简拖拽测试例程DragLabelFrm.java源代码文件,如下所示:

import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
public class DragLabelFrm extends JFrame {private JPanel leftPan = new JPanel();private JPanel rightPan = new JPanel();private JLabel txtTabel = new JLabel("拖拽");private JTextArea area = new JTextArea();public DragBtnFrm() {setLayout(new GridLayout(1, 2));leftPan.setBorder(BorderFactory.createLineBorder(Color.BLACK));rightPan.setBorder(BorderFactory.createLineBorder(Color.BLACK));add(leftPan);add(rightPan);leftPan.add(txtTabel,BorderLayout.CENTER);rightPan.setLayout(new GridLayout(1, 1)); //这一行能删除吗?rightPan.add(area);area.setFont(new Font("Serif", Font.BOLD, 48));//area.setText("这里是拖拽测试area区");txtTabel.setFont(new Font("Serif", Font.BOLD, 36));txtTabel.addMouseListener(new MouseAdapter() {@Overridepublic void mousePressed(MouseEvent e) {JComponent c = (JComponent)e.getSource();c.setTransferHandler(new TransferHandler("text"));TransferHandler handler = c.getTransferHandler();handler.exportAsDrag(c, e, TransferHandler.COPY);}	});   //给按钮设置监听器,同时开启拖拽功能}public static void main(String[] args) {JFrame frame = new DragLabelFrm();frame.setTitle("最简拖拽测试");frame.setSize(220,120);frame.setVisible(true);}
}	//最简拖拽测试例程DragLabelFrm.java源代码文件结束。

后面有一个功能更强大的演示例程,使用JButton和定制的JLabel组件开启拖拽功能的完整实例,请参见后文的“DragDropFrm例程”。

可以使用TransferHandler来实现图像标签的拖放功能!TransferHandler不仅可以处理拖放目标的逻辑,还可以处理拖放源的逻辑。为了定制Swing组件的拖放行为,通过定义传输控制器TransferHandler的实现类,可以为JLabel实现图像拖放功能。请看一个JLabel图像拖放例程:

【例程18-19】图像拖拽例程SwingImgLableDragAndDrop
图像拖放例程的源码和测试效果图:
在这里插入图片描述

package dragAndDrop;
import clipboard.ImageSelection;
import javax.swing.*;
import java.awt.*;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class SwingImgLableDragAndDrop extends JFrame {private JLabel imageSource, imageTarget; // 图像拖放源和目标public SwingImgLableDragAndDrop() {setTitle("图像拖拽演示");setSize(320, 150);setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);setLayout(new GridLayout(1, 2));// 图像拖放源
ImageIcon icon = new ImageIcon("image\\car.png");imageSource = new JLabel(icon);imageSource.setHorizontalAlignment(JLabel.CENTER);imageSource.setVerticalAlignment(JLabel.CENTER);// 设置TransferHandlerimageSource.setTransferHandler(new ImageTransferHandler()); // 启用拖放功能imageSource.addMouseListener( new MouseAdapter() {@Overridepublic void mousePressed(MouseEvent e) {JComponent c = (JComponent)e.getSource();TransferHandler handler = c.getTransferHandler(); //取得处理器int actionModel = TransferHandler.COPY;	//设置拖拽方式handler.exportAsDrag(c, e, actionModel); //导出拖拽数据对象}});add(new JScrollPane(imageSource));// 图像拖放目标imageTarget = new JLabel();imageTarget.setHorizontalAlignment(JLabel.CENTER);imageTarget.setVerticalAlignment(JLabel.CENTER);imageTarget.setText("拖放图像到这里");// 设置 TransferHandlerimageTarget.setTransferHandler(new ImageTransferHandler()); add(new JScrollPane(imageTarget));setVisible(true);}public static void main(String[] args) {new SwingImgLableDragAndDrop();}
}
/*** 图像对象的 TransferHandler ***/
class ImageTransferHandler extends TransferHandler {@Overrideprotected Transferable createTransferable(JComponent component) {JLabel label = (JLabel) component;ImageIcon icon = (ImageIcon) label.getIcon();if (icon != null) {return new ImageSelection(icon.getImage());}return null;}@Overridepublic int getSourceActions(JComponent component){return COPY; // 允许复制操作}@Overridepublic boolean canImport(TransferSupport support) {return support.isDataFlavorSupported(DataFlavor.imageFlavor);}@Overridepublic boolean importData(TransferSupport support) {try {Transferable trans = support.getTransferable(); //获取传输对象数据Image image = (Image)trans.getTransferData(DataFlavor.imageFlavor);JLabel target = (JLabel)support.getComponent();target.setIcon(new ImageIcon(image));target.setText(null); // 清除提示文本return true;} catch (Exception e) {e.printStackTrace();return false;}}
}	// SwingImgLableDragAndDrop程序源码结束。

【例程18-20】 图像列表拖拽例程ImageListDnDFrm

图像列表拖拽例程ImageListDnDFrm定义了一个TransferHandler的实现类ImageListTransHandler。首先,重写getSourceActions方法,用于定义拖拽源组件支持的拖放动作的类型(复制、移动、复制或移动,或者链接)。其次,重写createTransferable方法,以创建Transferable(传输的数据对象),它是数据传输源,其过程与剪贴板复制对象类似。这样,在拖放过程中调用getTransferable方法就可得到传输的数据对象。
例程定义的ImageList继承自JList组件,支持拖拽功能,用setDragEnabled(true)方法进行设置后,即可成为拖拽源(Drag Source);经自定义又成为拖放目标,允许拖入。

图像列表拖拽例程ImageListDnDFrm共包括四个类文件,包括前面剪贴板章节定义的 ImageSelection类,现在要介绍的三个类,图像列表传输处理器类 ImageListTransHandler类,自定义的组件 ImageList类,测试主控类 ImageListDnDFrm类

下面这行代码是程序主窗口类中生成ImageList类实例:

	listA = new ImageList(Paths.get(System.getProperty("user.dir"), "imgR"));

构造器ImageList( Path path ) 的参数是路径Path,代码里的路径path由工具类Paths的静态方法get()产生,其中的参数System.getProperty(“user.dir”)可获得用户工作目录,另一个参数”imgR”是工作目录下的子目录,在目录imgR中包含有测试用的图像文件。子目录中的图像文件被导入到ImageList对象中。

图像列表传输处理器ImageListTransHandler.java源代码文件,如下所示:

package drag;
import java.awt.Image;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.DefaultListModel;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JList;
import javax.swing.TransferHandler;
import clipboard.ImageSelection;  //需要前文中自定义类ImageSelection支持
/***定义一个支持拖拉功能的传输处理器实现类:图像列表传输处理器***/
public class ImageListTransHandler extends TransferHandler {/***下面三个方法是拖放源必须重写实现的***/@Override  //产生拖放对象方法,拖放源必须实现重写。protected Transferable createTransferable(JComponent src) {ImageList list = (ImageList)src;int index = list.getSelectedIndex();if (index<0) return null;ImageIcon icon = list.getModel().getElementAt(index);return new ImageSelection(icon.getImage());}@Override  //重写此方法,拖出后调整拖拽源。protected void exportDone(JComponent src, Transferable data, int action) {if (action==MOVE) {ImageList list = (ImageList)src; int index = list.getSelectedIndex();if (index<0) return; //组件中没有数据对象DefaultListModel<?> model = (DefaultListModel<?>)list.getModel();model.remove(index); //数据对象拖出时,在拖放源容器中删除对象。}}@Override  //获取拖出动作方式方法,拖放源必须实现重写public int getSourceActions(JComponent src) { return COPY_OR_MOVE; }/****上面三个方法是拖放源必须重写的;下面二个方法是拖放目标必须重写***/@Override  //允许拖入方法。支持拖放drop,拖放目标必须重写public boolean canImport(TransferSupport support) {if (support.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {if (support.getUserDropAction()==MOVE) support.setDropAction(COPY);return true;}else  return support.isDataFlavorSupported(DataFlavor.imageFlavor);}@Override  //拖入对象方法。支持拖放drop,拖放目标必须重写public boolean importData(TransferSupport support) {ImageList list = (ImageList)support.getComponent();
DefaultListModel<ImageIcon> model = (DefaultListModel<ImageIcon>)list.getModel();Transferable trans = support.getTransferable(); //获取传输对象数据List<DataFlavor> flavors = Arrays.asList(trans.getTransferDataFlavors()); //数据类型List<Image> images = new ArrayList<>(); //图像列表,类似保存图像的数组try {if(flavors.contains(DataFlavor.javaFileListFlavor)){ //文件列表@SuppressWarnings("unchecked")List<File> fList = (List<File>)trans.getTransferData(DataFlavor.javaFileListFlavor);for (File file : fList)  images.add(ImageIO.read(file)); //遍历打开图像文件} else if (flavors.contains(DataFlavor.imageFlavor)) //普通图像images.add((Image)trans.getTransferData(DataFlavor.imageFlavor));int index;if (support.isDrop()) { //是拖放操作JList.DropLocation location = (JList.DropLocation)support.getDropLocation();index = location.getIndex();if (!location.isInsert()) model.remove(index); //替换位置} else index = model.size();for (Image image : images) model.add(index++, new ImageIcon(image));return true;} catch (IOException|UnsupportedFlavorException e) {return false;} }
}	//图像列表传输处理器ImageListTransHandler.java源代码文件结束。

图像列表组件类ImageList.java源代码文件,如下所示:

package drag;
import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import javax.swing.DefaultListModel;
import javax.swing.DropMode;
import javax.swing.ImageIcon;
import javax.swing.JList;
/***继承自JList的自义的图像列表组件***/
public class ImageList extends JList<ImageIcon>{DefaultListModel<ImageIcon> model;public ImageList() { //无参数构造器model = new DefaultListModel<>();setModel(model);  //设置列表模式setVisibleRowCount(0);setLayoutOrientation(JList.HORIZONTAL_WRAP);setDragEnabled(true);  //设置为拖拽源,允许拖出。setDropMode(DropMode.ON_OR_INSERT); //设置拖放模式setTransferHandler(new ImageListTransHandler()); //设置传输处理器}public ImageList( Path path ) { //参数是文件路径(文件夹、文件目录)this();  //调用无参数构造器try(DirectoryStream<Path> dirs = Files.newDirectoryStream(path)) {for (Path dir : dirs) //把文件夹中的图像导入到列表中model.addElement(new ImageIcon(dir.toString()));} catch (IOException e) { e.printStackTrace(); }}
}		//图像列表组件类ImageList.java源代码文件结束。

swing拖放测试程序主窗口类ImageListDnDFrm.java源代码文件,如下所示:

package drag;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.nio.file.Paths;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
public class ImageListDnDFrm extends JFrame {private static final int W=300;private static final int H=160;private ImageList listA,listB;public ImageListDnDFrm() {setTitle("Swing拖放测试");setSize(new Dimension(W, H));listA = new ImageList(Paths.get(System.getProperty("user.dir"), "imgR"));listB = new ImageList(Paths.get(System.getProperty("user.dir"), "imgB"));setLayout(new GridLayout(2, 1));add(new JScrollPane(listA)); //有滚动条组件add(new JScrollPane(listB));setVisible(true);}/***测试例程主控程序***/public static void main(String[] args) {	new ImageListDnDFrm(); 	}
}	//swing拖放测试程序主窗口类ImageListDnDFrm.java源代码文件结束。

例程ImageListDnDFrm的主窗口包含二个ImageList(图像列表框):其中一个读入子目录imgR中红棋的图像文件,显示的是红棋棋子;另一个读入子目录imgB中黑棋的图像文件,显示的是黑棋棋子。二幅插图,第一幅是程序运行的初始状态:
在这里插入图片描述

然后,把“红炮”从上面的图像列表框拖拽到下面的图像列表框中,第二幅是演示效果图。
在这里插入图片描述

请自行探究代码,并测试体验。此例程也同时支持直接在文件夹中选择图像文件拖入图像列表框。

【例程18-21】 图像拖拽例程DragDropFrm

我们再来看一个Swing组件的拖拽例程。DragDropFrm例程演示的是另一种Swing拖拽功能。例程的程序窗口定义了文本按钮、图像标签、文本区域JTextArea和图像列表ImageList四部分,其中前二个组件作为拖拽源,后二个作为拖放目标组件。附图是运行效果图,把左边的按钮或标签拖拽到右边组件的运行结果。程序需要用到前文中介绍的剪贴板中定义的ImageSelection类,以及上个例程定义的ImageList等类。

文本区域JTextArea组件支持拖拽功能,默认情况下只支持文本拖入;但若要支持文本拖出作为,作为拖拽源需要如下打开拖拽功能:

	area.setDragEnabled(true); //开启,成为拖拽源。可使文本拖拽到“写字板”等程序中

因此,DragDropFrm例程已支持JTextArea中的文本拖到WPS文档或写字板中。如果注释掉上面这一行,则关闭此功能。

例程源码文件DragDropFrm.java 如下所示:

package drag;
import java.awt.*;
import java.awt.datatransfer.Transferable;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
//需要剪贴板中定义的ImageSelection类
import clipboard.ImageSelection; 
public class DragDropFrm extends JFrame {private JPanel leftPan = new JPanel();private JPanel rightPan = new JPanel();private JButton txtBtn = new JButton("拖拽");private ImageIcon icon = new ImageIcon("image\\地球80.png");private JLabel imgLab = new JLabel(icon);private JTextArea area = new JTextArea();private ImageList imageList = new ImageList(); //自定义图像列表public DragDropFrm() {setLayout(new GridLayout(1, 2));leftPan.setBorder(BorderFactory.createLineBorder(Color.BLACK));rightPan.setBorder(BorderFactory.createLineBorder(Color.BLACK));add(leftPan);add(rightPan);leftPan.setLayout(new GridLayout(2, 1));leftPan.add(txtBtn,BorderLayout.CENTER);leftPan.add(imgLab);rightPan.setLayout(new GridLayout(2, 1));rightPan.add(area);rightPan.add(new JScrollPane(imageList)); //自定义图像列表组件area.setDragEnabled(true); //开启文本区域的拖拽功能area.setFont(new Font("Serif", Font.BOLD, 24));txtBtn.addMouseListener(new MouseAdapter() {@Overridepublic void mousePressed(MouseEvent e) {JComponent c = (JComponent)e.getSource();c.setTransferHandler(new TransferHandler("text"));TransferHandler handler = c.getTransferHandler();int actionModel = handler.getSourceActions(c);handler.exportAsDrag(c, e, actionModel);}	});   //给按钮设置监听器,同时开启拖拽功能imgLab.addMouseListener(new MouseAdapter() {@Overridepublic void mousePressed(MouseEvent e) {JComponent c = (JComponent)e.getSource();c.setTransferHandler(new LabelImgTransHandler());TransferHandler handler = c.getTransferHandler();int actionModel = handler.getSourceActions(c);handler.exportAsDrag(c, e, actionModel);}	});   //给标签设置监听器,同时开启拖拽功能}public static void main(String[] args) {JFrame frame = new DragDropFrm();frame.setTitle("Swing拖拽功能测试");frame.setSize(300,200);frame.setVisible(true);}
}
class LabelImgTransHandler  extends TransferHandler{@Override  //产生拖放数据对象方法,拖放源必须实现重写。protected Transferable createTransferable(JComponent src) {Image image = ((ImageIcon)((JLabel)src).getIcon()).getImage();return new ImageSelection(image);}@Override  //获取拖出动作方式方法,拖放源必须实现重写public int getSourceActions(JComponent src) { return COPY ; } 
}		//示例程序源代码文件DragDropFrm.java结束。

测试效果图:
在这里插入图片描述

【例程18-22】 中国象棋拖拽设置局面例程ChessFrm

很多棋牌类的象棋游戏程序,如天天象棋、象棋演播室(XQStudio)、象棋旋风等游戏程序都有利用拖拽设置象棋局面的功能。我们今天也来设计一个类似的程序。

在前文的“Swing中间容器与边框(Border)组件”章节,我们曾介绍过一个“中国象棋设置局面演示程序”那是初稿,只实现界面展示,现在我们来加上Swing组件的拖拽功能。

我们把标签组件同时作为拖拽源(Drag Source)和拖放目标(Drop target),程序的设计要点如下:
1,在前文的程序初稿中试图用没有图像和文本的标签作为占位符,结果与预期不符。我的解决方法是使用空白图像作为占位符(全透明的图像,这与null是不同的)。
2,设计一个图像标签的传输控制器类ImgLabelTransHandler。
3,对初稿程序中的ManPad类略作更新,增加了一个静态成员mouseLsn作为鼠标适配器,以作为拖拽的监听器使用。还给标签注册了监听器。 label.addMouseListener(mouseLsn);

	//定义鼠标适配器,以开启拖拽功能public static MouseAdapter mouseLsn = new MouseAdapter(){@Overridepublic void mousePressed(MouseEvent e) {JComponent c = (JComponent)e.getSource();c.setTransferHandler(new ImgLabelTransHandler());TransferHandler handler = c.getTransferHandler();int actionModel = handler.getSourceActions(c);handler.exportAsDrag(c, e, actionModel);}};     //定义鼠标适配器,以开启拖拽功能。代码结束

4,ChessPad类中的位置标签组件,兼要作为拖拽源,又要作为拖放目标需要多做一点工作,在初始化时给标签组件设置传输控制器。构造器ChessPad()增加了如下代码:

				/***给棋盘着点位置标签设置传输处理器***/JComponent c = (JComponent)label;c.setTransferHandler(new ImgLabelTransHandler());//给标签设置监听器,同时开启拖拽功能label.addMouseListener(ManPad.mouseLsn);

增加了拖拽功能的“中国象棋设置局面演示程序”完整程序代码如下:

中国象棋设置局面例程ChessFrm.java 如下所示:

package fram;
import java.awt.*;
import java.awt.event.*;
import java.awt.datatransfer.*;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.CompoundBorder;import clipboard.ImageSelection;
public class ChessFrm extends JFrame {String path = "image\\small\\Board.png";String path2 = "image\\small\\xy1.png";private ImageIcon icon,icon2;private ChessPad cPad = new ChessPad();private JPanel bManP = null;private JPanel rManP = null;public ChessFrm() {setTitle("中国象棋设置局面演示程序");int w = ChessPad.getWIDTH();int h = ChessPad.getHEIGHT();cPad.setBounds(28, 28, w, h);icon = new ImageIcon(path);icon2 = new ImageIcon(path2);CBoard board = new CBoard(icon,icon2);board.add(cPad); //把cPad放入CBoardbManP = ManPad.crtManPad('b');rManP = ManPad.crtManPad('r');add(bManP, BorderLayout.WEST);add(board,BorderLayout.CENTER);add(rManP, BorderLayout.EAST);pack();setVisible(true);}public static void main(String[] args) {	new ChessFrm(); }}//棋盘的定义类
class CBoard extends JPanel {private ImageIcon img,img2;public CBoard(ImageIcon icon,ImageIcon icon2) {img = icon;img2 = icon2;int width = icon.getIconWidth();int height = icon.getIconHeight();setPreferredSize(new Dimension(width,height));setLayout(null); //设置空的布局管理器 }@Overrideprotected void paintComponent(Graphics g) {super.paintComponent(g);// 画图像:第一行用于显示棋盘图像;第二行用于显示横坐标g.drawImage(img.getImage(), 0, 0, null);g.drawImage(img2.getImage(), 0, 0, null);}	
}//棋盘Pad的定义类。此类要继承自JComponent,这里不能用JPanel。
class ChessPad extends JComponent {private ImageIcon bkImg,rkImg,占位符Img;//棋盘着点标签:可放棋子的位置(标签),共90个。private JLabel points[][] = new JLabel[10][9];private static  int WIDTH = 234;private static  int HEIGHT = 260;//使用网格布局管理器放置标签组件public ChessPad(){ bkImg = new ImageIcon("image\\small\\bk.png");rkImg = new ImageIcon("image\\small\\rk.png");JLabel label = null;占位符Img = new ImageIcon("image\\small\\透明占位符.png");setLayout(new GridLayout(10,9));//设置布局管理器for (int i = 0; i < points.length; i++) {for (int j = 0; j < points[i].length; j++) {label = new JLabel(); label.setIcon(占位符Img);label.setPreferredSize(new Dimension(26,26));add(label); //把位置标签放在ChessPad/***给棋盘着点位置标签设置传输处理器***/JComponent c = (JComponent)label;c.setTransferHandler(new ImgLabelTransHandler());//给标签设置监听器,同时开启拖拽功能label.addMouseListener(ManPad.mouseLsn);points[i][j] =  label;}}points[0][4].setIcon(bkImg);points[9][4].setIcon(rkImg);}public static int getWIDTH() { return WIDTH; }public static int getHEIGHT() { return HEIGHT;}
}//棋子托盘ManPad的定义类
class ManPad {private static String ManNames[][] = {{"士","象","卒","车","马","炮"},{"士","相","兵","车","马","炮"}};private static ImageIcon icons[][] = {{	new ImageIcon("image\\small\\ba.png"),new ImageIcon("image\\small\\bb.png"),new ImageIcon("image\\small\\bp.png"),new ImageIcon("image\\small\\br.png"),new ImageIcon("image\\small\\bn.png"),new ImageIcon("image\\small\\bc.png") },{	new ImageIcon("image\\small\\ra.png"),new ImageIcon("image\\small\\rb.png"),new ImageIcon("image\\small\\rp.png"),new ImageIcon("image\\small\\rr.png"),new ImageIcon("image\\small\\rn.png"),new ImageIcon("image\\small\\rc.png") }};//定义鼠标适配器,开启拖拽功能public static MouseAdapter mouseLsn = new MouseAdapter(){@Overridepublic void mousePressed(MouseEvent e) {JComponent c = (JComponent)e.getSource();c.setTransferHandler(new ImgLabelTransHandler());TransferHandler handler = c.getTransferHandler();int actionModel = handler.getSourceActions(c);handler.exportAsDrag(c, e, actionModel);}};public static JPanel crtManPad(char type) {JPanel manPad = new JPanel();manPad.setLayout(new GridLayout(6,1));if (type=='b') /**黑棋棋子**/for (int i=0; i< ManNames[0].length; i++){JLabel label = new JLabel(icons[0][i]);manPad.add(label);//给标签设置监听器,同时开启拖拽功能label.addMouseListener(mouseLsn);}if (type=='r') /**红棋棋子**/for (int i=0; i< ManNames[1].length; i++){JLabel label = new JLabel(icons[1][i]);manPad.add(label);//给标签设置监听器,同时开启拖拽功能label.addMouseListener(mouseLsn);}/***给面板设置组合边框***/Border rbBorder = BorderFactory.createRaisedBevelBorder();Border lbBorder = BorderFactory.createLoweredBevelBorder();CompoundBorder cBorder = new CompoundBorder(rbBorder, lbBorder);manPad.setBorder(cBorder);return manPad;}
}class ImgLabelTransHandler  extends TransferHandler{@Override  //产生拖放数据对象方法,拖放源必须实现重写。protected Transferable createTransferable(JComponent src) {ImageIcon icon = (ImageIcon)((JLabel)src).getIcon();return new ImageSelection(icon.getImage());}@Override  //获取拖出动作方式方法,拖放源必须实现重写。public int getSourceActions(JComponent src) { return /*COPY_OR_MOVE*/COPY ; } @Override  //重写此方法,拖出后调整拖拽源。protected void exportDone(JComponent src, Transferable data, int action) {if (action==MOVE) {JLabel label = (JLabel)src; label.setIcon(null); //数据对象(ImageIcon)拖出时,在拖放源中删除对象。label.revalidate();//label.repaint();//}} /***上面三个是拖放源必须重写的方法;下面二个是拖放目标必须重写的方法***/@Override  //允许拖入方法。支持拖放drop,拖放目标必须重写//public boolean canImport(TransferSupport support) {public boolean canImport(JComponent comp, DataFlavor[] flavors){List<DataFlavor> list = Arrays.asList(flavors);if (list.contains(DataFlavor.imageFlavor)) {return true;}else return false;}@Override  //拖入对象方法。支持拖放drop,拖放目标必须重写public boolean importData(JComponent comp, Transferable t){//public boolean importData(TransferSupport support) {Image image=null;JLabel label = (JLabel)comp; //拖放目标List<DataFlavor> flavors = Arrays.asList(t.getTransferDataFlavors()); //数据类型try {if (flavors.contains(DataFlavor.imageFlavor))image = (Image)t.getTransferData(DataFlavor.imageFlavor);label.setIcon(new ImageIcon(image));return true;} catch (IOException|UnsupportedFlavorException e) { 	return false; }}
}         //中国象棋拖拽设置局面例程ChessFrm.java结束。

测试效果图,附图是通过拖拽设置的一个双马饮泉的残局:
在这里插入图片描述

博客导航:
上一篇博客:Java图形图像处理【剪贴板与图像拖拽】【四】
下一篇博客:Java图形图像处理【仿射变换】【六】

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/94761.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/94761.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/94761.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

状态模式与几个经典的C++例子

1. 状态模式定义与核心思想 状态模式解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况。通过将状态的判断逻辑转移到表示不同状态的一系列类中,可以把复杂的判断逻辑简化。 核心思想: 状态抽象:将对象的每个状态都封装到一个独立的类中。 委托代替条件判断:…

SyncBackPro 备份及同步软件中的脚本功能简介

脚本提供了一种灵活而简单的方法来自动执行任务和扩展应用程序的功能。脚本是一个小程序&#xff0c;能够自定义和控制备份作业。例如&#xff0c;用户可以编写脚本来复制、重命名或删除特定文件、自定义用户界面或更改配置文件设置。SyncBackPro 的脚本功能类似于 Microsoft O…

部署2.516.2版本的jenkins,同时适配jdk8

&#x1f4cc; 前言 在企业级开发中&#xff0c;我们常常面临 新老项目并存 的复杂局面&#xff1a; 老项目基于 JDK 8 开发&#xff0c;短期内无法升级&#xff1b; 新项目采用 JDK 17&#xff08;LTS&#xff09;甚至更高版本&#xff1b; 而作为 CI/CD 核心的 Jenkins&#…

Autodesk Maya 2026.2 全新功能详解:MotionMaker AI 动画、LookdevX 材质增强、USD 工作流优化

软件介绍 Autodesk Maya 2026.2是一款专业的3D计算机图形软件&#xff0c;它为数字内容创作者提供了丰富的工具集&#xff0c;以实现高质量的建模、动画、模拟和渲染。该版本带来了多项性能优化和工作流程改进&#xff0c;特别是针对生成式动画工具MotionMaker进行了重大升级&…

STM32之DMA详解

一、DMA 1. DMA的引入 在嵌入式系统或计算机系统中&#xff0c;数据的传输和处理是非常重要的操作。以下通过一个简单的示例来展示传统数据操作方式与 DMA 引入的必要性&#xff1a; int a 10; int b 20;a b;上述代码包含了变量定义、初始化以及变量数据赋值操作。在传统…

链表有环找入口节点原理

环形链表入口检测算法数学解释 数学原理假设定义: 链表头到环入口的距离为 a环入口到相遇点的距离为 b相遇点到环入口的距离为 c环的长度为 b c第一次相遇时: 慢指针走了 s a b 步快指针走了 f a b n(b c) 步&#xff0c;其中 n 是快指针在环内转的圈数由于快指针速度是…

Java 基本类型与包装类详解

Java 基本类型与包装类详解 在 Java 开发中&#xff0c;理解 基本数据类型与包装类、字符串处理、以及高精度计算类是非常核心的能力。这不仅关系到程序性能&#xff0c;还影响代码的正确性和可维护性。本文将详细讲解这些知识点&#xff0c;并给出常见的使用技巧和陷阱。 1️…

CRYPT32!CryptMsgUpdate函数分析之CRYPT32!PkiAsn1Decode函数的作用是得到pci

第一部分&#xff1a; CryptMsgUpdate( #endifIN HCRYPTMSG hCryptMsg,IN const BYTE *pbData,IN DWORD cbData,IN BOOL fFinal) {ContentInfo *pci NULL;if ((PHASE_FIRST_FINAL pcmi->dwPhase) &&(0 pcmi->dwMsgType)) {if (0 …

华为交换机S5700设置acl

1.、配置ACL1.1、定义允许的ACL规则[sw1]acl number 3001[sw1-acl-adv-3001]rule permit ip source 192.168.20.0 0.0.0.255 destination 192.168.40.1 0[sw1-acl-adv-3001]rule permit ip source 192.168.30.0 0.0.0.255 destination 192.168.40.1 01.2、定义禁止的ACL规则[sw…

在使用spring ai进行llm处理的rag的时候,选择milvus还是neo4j呢?

在使用spring ai进行llm处理的rag的时候&#xff0c;选择milvus还是neo4j呢&#xff1f; 对于Spring AI中的RAG&#xff08;Retrieval-Augmented Generation&#xff09;应用&#xff0c;选择Milvus还是Neo4j&#xff0c;主要取决于你的数据类型以及RAG流程中对数据检索的侧重点…

计算机视觉与深度学习 | 视觉里程计技术全景解析:从原理到前沿应用

视觉里程计技术全景解析:从原理到前沿应用 一、定义与核心价值 二、技术原理与分类体系 2.1 基本工作流程 2.2 主流技术路线对比 2.3 算法范式演进 三、典型应用场景 3.1 地面移动机器人 3.2 自动驾驶领域 3.3 深空探测 3.4 增强现实 四、核心技术挑战与突破路径 4.1 主要技术…

Wireshark和USRP捕获同一信号波形差异原因

一、波形差异 在前面的博客中我对比绘制了同一信号的Wireshark和USRP两种波形&#xff1a; 可以看出波形差别还是挺大的&#xff0c;尤其是在信号分布间隔方面。 我猜想Wireshark的一条数据包在物理上并不是连续的&#xff1a; 而是分组发送&#xff0c;但在Wireshark中合并在…

Python-GEE遥感云大数据分析、可视化与Satellite Embedding应用

随着航空、航天、近地空间遥感平台的持续发展&#xff0c;遥感技术近年来取得显著进步。遥感数据的空间、时间、光谱分辨率及数据量均大幅提升&#xff0c;呈现出大数据特征。2025年7月&#xff0c;Google DeepMind发布了革命性的AlphaEarth Foundations模型及Satellite Embedd…

Python常见设计模式2: 结构型模式

文章目录适配器模式桥接模式组合模式外观模式代理模式适配器模式 将一个类的接口转换成客户希望的另一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。两种实现方式&#xff1a; 类适配器&#xff1a;使用多继承对象适配器&#xff1a;使用组合…

HDMI2.1 8K验证平台

本文推荐其中一个平台ZCU106HDMI2.1 FMC Card 一、ZCU106主要特性与优势 经过优化&#xff0c;可采用 Zynq Ultrascale MPSoC 快速进行应用原型设计集成型视频编解码器单元支持 H.264/H.265HDMI 视频输入输出PCIe 端点 Gen3x4、USB3、DisplayPort 和 SATADDR4 SODIMM – 64 位…

R语言使用随机森林对数据进行插补

数据插补的目的是为了恢复数据的完整性&#xff0c;以便后续的数据分析和挖掘工作能够顺利进行。插补方法的选择取决于数据的特点和缺失模式。常见的插补方法包括均值插补、回归插补、多重插补等。均值插补简单易行&#xff0c;但可能会改变数据分布&#xff1b;回归插补考虑了…

论文阅读:ICLR 2024 GAIA: A Benchmark for General AI Assistants

https://arxiv.org/pdf/2311.12983 https://www.doubao.com/chat/18484357054754562 GAIA: A Benchmark for General AI Assistants GAIA&#xff1a;通用人工智能助手基准测试 该论文介绍了GAIA&#xff08;General AI Assistants&#xff09;基准测试&#xff0c;这是一…

【Cmake】静态库(编译-链接-引用)相关函数

目录 一.file 1.1.示例一 1.2.示例二 1.2.1.GLOB 1.2.2.GLOB_RECURSE 1.3.示例三 1.3.1.GLOB 1.3.2.GLOB_RECURSE 1.4.file(GLOB)的缺点 二.add_library 示例 1&#xff1a;创建一个简单的静态库 示例 2&#xff1a;创建一个简单的共享库&#xff08;动态库&#x…

【50页PPT】钢铁企业数字化工厂解决方案需求要点(附下载方式)

篇幅所限&#xff0c;本文只提供部分资料内容&#xff0c;完整资料请看下面链接 https://download.csdn.net/download/2501_92796370/91716817 资料解读&#xff1a;钢铁企业数字化工厂解决方案需求要点 详细资料请看本解读文章的最后内容 钢铁行业数字化转型背景与意义 当…

Java深拷贝与浅拷贝核心解析

Java深拷贝与浅拷贝的概念浅拷贝&#xff08;Shallow Copy&#xff09;只复制对象的引用&#xff0c;而不复制对象本身。拷贝后的对象和原对象共享同一块内存地址中的子对象。修改其中一个对象的非基本类型属性时&#xff0c;另一个对象的对应属性也会被修改。深拷贝&#xff0…