这是第一次写技术博客,还是有点不知所措,前些时听人说技术这东西,不仅要自己会,还要能够说出来,让别人明白你在说什么。博主暂时是学生,本身还没什么实际企业项目开发经验,这篇文章权当学习经历的总结。
好,言归正传。这个案例我会一步一步的来拆分讲解。
-----------------------------------------------------------我是分界线-------------------------------------------------------------
涉及到的知识点:
软件说明:可以进行局部的截屏。
实现大概原理:
1.首先获取整个屏幕的图像内容,类似于全屏截图,但是不用去保存,而是将图像捕获到;
2.然后在捕获的图像上,再去截取所需的图像,保存下来。
我们先完成第一步,先把这个软件的界面制作出来。如图下图:
虽然界面很简单,但是简单易用可依赖性强,界面太复杂反而不容易使用。
首先在main函数里面创建一个相框【JFrame】,接着创建桌布【JPanel】(我一直这么喊它= =什么东西都可以往上面放,这真的像桌布),就是整个界面,然后创建标签【JLabel】,将每个标签打上字,然后贴在桌布上,载将桌布嵌入相框。设置其界面大小等一类属性。代码如下:
<span style="font-size:14px;"> JFrame frame = new JFrame("截屏");
JPanel a = new JPanel();
JLabel b = new JLabel("Ctrl+Alt截图");
JLabel c = new JLabel("Esc退出截屏界面");
JLabel d = new JLabel("East_MrChiu制作");
a.add(b);
a.add(c);
a.add(d);
frame.add(a);
frame.setSize(140, 110);
frame.setVisible(true);
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.setResizable(false);//窗体大小不可变
</span>
这样我们得到了一个软件界面。接下来,我们要实现捕获全屏的图像,将其作为截屏的界面。接着我们定义按键Ctrl+Alt触发事件,那么需要创建一个监听管理器【KeyboardFocusManager】,然后在监听管理器中添加监听的进程,接着判断是否是否触发Ctrl+Alt按键。注意这里需要捕获异常。代码如下:
<span style="font-size:14px;">// 同时按下CTRL+ALT按键事件
KeyboardFocusManager manager = KeyboardFocusManager
.getCurrentKeyboardFocusManager();// 获得监听管理器
manager.addKeyEventPostProcessor(new KeyEventPostProcessor() {// 添加事件监听进程
@Override
public boolean postProcessKeyEvent(KeyEvent e) {
// 判断是否同时按下CTRL+ALT,若是,则进行截图
if (e.isAltDown() && e.isControlDown()) {
try {
setVisible(false);//设置窗体不可见
new ScreenWindow();//开始下一步的截屏处理
} catch (Exception e2) {
JOptionPane.showConfirmDialog(null, "出现意外错误!", "系统提示",
JOptionPane.DEFAULT_OPTION,
JOptionPane.ERROR_MESSAGE);
}
}
return false;
}
});
</span>
接下来继续实现接触处理,完成
<span style="font-size:14px;">new ScreenWindow();</span>
这一步,我们创建一个ScreenWindow的类,继承JFrame,创建ScreenWindow函数。在进行下一步前,先回顾几个东西:
1.Dimension类封装单个对象中组件的宽度和高度(精确到整数)。该类与组件的某个属性关联。由 Component
类和 LayoutManager
接口定义的一些方法将返回Dimension对象。通常,width
和 height
的值是非负整数。允许创建Dimension的构造方法不会阻止您为这些属性设置负值。如果 width
或 height
的值为负,则由其他对象定义的一些方法的行为是不明确的。
2.ImageIcon:一个 Icon 接口的实现,它根据 Image 绘制 Icon。可使用 MediaTracker 预载根据 URL、文件名或字节数组创建的图像,以监视该图像的加载状态。
声明变量:
<span style="font-size:14px;"> public Dimension screenDims = null;// 屏幕大小
public ImageIcon screenImageIcon = null;// 截取的图片
public JLabel label = null;
private boolean isDrag = false;
private int x = 0;
private int y = 0;
private int xEnd = 0;
private int yEnd = 0;
</span>
捕获全屏图像:
// 捕获全屏
screenDims = Toolkit.getDefaultToolkit().getScreenSize();// 获取整体屏幕大小内容
screenImageIcon = new ImageIcon(<span style="color:#FF0000;">ScreenImage</span>.getScreenImage(0, 0,
screenDims.width, screenDims.height));// 接受捕获的全屏图像
label = new JLabel(screenImageIcon);// 将接受来的全屏图像放到标签上,以便接下来的操作
label.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));// 设置在全屏截图图像上移动时的鼠标光标样式(这里设置成十字架样式)
其中的一个方法类ScreenImage需要定义:
static// 截图
class <span style="color:#FF0000;">ScreenImage</span> {
public static Image getScreenImage(int i, int j, int width, int height)
throws AWTException, InterruptedException {
Robot robot = new Robot();
Image screen = robot.createScreenCapture(
new Rectangle(i, j, width, height)).getScaledInstance(
width, height, Image.SCALE_SMOOTH);
MediaTracker tracker = new MediaTracker(new Label());
tracker.addImage(screen, 1);
tracker.waitForID(0);
return screen;
}
}
然后在捕获的全屏图像上进行分割截取所需局部图像,首先添加鼠标点击事件:分别是左键取消事件,按下事件,松开事件这三个事件。按下事件获取落点坐标,松开事件获取松点坐标,其中要松点坐标必须大于落点坐标,如果小于,则进行交换。当松开事件完成后,立刻弹出文件选择框,进行文件保存。具体先看代码:
// 添加鼠标点击事件
label.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON3) {
dispose();
}
}
public void mousePressed(MouseEvent e) {
x = e.getX();
y = e.getY();
}
public void mouseReleased(MouseEvent e) {
if (isDrag) {
xEnd = e.getX();
yEnd = e.getY();
if (x > xEnd) {
int temp = x;
x = xEnd;
xEnd = temp;
}
if (y > yEnd) {
int temp = y;
y = yEnd;
yEnd = temp;
}
// 保存文件
try {
JFileChooser fileChooser = new JFileChooser();// 构造一个文件选择器
// 设置主目录
// 1.固件存储位置
// fileChooser.setCurrentDirectory(new File("D:"));
// 2.弹出保存文件的选择框
fileChooser.setFileFilter(new FileFilter() {
public boolean accept(File f) {
return f.isDirectory() || f.getName().toLowerCase().endsWith(".jpg");
}
public String getDescription() {
return "*.jpg";
}
});
int result = fileChooser.showSaveDialog(null);
if (result == JFileChooser.APPROVE_OPTION) {
/*
* 获取局部截屏图像
*/
// 创建一个等大的图片框
BufferedImage bufferedImage = new BufferedImage(
xEnd - x, yEnd - x,
BufferedImage.TYPE_INT_BGR);
// 获得局部截屏图像
Image image = ScreenImage.getScreenImage(x, y, xEnd
- x, yEnd - y);
// 将图片框绑定一只画笔g,将image画在bufferedImage上
Graphics g = bufferedImage.getGraphics();
g.drawImage(image, 0, 0, xEnd - x, yEnd - y, null);
// 画出来的图片上会存有绿色选框,现在将绿色选框消除
g.setColor(Color.white);
g.drawRect(0, 0, xEnd - x, yEnd - y);
/*
* 文件写入磁盘
*/
File selectFile = fileChooser.getSelectedFile();
String name = selectFile.getName();
if (!name.endsWith("jpg")) {
String path = selectFile.getAbsolutePath();
selectFile = new File(path + ".jpg");
for (int i = 0; selectFile.exists(); i++) {
selectFile = new File(path + "(" + i + ").jpg");
}
}
// 输出图片
ImageIO.write(bufferedImage, "jpg", selectFile);
dispose();
} else {
label.setIcon(screenImageIcon);
}
} catch (Exception e2) {
e2.printStackTrace();
JOptionPane.showConfirmDialog(null, "出现意外错误!", "系统提示",
JOptionPane.DEFAULT_OPTION,
JOptionPane.ERROR_MESSAGE);
}
}
}
});
为了显示截图的预览效果,我们一般显示一个截图框:
// 鼠标拖拽事件
label.addMouseMotionListener(new MouseMotionListener() {
@Override
public void mouseDragged(MouseEvent e) {
if (!isDrag) {
isDrag = true;
}
// 拖动过程的虚线选框实现
int endx = e.getX();
int endy = e.getY();
BufferedImage bufferedImage = new BufferedImage(
screenDims.width, screenDims.height,
BufferedImage.TYPE_INT_BGR);
Graphics g = bufferedImage.getGraphics();
g.drawImage(screenImageIcon.getImage(), 0, 0, screenDims.width,
screenDims.height, null);
g.setColor(Color.green);
g.drawRect(x, y, endx - x, endy - y);
label.setIcon(new ImageIcon(bufferedImage));
}
@Override
public void mouseMoved(MouseEvent e) {
}
});
此外还有一个细节问题,通过上面大家都知道,我们是通过先全屏截图,捕获全屏图像,然后在这个全屏图像上进行矩形分割得到我们所需的局部图像,那么在全屏截取时得到的图像会出现边框,这样影响二次分割图像,我们需要对其边框的属性进行设置。代码如下:
this.setUndecorated(true);// 将捕获的全屏图像的边框消除
/*
* this就是默认你调用所定义类的实例化对象 this.getContentPane()的作用是初始化一个容器,用来在容器上添加一些控件
* 下面这句中,可以分开理解为: Container c = this.getContentPane();//初始化一个容器
* c.add(label);//在容器上添加控件
*/
this.getContentPane().add(label);// 将全屏图像标签添加到容器上
this.setSize(screenDims.width, screenDims.height);
this.setVisible(true);
/*
* 设置窗口状态 参数含义 NORMAL 默认状态 ICONIFIED 最小化 MAXIMIZED_HORIZ 水平方向最大化
* MAXIMIZED_VERT 垂直方向最大化 MAXIMIZED_BOTH 水平方向和数值方向都最大化
*/
this.setExtendedState(JFrame.MAXIMIZED_HORIZ);
到这里后,整体项目就完成了。下面我把我的整个代码块附在下面,水平有限,如果有大家发现什么问题,或者觉得我哪里讲的不好,没有讲清楚,希望在下面留言板中提出,我会一一作答并吸取经验,谢谢!
1.JframeShow.java:
package SCR;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class JframeShow extends JFrame {
private static final long serialVersionUID = -4767500078802197544L;
public JframeShow() {
JFrame frame = new JFrame("截屏");
JPanel a = new JPanel();
JLabel b = new JLabel("Ctrl+Alt截图");
JLabel c = new JLabel("Esc退出截屏软件");
JLabel d = new JLabel("East_MrChiu制作");
a.add(b);
a.add(c);
a.add(d);
frame.add(a);
frame.setSize(160, 100);
frame.setVisible(true);
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.setResizable(false);// 窗体大小不可变
}
}
2.ScreenWindow.java:
package SCR;
import java.awt.AWTException;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Label;
import java.awt.MediaTracker;
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 java.io.File;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.filechooser.FileFilter;
public class ScreenWindow extends JFrame {
private static final long serialVersionUID = -2214091416124833614L;
public Dimension screenDims = null;// 屏幕大小
public ImageIcon screenImageIcon = null;// 截取的图片
public JLabel label = null;
private boolean isDrag = false;
private int x = 0;
private int y = 0;
private int xEnd = 0;
private int yEnd = 0;
public ScreenWindow() throws AWTException,
InterruptedException {
// 捕获全屏
screenDims = Toolkit.getDefaultToolkit().getScreenSize();// 获取整体屏幕大小内容
screenImageIcon = new ImageIcon(ScreenImage.getScreenImage(0, 0,
screenDims.width, screenDims.height));// 接受捕获的全屏图像
label = new JLabel(screenImageIcon);// 将接受来的全屏图像放到标签上,以便接下来的操作
label.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));// 设置在全屏截图图像上移动时的鼠标光标样式(这里设置成十字架样式)
// 添加鼠标点击事件
label.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON3) {
dispose();
}
}
public void mousePressed(MouseEvent e) {
x = e.getX();
y = e.getY();
}
public void mouseReleased(MouseEvent e) {
if (isDrag) {
xEnd = e.getX();
yEnd = e.getY();
if (x > xEnd) {
int temp = x;
x = xEnd;
xEnd = temp;
}
if (y > yEnd) {
int temp = y;
y = yEnd;
yEnd = temp;
}
// 保存文件
try {
JFileChooser fileChooser = new JFileChooser();// 构造一个文件选择器
// 设置主目录
// 1.固件存储位置
// fileChooser.setCurrentDirectory(new File("D:"));
// 2.弹出保存文件的选择框
fileChooser.setFileFilter(new FileFilter() {
public boolean accept(File f) {
return f.isDirectory() || f.getName().toLowerCase().endsWith(".jpg");
}
public String getDescription() {
return "*.jpg";
}
});
int result = fileChooser.showSaveDialog(null);
if (result == JFileChooser.APPROVE_OPTION) {
/*
* 获取局部截屏图像
*/
// 创建一个等大的图片框
BufferedImage bufferedImage = new BufferedImage(
xEnd - x, yEnd - x,
BufferedImage.TYPE_INT_BGR);
// 获得局部截屏图像
Image image = ScreenImage.getScreenImage(x, y, xEnd
- x, yEnd - y);
// 将图片框绑定一只画笔g,将image画在bufferedImage上
Graphics g = bufferedImage.getGraphics();
g.drawImage(image, 0, 0, xEnd - x, yEnd - y, null);
// 画出来的图片上会存有绿色选框,现在将绿色选框消除
g.setColor(Color.white);
g.drawRect(0, 0, xEnd - x, yEnd - y);
/*
* 文件写入磁盘
*/
File selectFile = fileChooser.getSelectedFile();
String name = selectFile.getName();
if (!name.endsWith("jpg")) {
String path = selectFile.getAbsolutePath();
selectFile = new File(path + ".jpg");
for (int i = 0; selectFile.exists(); i++) {
selectFile = new File(path + "(" + i + ").jpg");
}
}
// 输出图片
ImageIO.write(bufferedImage, "jpg", selectFile);
dispose();
} else {
label.setIcon(screenImageIcon);
}
} catch (Exception e2) {
e2.printStackTrace();
JOptionPane.showConfirmDialog(null, "出现意外错误!", "系统提示",
JOptionPane.DEFAULT_OPTION,
JOptionPane.ERROR_MESSAGE);
}
}
}
});
// 鼠标拖拽事件
label.addMouseMotionListener(new MouseMotionListener() {
@Override
public void mouseDragged(MouseEvent e) {
if (!isDrag) {
isDrag = true;
}
// 拖动过程的虚线选框实现
int endx = e.getX();
int endy = e.getY();
BufferedImage bufferedImage = new BufferedImage(
screenDims.width, screenDims.height,
BufferedImage.TYPE_INT_BGR);
Graphics g = bufferedImage.getGraphics();
g.drawImage(screenImageIcon.getImage(), 0, 0, screenDims.width,
screenDims.height, null);
g.setColor(Color.green);
g.drawRect(x, y, endx - x, endy - y);
label.setIcon(new ImageIcon(bufferedImage));
}
@Override
public void mouseMoved(MouseEvent e) {
}
});
this.setUndecorated(true);// 将捕获的全屏图像的边框消除
/*
* this就是默认你调用所定义类的实例化对象 this.getContentPane()的作用是初始化一个容器,用来在容器上添加一些控件
* 下面这句中,可以分开理解为: Container c = this.getContentPane();//初始化一个容器
* c.add(label);//在容器上添加控件
*/
this.getContentPane().add(label);// 将全屏图像标签添加到容器上
this.setSize(screenDims.width, screenDims.height);
this.setVisible(true);
/*
* 设置窗口状态 参数含义 NORMAL 默认状态 ICONIFIED 最小化 MAXIMIZED_HORIZ 水平方向最大化
* MAXIMIZED_VERT 垂直方向最大化 MAXIMIZED_BOTH 水平方向和数值方向都最大化
*/
this.setExtendedState(JFrame.MAXIMIZED_HORIZ);
}
static// 截图
class ScreenImage {
public static Image getScreenImage(int i, int j, int width, int height)
throws AWTException, InterruptedException {
Robot robot = new Robot();
Image screen = robot.createScreenCapture(
new Rectangle(i, j, width, height)).getScaledInstance(
width, height, Image.SCALE_SMOOTH);
MediaTracker tracker = new MediaTracker(new Label());
tracker.addImage(screen, 1);
tracker.waitForID(0);
return screen;
}
}
}
3.CopyScreen.java:
package SCR;
import java.awt.KeyEventPostProcessor;
import java.awt.KeyboardFocusManager;
import java.awt.event.KeyEvent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
public class CopyScreen extends JFrame {// 继承JFrame
private static final long serialVersionUID = -394216642885173478L;
/*
* 先将整个当前屏幕内容整体截取出来
*/
public CopyScreen() {
new JframeShow();
KeyboardFocusManager manager = KeyboardFocusManager
.getCurrentKeyboardFocusManager();// 获得监听管理器
// 同时按下CTRL+ALT按键事件
manager.addKeyEventPostProcessor(new KeyEventPostProcessor() {// 添加事件监听进程
@Override
public boolean postProcessKeyEvent(KeyEvent e) {
// 判断是否同时按下CTRL+ALT,若是,则进行截图
if (e.isAltDown() && e.isControlDown()) {
try {
setVisible(false);// 设置窗体不可见
new ScreenWindow();// 开始下一步的截屏处理
} catch (Exception e2) {
JOptionPane.showConfirmDialog(null, "出现意外错误!", "系统提示",
JOptionPane.DEFAULT_OPTION,
JOptionPane.ERROR_MESSAGE);
}
}
return false;
}
});
// 按键ESC退出事件
manager.addKeyEventPostProcessor(new KeyEventPostProcessor() {
@Override
public boolean postProcessKeyEvent(KeyEvent e) {
// 判断是否按下ESC按键
if (KeyEvent.VK_ESCAPE == e.getKeyCode()) {
System.exit(0);// 退出程序
}
return false;
}
});
}
public static void main(String[] args) {
new CopyScreen();
}
}