在此之前,我们已经实现项目的第二步,处理了每一张图的颜色,我们让小人的颜色是黑色,背景整体调整为白色。本小节要做的就是在这些图片上进行绘画火柴人。因为我们绘画水平有限,我们希望在原图片的轮廓上进行绘画。
一、swing组建
我们要想在已有的图片上进行火柴人绘画,首先得有绘画的图形界面。可以使用java中的swing组建来实现。
谈到Java的图形界面程程,指的就是GUI(图形用户界面)。Graphical User Interface,用图形的方式,来显示计算机操作的界面。
Java为GUI提供的对象都在java.awt和javax.swing两个包中。
- java.awt,Abstract Window ToolKit(抽象窗口工具包),需要调用本地系统方法实现功能。属重量级控件。
- javax.swing,在awt的基础上,建立的一套图形界面系统,其中提供了更多的组件,而且完全由Java实现。增强了移植性,属轻量级控件。
相对于AWT而言Swing包中提供了更多的丰富的、快捷的、强大的GUI组件,而且这些组件都是java语言编写而成,因此Swing不依赖本地平台,可以真正做到跨平台运行。通常而言我们把AWT称之为重量级组件,Swing称之为轻量级软件,一般而言Swing组件都是在AWT组件名称前加J。
1、JFrame 窗口
JFrame 用来设计类似于 Windows 系统中窗口形式的界面。JFrame 是 Swing 组件的顶层容器,该类继承了 AWT 的 Frame 类,支持 Swing 体系结构的高级 GUI 属性。
在Swing组件中最常见的就是JFrame,他和Frame一样是一个独立存在的顶级窗口,不能放置在其他容器中,JFrame支持所有窗口的操作,例如窗口最小化,设定窗口大小。
JFrame():构造一个初始时不可见的新窗体。
JFrame(String title):创建一个具有 title 指定标题的不可见新窗体。
示例代码:
package qf.day05.demo;
import java.awt.FlowLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextField;
/**
* 创建图形界面程序的思路:
*
* step1:创建frame窗体,JFrame
*
* step2:对窗体进行基本的设置:
* 大小,位置,布局等等,标题。。。
*
* step3:定义里面的组件:
* 按钮,文本。。。---->JPanel
*
* step4:通过add()方法,将这些组件,添加到窗体中。
*
* step5:让窗体显示:setVisible(true)
*
* @author ruby
*
*/
public class Demo01_JFrame {
public static void main(String[] args) {
//1.创建frame窗体
JFrame frame =new JFrame();
//2.设置窗体
frame.setSize(400, 200);//窗口的大小,像素
frame.setLocation(300, 500);
//frame.setBounds(300, 500, 400, 200);
frame.setTitle("我的第一个GUI程序,哈哈哈哈");//设置标题
frame.setLayout(new FlowLayout());//设置布局
//3.摆放个按钮
JButton button = new JButton("我是一个按钮");
//将这个按钮放在frame上
frame.add(button);
JTextField textField =new JTextField("haha",30);
frame.add(textField);
//设置窗体可见
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//通过点击按钮可以直接关闭程序
}
}
运行结果如下:
2、JPanel 面板
JPanel 是一种中间层容器,它能容纳组件并将组件组合在一起,但它本身必须添加到其他容器中使用。JPanel 类的构造方法如下。
JPanel():使用默认的布局管理器创建新面板,默认的布局管理器为 FlowLayout。
JPanel(LayoutManagerLayout layout):创建指定布局管理器的 JPanel 对象。
示例代码:
package qf.day05.demo;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Demo02_JPanel {
public static void main(String[] args) {
JFrame frame = new JFrame("王二狗的程序");
frame.setLayout(new FlowLayout());
frame.setSize(520,700);
JPanel panel = new JPanel();//创建面板
panel.setPreferredSize(new Dimension(400, 300));//设置面板的宽和高
panel.setBackground(Color.BLUE);
JButton btn1 = new JButton("按钮1");
JButton btn2 = new JButton("按钮2");
JButton btn3 = new JButton("按钮3");
JButton btn4 = new JButton("按钮4");
panel.add(btn1);
panel.add(btn4);
panel.add(btn2);
panel.add(btn3);
frame.add(panel);
JPanel panel2 = new JPanel();
JButton btn5 = new JButton("按鈕5");
JButton btn6 = new JButton("按钮6");
JButton btn7 = new JButton("按钮7");
panel2.add(btn5);
panel2.add(btn6);
panel2.add(btn7);
frame.add(panel2);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
运行结果如下:
3、其他控件
以上都用到了一些具体的,比如JButton等控件。
4、事件监听
事件监听:
事件源:就是awt包或者swing包中的那些图形界面组件
事件:每一个事件源都有自己特有的对应事件和共性事件。
监听器:将可以出发某一个事件的动作都已经封装到了监听器中。
以上这些,在java中都已经定义好了,直接获取对象来用就可以了。我们要做的就是,对产生的动作进行处理
Interface MouseListener
void mouseClicked(MouseEvent e)
在组件上单击(按下并释放)鼠标按钮时调用。
void mouseEntered(MouseEvent e)
当鼠标进入组件时调用。
void mouseExited(MouseEvent e)
当鼠标退出组件时调用。
void mousePressed(MouseEvent e)
在组件上按下鼠标按钮时调用。
void mouseReleased(MouseEvent e)
在组件上释放鼠标按钮时调用。
Interface MouseMotionListener,鼠标动作监听
void mouseDragged(MouseEvent e)
在组件上按下鼠标按钮然后拖动时调用。
void mouseMoved(MouseEvent e)
当鼠标光标移动到组件上但没有按钮被按下时调用。
示例代码:
package qf.day04.demo04;
import java.awt.FlowLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionAdapter;
import javax.swing.JButton;
import javax.swing.JFrame;
public class Demo03_EventListener {
public static void main(String[] args) {
JFrame frame = new JFrame("事件监听");//直接设置标题
//对frame进行基本的设置
frame.setSize(400, 500);
frame.setLocation(300, 100);
frame.setLayout(new FlowLayout());
JButton button = new JButton("按钮");
frame.add(button);
//给组件添加监听:
button.addMouseListener(new MouseAdapter() {
int count = 0;
@Override
public void mouseClicked(MouseEvent e) {
System.out.println("鼠标点击。。"+(count++));
}
});
frame.addMouseMotionListener(new MouseMotionAdapter() {
@Override
public void mouseDragged(MouseEvent e) {
System.out.println("窗体上的鼠标被拖拽了。。"+e.getX()+","+e.getY());
}
});
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
//自己实现,需要实现MouseListener接口中的所有的方法,而MouseAdapter类已经实现了该接口中的所有方法,我们可以直接继承自MouseAdapter,然后只重写自己需要的方法即可。
//class MyButtonImple implements MouseListener{
//
// @Override
// public void mouseClicked(MouseEvent e) {
// System.out.println("鼠标被点击饿了。。");
// }
//
// @Override
// public void mouseEntered(MouseEvent e) {
// // TODO Auto-generated method stub
//
// }
//
// @Override
// public void mouseExited(MouseEvent e) {
// // TODO Auto-generated method stub
//
// }
//
// @Override
// public void mousePressed(MouseEvent e) {
// // TODO Auto-generated method stub
//
// }
//
// @Override
// public void mouseReleased(MouseEvent e) {
// // TODO Auto-generated method stub
//
// }
//
//}
运行结果:
每当鼠标点击一次,count的值就累加1。
二、项目中的绘图界面
好了,通过以上的学习,我们已经大致掌握了如果使用java的swing界面,来创建图解界面程序,我们需要创建出以下的程序界面,并且能够通过事件监听,实现绘画功能。
好了,接下来我们实现项目的第三步代码,新建一个java文件,D3_DrawPic.java,示例代码如下:
package demo;
import java.awt.AWTException;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Robot;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Ellipse2D;
import java.awt.image.BufferedImage;
import java.awt.image.renderable.RenderableImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import org.bytedeco.javacpp.indexer.IntRawIndexer;
public class D3_DrawPic {
public static void main(String[] args) {
//1.创建窗口
JFrame frame = new JFrame();
frame.setLayout(new FlowLayout());
frame.setSize(520,700);
frame.setTitle("王二狗的画板");
//2.创建面板:按钮区面板,绘画区面板
DrawPanel drawPanel = new DrawPanel();
drawPanel.setLayout(null);
drawPanel.setPreferredSize(new Dimension(520, 660));
drawPanel.setBackground(Color.WHITE);
ButtonPanel buttonPanel = new ButtonPanel(drawPanel,frame);
buttonPanel.setLayout(new FlowLayout());
buttonPanel.setPreferredSize(new Dimension(520,40));
// buttonPanel.setBackground(Color.BLUE);
//3.将面板,添加到窗口上
frame.add(buttonPanel);
frame.add(drawPanel);
//4.窗口添加监听
frame.addMouseListener(drawPanel);
frame.addMouseMotionListener(drawPanel);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
//按钮区的画板
class ButtonPanel extends JPanel{
//设置按钮面板的一堆按钮
JButton pencil = new JButton("画笔");
JButton circle = new JButton("圆");
JButton line = new JButton("直线");
JButton eraser = new JButton("橡皮擦");
JButton next = new JButton("下一帧");
JButton retry = new JButton("重画");
JButton save = new JButton("保存");
private DrawPanel drawPanel;
private JFrame frame;
int picture = 0;//图片的编号
public ButtonPanel(DrawPanel drawPanel,JFrame frame){
this.drawPanel = drawPanel;
this.frame = frame;
this.add(pencil);
this.add(circle);
this.add(line);
this.add(eraser);
this.add(next);
this.add(retry);
this.add(save);
//给这些按钮,添加监听。。
pencil.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
System.out.println("普通的绘画。。。");
drawPanel.setType(0);//0表示普通的绘画。。
}
});
//画圆按钮的
circle.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
System.out.println("要画圆了。。");
drawPanel.setType(1);//1表示画圆
}
});
//画直线的按钮
line.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
System.out.println("按钮被点击,表示要画直线了。。");
drawPanel.setType(2);//2表示画直线
}
});
//橡皮擦
eraser.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
System.out.println("擦除。。。");
drawPanel.setType(3);//3表示擦除
}
});
//重画
retry.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
System.out.println("重画这张图。。"+picture);
//思路:打开图片的目录,读取对应的图片数据--->显示到窗口,绘图工具,绘画这张图片
String imagepath = "C:\\Ruby\\萨日朗\\换色\\"+picture+".jpg";
System.out.println(imagepath);
File file = new File(imagepath);
System.out.println(file.exists());
try {
//读取图片到内存中-->BufferedImage
BufferedImage image = ImageIO.read(file);//原始图片的像素:112*184
//放大图片对象
Image image2=image.getScaledInstance(400, 600, Image.SCALE_DEFAULT);//使用默认的图像缩放算法。
//将放大的图片,绘画到面板上
Graphics2D g2 = (Graphics2D) drawPanel.getGraphics();
g2.drawImage(image2, 60, 0, null);
} catch (IOException e1) {
e1.printStackTrace();
}
}
});
//下一帧
next.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
picture++;
System.out.println("下一张。。"+picture);
//思路:打开图片的目录,读取对应的图片数据--->显示到窗口,绘图工具,绘画这张图片
String imagepath = "C:\\Ruby\\萨日朗\\换色\\"+picture+".jpg";
System.out.println(imagepath);
File file = new File(imagepath);
System.out.println(file.exists());
if(!file.exists()){
System.out.println("图片不存在。。"+picture);
return;//结束方法
}
try {
//读取图片到内存中-->BufferedImage
BufferedImage image = ImageIO.read(file);//原始图片的像素:112*184
//放大图片对象
Image image2=image.getScaledInstance(400, 600, Image.SCALE_DEFAULT);//使用默认的图像缩放算法。
//将放大的图片,绘画到面板上
Graphics2D g2 = (Graphics2D) drawPanel.getGraphics();
g2.drawImage(image2, 60, 0, null);
} catch (IOException e1) {
e1.printStackTrace();
}
}
});
//绘画后,进行图片保存
save.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
System.out.println("保存图片。。"+picture);
//捕捉屏幕的某块区域-->保存成图片
//1.图片要保存的路径
String imagePath = "C:\\Ruby\\萨日朗\\绘图\\"+picture+".jpg";
System.out.println(frame.getX()+","+frame.getY());
//从屏幕绘画的区域:4个参数:
//前的两个表示位置,后的两个表示宽度和高度
Rectangle rectangle = new Rectangle(frame.getX()+10,frame.getY()+82,500,600);
//创建包含从屏幕读取的像素的图像。
try {
BufferedImage image = new Robot().createScreenCapture(rectangle);
File file = new File(imagePath);
ImageIO.write(image, "jpg", file);
System.out.println("保存了。。"+file.getAbsolutePath());
} catch (AWTException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
}
});
}
}
//绘画区面板
class DrawPanel extends JPanel implements MouseListener,MouseMotionListener{
//绘画的类型:0代表普通的的的绘画,1表示画圆,2表示画直线,3表示擦除
private int type;
private int xd = 5;//偏移量
private int yd = 80;
int x1, y1; //要绘画的起点
int x2,y2;//要绘画终点
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
int r = 30;//椭圆的半径
@Override
public void mouseClicked(MouseEvent e) {
//当鼠标在绘画区点击一下,就产生一个圆圈
System.out.println(e.getX()+","+e.getY());
Graphics2D g2 = (Graphics2D) this.getGraphics();
if(type == 1){//画圆圈
g2.setColor(Color.RED);//设置绘画工具的颜色
//词义,椭圆
Ellipse2D circle = new Ellipse2D.Double();
int x = e.getX();
int y = e.getY();
circle.setFrameFromCenter(x-xd, y-yd, x+r-xd, y+r-yd);
g2.fill(circle);
}
}
@Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mousePressed(MouseEvent e) {
System.out.println("鼠标按钮的位置。。");
x1 = e.getX();
y1 = e.getY();
// System.out.println(x1+","+y1);
}
@Override
public void mouseReleased(MouseEvent e) {
x2 = e.getX();
y2 = e.getY();
if(type == 2){//绘画直线
Graphics2D g2= (Graphics2D) this.getGraphics();
g2.setColor(Color.RED);//颜色,红色
g2.setStroke(new BasicStroke(15.0f));//画笔的粗细
x1 -= xd;
y1 -= yd;
x2 -= xd;
y2 -= yd;
Ellipse2D circle =new Ellipse2D.Double();
circle.setFrameFromCenter(x1, y1,x1+7.5,y1+7.5);
g2.fill(circle);
g2.drawLine(x1, y1, x2, y2);
circle.setFrameFromCenter(x2, y2,x2+7.5,y2+7.5);
g2.fill(circle);
System.out.println("画直线。。。");
}
}
@Override
public void mouseDragged(MouseEvent e) {
x2 = e.getX();
y2 = e.getY();
Graphics2D g2 = (Graphics2D) this.getGraphics();//绘图工具
if(type == 0 || type == 3){
System.out.println("普通的绘画。。或者擦除");
g2.setStroke(new BasicStroke(10));//设置画笔
if(type == 3){//擦除
System.out.println("擦除。。。");
g2.setColor(Color.WHITE);
}else{
g2.setColor(Color.RED);//设置画笔颜色
}
//使用抗锯齿来进行绘画
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.drawLine(x1-xd, y1-yd, x2-xd, y2-yd);
x1 = x2;
y1 = y2;
}
}
@Override
public void mouseMoved(MouseEvent e) {
// TODO Auto-generated method stub
}
}
运行结果如下:
三、绘画每一张图片
上面的程序运行起来,就可以进行每一张图片的绘画了。
Java实现手绘火柴人
我们需要将C:\Ruby\萨日朗\换色,目录下的所有图片,一张一张绘画,保存到指定的C:\Ruby\萨日朗\绘图,这个目录下。