Swing 实现截图小软件 (三)
前面两节完成了,截图软件的基本功能:全屏,区域截图 功能
本节实现:涂鸦
, 画线 ,
画圈 和
保存
第一步:实现涂鸦功能。
涂鸦也就是说:使用鼠标随意的拖动,去绘制随意的线条。那么就只需要在主程序的展示截图的JLabel中添加鼠标拖动监听。在鼠标每个移动点是绘制“一个点”,这样就可以按照鼠标的移动轨迹,来绘制任意的线条了。
按照上面的思路给出代码:SnapShoot.java
import java.awt.AWTException;
import java.awt.Color;
import java.awt.Container;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import javax.swing.GroupLayout;
import javax.swing.ImageIcon;
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;
private int x, y; //记录鼠标坐标
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();
}
}
});
imageLabel.addMouseMotionListener(new MouseMotionListener() {
public void mouseDragged(MouseEvent e) {
x = e.getX();
y = e.getY();
//鼠标移动时,在imageLabel展示的图像中,绘制点
//1. 取得imageLabel中的图像
Image img = ((ImageIcon)imageLabel.getIcon()).getImage();
//2. 创建一个缓冲图形对象 bi
BufferedImage bi = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = (Graphics2D) bi.getGraphics();
//3. 将截图的原始图像画到 bi
g2d.drawImage(img, 0, 0, null);
//4. 在鼠标所在的点,画一个点
g2d.setColor(Color.RED); //设置画笔颜色为红色
g2d.drawLine(x, y, x, y); //Java中没有提供点的绘制,使用起点和终点为同一个点的画线代替
g2d.dispose();
//5. 为了保留每一个点,不能直接使用imageLabel.getGraphics()来画,
//需要使用imageLabel.setIcon()来直接将画了点的图像,设置到imageLabel中,
//这样,在第一步中,取得img时,就为已经划过上一个点的图像了。
imageLabel.setIcon(new ImageIcon(bi));
}
public void mouseMoved(MouseEvent e) {
}
});
}
public static void main(String[] args) {
new SnapShoot();
}
}
运行该代码,发现在涂鸦时,跟踪鼠标画的点,太过分散,不连续。鼠标移动越快,点就越不连续。如图:
可见,在监听鼠标移动时,画点的速度,跟不上鼠标移动的速度。又图想到,如果把上面的散点全部连接起来,则可以构成一条平滑的线了。
于是:改变思路,由画点,改为画线。将鼠标移动监听到的每一个点,都首尾连接的画线。
节约篇幅:展示修改的部分代码,在createActon()中
imageLabel.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
//鼠标按下的点,作为画线的最初的起点
x = e.getX();
y = e.getY();
}
});
imageLabel.addMouseMotionListener(new MouseMotionListener() {
public void mouseDragged(MouseEvent e) {
xEnd = e.getX();
yEnd = e.getY();
//鼠标移动时,在imageLabel展示的图像中,绘制点
//1. 取得imageLabel中的图像
Image img = ((ImageIcon)imageLabel.getIcon()).getImage();
//2. 创建一个缓冲图形对象 bi
BufferedImage bi = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = (Graphics2D) bi.getGraphics();
//3. 将截图的原始图像画到 bi
g2d.drawImage(img, 0, 0, null);
//4. 在鼠标所在的点,画一个点
g2d.setColor(Color.RED); //设置画笔颜色为红色
g2d.drawLine(x, y, xEnd, yEnd); //将画点,改为画线
g2d.dispose();
//5. 为了保留每一个点,不能直接使用imageLabel.getGraphics()来画,
//需要使用imageLabel.setIcon()来直接将画了点的图像,设置到imageLabel中,
//这样,在第一步中,取得img时,就为已经划过上一个点的图像了。
imageLabel.setIcon(new ImageIcon(bi));
//下次画线起点是设置为这次画线的终点
x = xEnd;
y = yEnd;
}
public void mouseMoved(MouseEvent e) {
}
});
改变思路后,涂鸦的线条效果,就好多了。如图:
第二步:实现画线功能。
画线的话,与涂鸦有点不同。 涂鸦是根据鼠标移动实时画线。 而画线功能要求是:从鼠标按下, 到鼠标弹起的两点之间画一条线段。鼠标移动时,只是展示最后效果,并不直接画上去。 鼠标弹起后,才真的画上去。
同时因为,有了连个功能,我们给个功能开关,是要涂鸦,还是要画线。
代码片段:createAction() 中
imageLabel.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
//鼠标按下的点,作为画线的最初的起点
x = e.getX();
y = e.getY();
}
public void mouseReleased(MouseEvent e) {
if(isLine) {
//该方法只用作画线时处理
//鼠标弹起时需要将最后定为的图像 bi,调用imageLabel.setIcon()方法,设置进去。
//这样就可以将线段真的画进去了。为了使用变量 bi 将其转为 该类的private变量
imageLabel.setIcon(new ImageIcon(bi));
}
}
});
imageLabel.addMouseMotionListener(new MouseMotionListener() {
public void mouseDragged(MouseEvent e) {
if(isDoodle) { //涂鸦开关
xEnd = e.getX();
yEnd = e.getY();
//鼠标移动时,在imageLabel展示的图像中,绘制点
//1. 取得imageLabel中的图像
Image img = ((ImageIcon)imageLabel.getIcon()).getImage();
//2. 创建一个缓冲图形对象 bi
bi = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = (Graphics2D) bi.getGraphics();
//3. 将截图的原始图像画到 bi
g2d.drawImage(img, 0, 0, null);
//4. 在鼠标所在的点,画一个点
g2d.setColor(Color.RED); //设置画笔颜色为红色
g2d.drawLine(x, y, xEnd, yEnd); //Java中没有提供点的绘制,使用起点和终点为同一个点的画线代替
g2d.dispose();
//5. 为了保留每一个点,不能直接使用imageLabel.getGraphics()来画,
//需要使用imageLabel.setIcon()来直接将画了点的图像,设置到imageLabel中,
//这样,在第一步中,取得img时,就为已经划过上一个点的图像了。
imageLabel.setIcon(new ImageIcon(bi));
//下次画线起点是设置为这次画线的终点
x = xEnd;
y = yEnd;
}
if(isLine) { //画线开关
xEnd = e.getX();
yEnd = e.getY();
//鼠标移动时,在imageLabel展示的图像中,绘制点
//1. 取得imageLabel中的图像
Image img = ((ImageIcon)imageLabel.getIcon()).getImage();
//2. 创建一个缓冲图形对象 bi
bi = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = (Graphics2D) bi.getGraphics();
//3. 将截图的原始图像画到 bi
g2d.drawImage(img, 0, 0, null);
//4. 在鼠标所在的点,画一个点
g2d.setColor(Color.RED); //设置画笔颜色为红色
g2d.drawLine(x, y, xEnd, yEnd); //Java中没有提供点的绘制,使用起点和终点为同一个点的画线代替
g2d.dispose();
//5. 不需要保留在鼠标拖动过程中所画的线段,所以直接使用imageLabel.getGraphics()绘制
//这样imageLabel.getIcon()并没有被改变,所以每次都只到原始截图和多了一条线,即为最后效果的演示
Graphics g = imageLabel.getGraphics();
g.drawImage(bi, 0, 0, null); //将处理后的图片,画到imageLabel
g.dispose();
}
}
运行情况如图:
完整代码,见附件:SnapShoot1.jar 源码在 jar 包中。
第三步:实现画圈功能。
画圈的功能实现与画线就很想了, 只是修改一下,drawLine -- > drawOval
代码片段:
if(isLine) {
g2d.drawLine(x, y, xEnd, yEnd); //Java中没有提供点的绘制,使用起点和终点为同一个点的画线代替
}
if(isCircle) {
//因为如果鼠标向上,或向左移动时,xEnd > x, yEnd > y ,所以画圆的起点要取两者中的较小的,
//而宽度和高度是不能 < 0 的,所以取绝对值
g2d.drawOval(Math.min(x, xEnd), Math.min(y, yEnd), Math.abs(xEnd - x), Math.abs(yEnd - y));
}
运行如图:
完成代码见附件:SnapShoot2.jar 源码在 jar 包中。
第四步: 为了然该小软件有点用处,光截图,涂鸦之类的,只能看不能用。所以现在就加入 保存 功能吧。
代码片段:
chooser = new JFileChooser();
FileNameExtensionFilter filter = new FileNameExtensionFilter("JPG & PNG Images", "jpg", "png");
chooser.setFileFilter(filter);
saveButton = new JButton("保存");
saveButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if(imageLabel.getIcon() == null) {
JOptionPane.showMessageDialog(SnapShoot.this, "没有图片信息,请先截图", "提示", JOptionPane.INFORMATION_MESSAGE);
return;
}
File file = null;
int returnVal = chooser.showOpenDialog(SnapShoot.this);
if(returnVal == JFileChooser.APPROVE_OPTION) {
file = chooser.getSelectedFile();
}
//取得imageLabel中的图像
Image img = ((ImageIcon)imageLabel.getIcon()).getImage();
try {
if(file != null) {
ImageIO.write((BufferedImage)img, "png", file);
JOptionPane.showMessageDialog(SnapShoot.this, "保存成功", "提示", JOptionPane.INFORMATION_MESSAGE);
}
} catch (IOException e1) {
e1.printStackTrace();
}
}
});
运行如图:
完成代码见附件:SnapShoot3.jar 源码在 jar 包中
最后,界面是丑了点啦。。。
想要稍微让界面好看点,可以修改 LookAndFeel 为本地皮肤,修改 main()方法
public static void main(String[] args) {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
JFrame.setDefaultLookAndFeelDecorated(true);
} catch (Exception e) {
System.out.println("Error setting native LAF: " + e);
}
new SnapShoot();
}
如图:
完成代码见附件:SnapShoot.jar 源码在 jar 包中
到此,该小软件还剩下,水印功能。 明天继续。。。O(∩_∩)O~
推荐阅读
-
微信小程序 ecshop地址三级联动实现实例代码
-
小程序和web画三角形实现解析
-
Python实现的二维码生成小软件
-
“软件工程(C编码实践篇)”实验报告【实验五:用callback增强链表模块来实现命令行菜单小程序V2.8】
-
“软件工程(C编码实践篇)”实验报告【实验三:内部模块化的命令行菜单小程序V2.0】
-
HTML学习三天之后结合CSS实现网页头部以及尾部的小练习
-
ESP32 开发笔记(三)源码示例 11_IIC_AT24C02 使用IIC总线实现EEPROM小容量数据储存测试
-
Photoscan扫描软件怎么通过实物图实现三维重建?
-
如何将多张图片进行合并?金舟截图软件来实现!
-
微信小程序实现省市区三级地址选择