论坛首页 Java企业应用论坛

Swing 实现截图小软件 (二)

浏览 6261 次
精华帖 (0) :: 良好帖 (1) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2011-02-23   最后修改:2011-02-23

 

刚才写了 Swing 实现截图小软件(一)后,点击“保存草稿”,回来再修改,点“编辑”在“可视化编辑器”中就出现了一堆的HTML代码了。 写不下去了,就直接转到(二)好了。

 

接上一节

 

第三步:为截取图像时,鼠标所标示的截取区域用矩形表示出来。

要实现根据鼠标的拖动,实时画矩形,可以采用

1. 取得鼠标的按下点和移动当前点坐标

2. 创建一个缓冲图形对象(BufferedImage) bi

3. 将原始图形画到 bi 中

4. 根据取得的坐标画一个矩形到 bi 中

5. 将 bi 画到屏幕上

 

 

imageLabel.addMouseMotionListener(new MouseMotionListener() {
	public void mouseDragged(MouseEvent e) {
		//1. 取得鼠标的按下点和移动当前点坐标
		xEnd = e.getX();
		yEnd = e.getY();
		
		//2. 创建一个缓冲图形对象(BufferedImage) bi
		BufferedImage bi = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_RGB);
		Graphics2D g2d = (Graphics2D)bi.getGraphics();
		//3. 将原始图形画到 bi 中
		g2d.drawImage(image, 0, 0, null);	
		g2d.setColor(Color.RED);			//设置画笔颜色为红色
		//4. 根据取得的坐标画一个矩形到 bi 中
		//以鼠标按下点坐标和鼠标拖动的当前点坐标画矩形,作为截图区域的展示
		//+1 与 -1 是为了防止截图时将矩形框也截进去
		g2d.drawRect(Math.min(x, xEnd) - 1, Math.min(y, yEnd) - 1, Math.abs(xEnd - x) + 1, Math.abs(yEnd - y) + 1);	
		g2d.dispose();
		
		//5. 将 bi 画到屏幕上
		Graphics g = imageLabel.getGraphics();
		g.drawImage(bi, 0, 0, null);
		g.dispose();
	}

	public void mouseMoved(MouseEvent e) {
		
	}
});

 

 完整代码:

import java.awt.AWTException;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;

import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;

/**
 * 全屏显示的窗口, 按 Alt + F4 退出
 * @author pengranxiang
 */
public class ScreenWindow extends JFrame {
	private static final long serialVersionUID = -3758062802950480258L;
	
	private Image image;
	private JLabel imageLabel;
	
	private int x, y, xEnd, yEnd;	//用于记录鼠标点击开始和结束的坐标

	public ScreenWindow() throws AWTException, InterruptedException {
		//取得屏幕尺寸
		Dimension screenDims = Toolkit.getDefaultToolkit().getScreenSize();
		//取得全屏幕截图
		image = GraphicsUtils.getScreenImage(0, 0, screenDims.width, screenDims.height);
		//用于展示截图
		imageLabel = new JLabel(new ImageIcon(image));
		//当鼠标在imageLabel上时,展示为 十字形
		imageLabel.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
		
		createAction();
		
		this.getContentPane().add(imageLabel);
		
		this.setUndecorated(true);	//去掉窗口装饰
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.setVisible(true);
		this.setExtendedState(JFrame.MAXIMIZED_BOTH);	//窗口最大化
	}
	
	/**
	 * 实现监听动作
	 */
	private void createAction() {
		imageLabel.addMouseListener(new MouseAdapter() {
			public void mousePressed(MouseEvent e) {
				x = e.getX();
				y = e.getY();
			}

			public void mouseReleased(MouseEvent e) {
				xEnd = e.getX();
				yEnd = e.getY();
				
				//鼠标弹起时,取得鼠标起始两点组成的矩形区域的图像
				try {
					//因为 xEnd 可能比  x 小 (由右网左移动)起始坐标取其中较小值,xEnd - x 取其绝对值, 同样处理y
					image = GraphicsUtils.getScreenImage(Math.min(x, xEnd), Math.min(y, yEnd), Math.abs(xEnd - x), Math.abs(yEnd - y));
				} catch (AWTException e1) {
					e1.printStackTrace();
				} catch (InterruptedException e1) {
					e1.printStackTrace();
				}
				
				//为了查看截图效果,将区域截图的部分代替全屏的截图展示
				imageLabel.setIcon(new ImageIcon(image));
			}
		});
		
		imageLabel.addMouseMotionListener(new MouseMotionListener() {
			public void mouseDragged(MouseEvent e) {
				//1. 取得鼠标的按下点和移动当前点坐标
				xEnd = e.getX();
				yEnd = e.getY();
				
				//2. 创建一个缓冲图形对象(BufferedImage) bi
				BufferedImage bi = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_RGB);
				Graphics2D g2d = (Graphics2D)bi.getGraphics();
				//3. 将原始图形画到 bi 中
				g2d.drawImage(image, 0, 0, null);	
				g2d.setColor(Color.RED);			//设置画笔颜色为红色
				//4. 根据取得的坐标画一个矩形到 bi 中
				//以鼠标按下点坐标和鼠标拖动的当前点坐标画矩形,作为截图区域的展示
				//+1 与 -1 是为了防止截图时将矩形框也截进去
				g2d.drawRect(Math.min(x, xEnd) - 1, Math.min(y, yEnd) - 1, Math.abs(xEnd - x) + 1, Math.abs(yEnd - y) + 1);	
				g2d.dispose();
				
				//5. 将 bi 画到屏幕上
				Graphics g = imageLabel.getGraphics();
				g.drawImage(bi, 0, 0, null);
				g.dispose();
			}
		
			public void mouseMoved(MouseEvent e) {
				
			}
		});
	}
	
	public static void main(String[] args) throws AWTException, InterruptedException {
		new ScreenWindow();
	}
}

class GraphicsUtils {
	/**
     * 截图屏幕中制定区域的图片
     * @param x
     * @param y
     * @param w
     * @param h
     * @return 被截部分的BufferedImage对象
     * @throws AWTException
     * @throws InterruptedException
     */
    public static BufferedImage getScreenImage(int x, int y, int w, int h) throws AWTException, InterruptedException {
		Robot robot = new Robot();
		BufferedImage screen = robot.createScreenCapture(new Rectangle(x, y, w, h));
		return screen;
	}
}

 

到这里,就可以有截图区域提示了。

 

然后,现在的截图显示,直接覆盖了前面的模拟屏幕,这只是一个演示。 软件中是需要一个按钮来开启这个模拟屏幕然后再去截图的。

 

第四步:创建程序主窗口,触发模拟屏幕,让截图的图像显示在主窗口中。

为了将截得的图像,传入到主窗口中,需要修改ScreenWindow, 增加 targetLabel 参数,用户接收主窗口中需要显示该截图的,目标组件。增加了一个右键点击退出模拟屏幕,所以增加了鼠标右键点击事件监听。

 

ScreenWindow完整代码:

import java.awt.AWTException;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;

import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;

/**
 * 全屏显示的窗口, 按 Alt + F4 退出
 * @author pengranxiang
 */
public class ScreenWindow extends JFrame {
	private static final long serialVersionUID = -3758062802950480258L;
	
	private Image image;
	private JLabel imageLabel;
	
	//将截图的图像加载到的目标组件,用于传值
	private JLabel targetLabel;
	
	private int x, y, xEnd, yEnd;	//用于记录鼠标点击开始和结束的坐标

	public ScreenWindow(JLabel targetLabel) throws AWTException, InterruptedException {
		//目标组件初始化
		this.targetLabel = targetLabel;
		
		//取得屏幕尺寸
		Dimension screenDims = Toolkit.getDefaultToolkit().getScreenSize();
		//取得全屏幕截图
		image = GraphicsUtils.getScreenImage(0, 0, screenDims.width, screenDims.height);
		//用于展示截图
		imageLabel = new JLabel(new ImageIcon(image));
		//当鼠标在imageLabel上时,展示为 十字形
		imageLabel.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
		
		createAction();
		
		this.getContentPane().add(imageLabel);
		
		this.setUndecorated(true);	//去掉窗口装饰
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.setVisible(true);
		this.setExtendedState(JFrame.MAXIMIZED_BOTH);	//窗口最大化
	}
	
	/**
	 * 实现监听动作
	 */
	private void createAction() {
		imageLabel.addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent e) {
				if(e.getButton() == MouseEvent.BUTTON3) { //鼠标右键单击事件
					//退出ScreenWindow
					ScreenWindow.this.dispose();
				}
			}
			
			public void mousePressed(MouseEvent e) {
				x = e.getX();
				y = e.getY();
			}
			
			public void mouseReleased(MouseEvent e) {
				xEnd = e.getX();
				yEnd = e.getY();
				
				//鼠标弹起时,取得鼠标起始两点组成的矩形区域的图像
				try {
					//因为 xEnd 可能比  x 小 (由右网左移动)起始坐标取其中较小值,xEnd - x 取其绝对值, 同样处理y
					image = GraphicsUtils.getScreenImage(Math.min(x, xEnd), Math.min(y, yEnd), Math.abs(xEnd - x), Math.abs(yEnd - y));
				} catch (AWTException e1) {
					e1.printStackTrace();
				} catch (InterruptedException e1) {
					e1.printStackTrace();
				}
				
				//为了查看截图效果,将区域截图的部分代替全屏的截图展示
				//imageLabel.setIcon(new ImageIcon(image));
				
				//将所截得的图像,加载到目标组件targetLabel
				targetLabel.setIcon(new ImageIcon(image));
				
				//退出该ScreenWindow
				ScreenWindow.this.dispose();
			}
		});
		
		imageLabel.addMouseMotionListener(new MouseMotionListener() {
			public void mouseDragged(MouseEvent e) {
				//1. 取得鼠标的按下点和移动当前点坐标
				xEnd = e.getX();
				yEnd = e.getY();
				
				//2. 创建一个缓冲图形对象(BufferedImage) bi
				BufferedImage bi = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_RGB);
				Graphics2D g2d = (Graphics2D)bi.getGraphics();
				//3. 将原始图形画到 bi 中
				g2d.drawImage(image, 0, 0, null);	
				g2d.setColor(Color.RED);			//设置画笔颜色为红色
				//4. 根据取得的坐标画一个矩形到 bi 中
				//以鼠标按下点坐标和鼠标拖动的当前点坐标画矩形,作为截图区域的展示
				//+1 与 -1 是为了防止截图时将矩形框也截进去
				g2d.drawRect(Math.min(x, xEnd) - 1, Math.min(y, yEnd) - 1, Math.abs(xEnd - x) + 1, Math.abs(yEnd - y) + 1);	
				g2d.dispose();
				
				//5. 将 bi 画到屏幕上
				Graphics g = imageLabel.getGraphics();
				g.drawImage(bi, 0, 0, null);
				g.dispose();
			}
		
			public void mouseMoved(MouseEvent e) {
				
			}
		});
	}
	
//	public static void main(String[] args) throws AWTException, InterruptedException {
//		new ScreenWindow();
//	}
}

class GraphicsUtils {
	/**
     * 截图屏幕中制定区域的图片
     * @param x
     * @param y
     * @param w
     * @param h
     * @return 被截部分的BufferedImage对象
     * @throws AWTException
     * @throws InterruptedException
     */
    public static BufferedImage getScreenImage(int x, int y, int w, int h) throws AWTException, InterruptedException {
		Robot robot = new Robot();
		BufferedImage screen = robot.createScreenCapture(new Rectangle(x, y, w, h));
		return screen;
	}
}

 主程序 SanpShoot 完整代码:

import java.awt.AWTException;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;

/**
 * 屏幕截图小程序
 * @author pengranxiang
 *
 */
public class SnapShoot extends JFrame {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private JButton snapButton;
	private JLabel imageLabel;
	
	public SnapShoot() {
		initUI();
		initLayout();
		createAction();
		
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.setSize(600, 400);
		this.setTitle("截图小工具");
		this.setLocationRelativeTo(null);	//居中
		this.setVisible(true);
	}
	
	private void initUI() {
		snapButton = new JButton("开始截图(点右键退出)");
		imageLabel = new JLabel();
	}
	
	private void initLayout() {
		JPanel pane = new JPanel();
		pane.add(imageLabel);
		JScrollPane imgScrollPane = new JScrollPane(pane);
		
		Container container = this.getContentPane();
		GroupLayout layout = new GroupLayout(container);
		container.setLayout(layout);
		
		layout.setAutoCreateContainerGaps(true);
		layout.setAutoCreateGaps(true);
		
		GroupLayout.ParallelGroup hGroup = layout.createParallelGroup();
		hGroup
			.addComponent(snapButton)
			.addComponent(imgScrollPane);
		layout.setHorizontalGroup(hGroup);
		
		GroupLayout.SequentialGroup vGroup = layout.createSequentialGroup();
		vGroup
			.addComponent(snapButton)
			.addComponent(imgScrollPane);
		layout.setVerticalGroup(vGroup);
	}
	
	private void createAction() {
		snapButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				try {
					//开启模拟屏幕,将显示截图的目标组件传入
					new ScreenWindow(imageLabel);
				} catch (AWTException e1) {
					e1.printStackTrace();
				} catch (InterruptedException e1) {
					e1.printStackTrace();
				}
			}
		});
	}

	public static void main(String[] args) {
		new SnapShoot();
	}
}

 

以上,就完成了软件中,全屏,区域截图的功能。

 

小软件还有其他四个功能,明天继续。。。。O(∩_∩)O~

   发表时间:2011-02-25  
支持这样的文章,如果能配上图就好了。
0 请登录后投票
   发表时间:2011-02-27  
支持支持,我才用qt实现了类qq的截图,正想再用swing实现,好好学习下了~~~
0 请登录后投票
   发表时间:2011-03-01  
楼主单独看这个截图软件的第2部分是不是有2处小bug,
1.楼主做了一个右键单击退出的操作,但是如果程序运行后我不点击截图button,那么ScreenWindow类就没有初始化,那么基于ScreenWindow的imageLabel的鼠标事件就是不成立的,再者我点击截图后实际上显示的就是targetLabel,此时的事件都是基于targetLabel的,这时候imageLabel的鼠标事件也不是成立的。
2.执行imageLabel的截图坐标处如果我截图只单击一下鼠标,则要么是w,要么是h肯定有一个是没有值的,这个应该给一个初始值吧。
0 请登录后投票
   发表时间:2011-03-02  
lancelotly 写道
楼主单独看这个截图软件的第2部分是不是有2处小bug,
1.楼主做了一个右键单击退出的操作,但是如果程序运行后我不点击截图button,那么ScreenWindow类就没有初始化,那么基于ScreenWindow的imageLabel的鼠标事件就是不成立的,再者我点击截图后实际上显示的就是targetLabel,此时的事件都是基于targetLabel的,这时候imageLabel的鼠标事件也不是成立的。
2.执行imageLabel的截图坐标处如果我截图只单击一下鼠标,则要么是w,要么是h肯定有一个是没有值的,这个应该给一个初始值吧。


关于第一点:
程序运行后,不点击截图Button,那么截图用的模拟屏幕不会出现, ScreenWindow不会要初始化,也不会有ScreenWindow中 imageLabel 的鼠标事件需要响应。

点击截图后,显示的是 模拟屏幕 ScreenWindow中的 imageLabel , 不是targetLabel,
targetLabel  只为 SnapShoot 的 imageLabel 设置显示图片用。

截图完毕后,模拟屏幕 ScreenWindow 会释放,此时 ScreenWindow 的 imageLabel 也不会有需要响应的事件了。 而 SnapShoot 的 imageLabel 已经通过 ScreenWindow 的targetLabel 设置了,需要显示的图片。是可以响应事件的。

所以第一点,我没有测试出什么问题。 可能是我没理解清楚吧。

关于第二点:
的确存在这个问题,可以在截图方法中判断,确保截图的宽和高 > 0
/**
 * 截图屏幕中制定区域的图片
 * @param x
 * @param y
 * @param w
 * @param h
 * @return 被截部分的BufferedImage对象
 * @throws AWTException
 * @throws InterruptedException
 */
public static BufferedImage getScreenImage(int x, int y, int w, int h) throws AWTException, InterruptedException {
	Robot robot = new Robot();
	w = w > 0 ? w : 1;
	h = h > 0 ? h : 1;
	BufferedImage screen = robot.createScreenCapture(new Rectangle(x, y, w, h));
	return screen;
}
0 请登录后投票
   发表时间:2011-03-15  
写的挺好的,就是有一点不足,文件保存的格式没有
[code language="Java"]
file = chooser.getSelectedFile();
System.out.println(file.getAbsolutePath());
file = new File(file.getAbsoluteFile()+".jpg");
0 请登录后投票
   发表时间:2011-05-13  
我写了一下,怎么就不能运行的啊,
0 请登录后投票
   发表时间:2011-05-13  
我检查了一下我写的代码,没有错啊,
0 请登录后投票
   发表时间:2011-05-13  
呵呵,是我的错啊,最后居然忘记调事件了。晕
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics