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

发布于:2025-09-01 ⋅ 阅读:(17) ⋅ 点赞:(0)

Java图形图像处理【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拖拽功能
			@Override
			public 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() {
			@Override
			public 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);
        // 设置TransferHandler
        imageSource.setTransferHandler(new ImageTransferHandler()); 
        // 启用拖放功能
        imageSource.addMouseListener( new MouseAdapter() {
			@Override
			public 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("拖放图像到这里");
        // 设置 TransferHandler
        imageTarget.setTransferHandler(new ImageTransferHandler()); 
        add(new JScrollPane(imageTarget));
        setVisible(true);
    }

    public static void main(String[] args) {
        new SwingImgLableDragAndDrop();
    }
}
/*** 图像对象的 TransferHandler ***/
class ImageTransferHandler extends TransferHandler {
    @Override
    protected Transferable createTransferable(JComponent component) {
        JLabel label = (JLabel) component;
        ImageIcon icon = (ImageIcon) label.getIcon();
        if (icon != null) {
            return new ImageSelection(icon.getImage());
        }
        return null;
    }
    @Override
    public int getSourceActions(JComponent component){
        return COPY; // 允许复制操作
    }
    @Override
    public boolean canImport(TransferSupport support) {
        return support.isDataFlavorSupported(DataFlavor.imageFlavor);
    }
    @Override
    public 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() {
			@Override
			public 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() {
			@Override
			public 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(){
		@Override
		public 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放入CBoard
		
		bManP = 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); //设置空的布局管理器 
	}

	@Override
	protected 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(){
		@Override
		public 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图形图像处理【仿射变换】【六】


网站公告

今日签到

点亮在社区的每一天
去签到