1.首先是通过JFrame写一个画图面板的界面。
(1)需要给界面添加一个监听器,用来获取鼠标按下和释放时候的坐标。这个不能使用动作监听器(ActionListener),只有使用鼠标监听器(MouseListener)才能够获取到坐标。
ActionListener:可以给需要关注其动作的组件(如Button)添加监听器(addActionListener(this);),之后在事件处理方法(public void actionPerformed(ActionEvent event){})中,对每个事件进行不同处理。
MouseListener:用于接收组件上“感兴趣”的鼠标事件(按下、释放、单击、进入或离开)的侦听器接口。(要跟踪鼠标移动和鼠标拖动,请使用 MouseMotionListener
。)旨在处理鼠标事件的类要么实现此接口(及其包含的所有方法),要么扩展抽象类 MouseAdapter
(仅重写所需的方法)。
(2)为了绘制出一个形状,我们还需要获取绘制权限获取画笔对象,这个权限在java中叫做Graphics对象,或者叫画布对象。我们可以从窗体上获取这个对象,然后创建一个鼠标监听器对象,把画布对象传给监听器。最后给窗体就加上鼠标监听器对象,这样我们在窗体上按下和释放鼠标的时候,就会获取到这两个位置的坐标。最后调用画布对象绘制的方法(对象.drawline())
<textarea readonly="readonly" name="code" class="java">
package com.xml.study0708;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import javax.swing.JButton;
import javax.swing.JFrame;
public class DrawFrame extends JFrame { // 继承JFrame,重写paint重绘方法
private Shape[] shapeArray = new Shape[1000000];// 主函数创建数组,后面要使用数组
public static void main(String[] args) {
DrawFrame dw = new DrawFrame();
dw.showUI();// TODO Auto-generated method stub
}
public void showUI() {
this.getContentPane().setBackground(Color.BLACK);// 设置画图面板的背景颜色getcontentpane()
this.setTitle("画图面板");// 设置标题
this.setSize(800, 800);// 设置窗体大小
this.setDefaultCloseOperation(3);// 设置窗体关闭时程序退出,括号内数值3为全部关闭,或2为单个关闭退出,0或1为隐藏
this.setLocationRelativeTo(null);// 设置窗体居中显示
FlowLayout f1 = new FlowLayout();//流式布局管理器
this.setLayout(f1);
//数组创建图形按钮以及颜色按钮
String[] jb = { "直线", "矩形", "椭圆", "三角形", "多边形", "实心圆", "动圆", "随机", "迭代分形1", "迭代分形2", "迭代分形3", "迭代分形4", "曲线",
"橡皮擦" };
Color[] co = { Color.RED, Color.BLUE, Color.magenta, Color.YELLOW, Color.GREEN };
DrawMouse dr = new DrawMouse();// 创建对象用来实现监听
//设置图形按钮以及颜色按钮
JButton jbu[] = new JButton[14];
for (int i = 0; i < jbu.length; i++) {
jbu[i] = new JButton(jb[i]);
this.add(jbu[i]);
jbu[i].addActionListener(dr);// 添加动作监听器
if (i > 12) {
jbu[i].addMouseMotionListener(dr);// 添加鼠标拖动监听器
}
}
JButton color[] = new JButton[5];
for (int i = 0; i < color.length; i++) {
color[i] = new JButton();
color[i].setBackground(co[i]);
color[i].setPreferredSize(new Dimension(30, 30));
this.add(color[i]);
color[i].addActionListener(dr);// 动作监听器
}
// 设置在画笔之前,窗体可视化组件之后。设置窗体可见
this.setVisible(true);
/**1、图形画在哪个图形上,那么画笔组件就在这个图形上获取
2、获取画笔对象,一定在窗体可见之后
3、获取的画笔对象必须要用一个方法来传给自己定义的事件监听类中
*/
Graphics g = this.getGraphics();// 从窗体上获取画布对象,即获取窗体在屏幕上占据的区域,这块区域是可以改变颜色的
dr.setGraphics(g);//传入画布对象
/**
* 1.找到事件源 2.确定监听器方法 3.绑定事件处理的类 格式:1.2(3);
*/
// DrawMouse dr = new DrawMouse();// 创建对象用来实现监听
this.addMouseListener(dr);// 给窗体添加鼠标监听器
this.addMouseMotionListener(dr);// 给窗体添加鼠标拖动监听器
dr.setShapeArray(shapeArray);// 传递数组对象
}
// 绘制组件重写方法
public void paint(Graphics gr) {// 继承的JFrame的方法
super.paint(gr);//super指父类
System.out.println("重绘!");
for (int i = 0; i < shapeArray.length; i++) {
Shape shape = shapeArray[i];
if (shape != null) {//如果赋值出的数组不是空的
shape.drawshape(gr);
} else {//如果赋值出的数组是空的
break;
}
}
}
// 取出shapeArray数组中保存的图形对象,绘制
}</textarea>
2.重新定义一个类,即事件处理类(如DrawMouse),继承接口MouseListener, ActionListener,MouseMotionListener。
(1)首先实例化一个Graphics对象来接受传过来的画笔对象。
private Graphics gr;//创建画笔
public void setGraphics(Graphics g) { // 创建对象,初始化
gr = g;
}
(2)由于使用的是字符串数组来储存画图按钮上面的字符串(即图形名字),以及Color类的数组储存按钮的背景颜色,所以在重写ActionListener接口中的actionPerformed方法时,要首先获取按钮上的颜色或者文字,当获取的不是文字时,说明点击的是颜色按钮,读取按钮的颜色后给设置画笔的颜色;如果读取的是文字时,需要继续下一步的操作,实现代码如下:
e.getSource()方法依赖于事件对象。
比如:JButton jbt = new JButton("直线");中事件对象就是jbt
e.getActionCommand()方法依赖于按钮上的字符串
比如:JButton jbt = new JButton("直线");中字符串button
简而言之:用e.getSource()得到的是jbt,而用e.getActionCommand()得到的是button。
<textarea readonly="readonly" name="code" class="java">
public void actionPerformed(ActionEvent e) {
if ("".equals(e.getActionCommand())) {
JButton JBu = (JButton) e.getSource();// 获取事件源。强制转换,父类转换成子类,
Color color = JBu.getBackground();// 获取背景色
gr.setColor(color);// 设置画笔颜色
} else {
name = e.getActionCommand();
System.out.println("name=" + name);// 输出语句测试监听按钮是否完成
}
}</textarea>
(3)重写实现其他继承的方法
java中有许多绘制图形的方法,画图需要获取一定数量的坐标值x,y。在继承的接口的方法中有MouseEvent类型的形参e,可以通过形参调用getx(),gety()来获取经过某种操作(如点击,释放,单击)过后当前位置的x坐标以及y坐标。
用画笔的实例化对象调用drawline(x1,y1,x2,y2)方法可以画一条直线
public void mousePressed(MouseEvent e) {
System.out.println("按下");
x1 = e.getX();
y1 = e.getY();
}
public void mouseReleased(MouseEvent e) {
System.out.println("释放");
x2 = e.getX();
y2 = e.getY();
if (name.equals("直线")) {
gr.drawLine(x1, y1, x2, y2);
}
if (name.equals("椭圆")) {
gr.drawOval(Math.min(x1, x2), Math.min(y1, y2), Math.abs(x1 - x2), Math.abs(y1 - y2));
}
}
使用画笔对象gr调用drawRext()的方法画矩形(圆形同理,圆形是通过绘制矩形的方法绘制内切圆形)。需要注意的是坐标(x1,y1)代表的是矩形左上角的点,即x1,y1均是最小值。因此需要math类取大小值,绝对值。
由于java中没有定义画三角形的函数,所以需要通过一个变量的操作来控制花三条直线的方法来实现。多边形同理
private int count = 1;
public void mouseClicked(MouseEvent e) {
System.out.println("单击");
if (name.equals("三角形") && count == 2) {
x3 = e.getX();
y3 = e.getY();
gr.drawLine(x2, y2, x3, y3);
if (e.getClickCount() == 2) {
gr.drawLine(x1, y1, x3, y3);
count = 1;
}
}
public void mousePressed(MouseEvent e) {
System.out.println("按下");
if (count == 1) {
x1 = e.getX();
y1 = e.getY();
}
public void mouseReleased(MouseEvent e) {
System.out.println("释放");
if (count == 1) {
x2 = e.getX();
y2 = e.getY();
if (name.equals("三角形") && count == 1) {
gr.drawLine(x1, y1, x2, y2);
}
}
3.重绘
在创建窗体时我们已经定义了窗体的大小,如果我们再次改变窗体大小的时候,原来的窗体就不满足显示的需求。这时候就会将窗体上所有的组件再重新绘制一次,自动调用了paint方法,这个方法是定义在JFrame和JPanel中都有的。因此必须让窗体类继承JFrame类或者JPanel类,再重写paint()方法。
但是由于可能改变窗体大小之前已经绘制了不少图形,会有较多坐标需要保存重绘,所以需要新建一个对象数组用来存储数据,然后再重写的paint方法中直接获取使用。
(1) 首先创建一个用来保存图形数据的shape类,新建数组对象。
/**
* 创建图形对象时初始化该图形的数据
*/
public Shape(int x1, int y1, int x2, int y2, String name, Color color) {
if (name.equals("直线") || name.equals("矩形") || name.equals("椭圆")) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
this.name = name;
this.color = color;
}
}
}
(2)在事件处理类DrawMouse中创建数组对象和数组角标index。每画出一个图形保存在数组中一次。由于点,三角形,多边形,曲线,橡皮擦,迭代图形都是通过两点绘制即drawline(),保存在直线重绘即可。
private Shape[] shapeArray;
private int index = 0;// 创建数组角标
public void setShapeArray(Shape[] shapeArray) {//实例化数组
this.shapeArray = shapeArray;
}
if (name.equals("直线")) {
gr.drawLine(x1, y1, x2, y2);
Shape shape = new Shape(x1, y1, x2, y2, "直线", gr.getColor());// 创建shape对象用来保存绘制图形的数据
shapeArray[index] = shape;
index++;
}
if (name.equals("矩形")) {
gr.drawRect(Math.min(x1, x2), Math.min(y1, y2), Math.abs(x1 - x2), Math.abs(y1 - y2));
Shape shape = new Shape(x1, y1, x2, y2, "矩形", gr.getColor());
shapeArray[index] = shape;
index++;
}
(3)三角形和多边形的绘制是由多条直线绘制,所以每画出一条直线保存一次数据到数组中。
第一次点击的时候
if (name.equals("三角形") && count == 1) {
gr.drawLine(x1, y1, x2, y2);
Shape shape = new Shape(x1, y1, x2, y2, "直线", gr.getColor());
shapeArray[index] = shape;
index++;
count++;
}
第二次点击的时候
if (name.equals("三角形") && count == 2) {
x3 = e.getX();
y3 = e.getY();
gr.drawLine(x2, y2, x3, y3);
Shape shape = new Shape(x2, y2, x3, y3, "直线", gr.getColor());
shapeArray[index] = shape;
index++;
if (e.getClickCount() == 2) {
gr.drawLine(x1, y1, x3, y3);
Shape shape1 = new Shape(x1, y1, x3, y3, "直线", gr.getColor());
shapeArray[index] = shape1;
index++;
count = 1;
}
}
多边形count == 2(即第二次点击)或者双击闭合图形的时候
if (name.equals("多边形") && count == 2) {
x3 = e.getX();
y3 = e.getY();
gr.drawLine(x2, y2, x3, y3);
Shape shape = new Shape(x2, y2, x3, y3, "直线", gr.getColor());
shapeArray[index] = shape;
index++;
x2 = x3;
y2 = y3;
System.out.println("画一条线");
if (e.getClickCount() == 2) {
gr.drawLine(x1, y1, x3, y3);
Shape shape1 = new Shape(x1, y1, x3, y3, "直线", gr.getColor());
shapeArray[index] = shape1;
index++;
System.out.println("最后连接");
count = 1;
}
}
(4)数据全部保存到数组中以后在界面类中继承JFrame或者JPanel,重写paint方法。
首先在界面类中调用事件处理类DrawMouse中的setShapeArray方法 ,将方法传递给界面类中的数组对象。
dr.setShapeArray(shapeArray);
super指父类中。this指本类中。
// 绘制组件重写方法
public void paint(Graphics gr) {
super.paint(gr); //调用父类中的方法
System.out.println("重绘!");
for (int i = 0; i < shapeArray.length; i++) {
Shape shape = shapeArray[i]; //将数组赋值给shape对象
if (shape != null) { //如果数组不是空的
shape.drawshape(gr); //调用shape类中的方法drawshape
} else {
break;
}
}
}
// 取出shapeArray数组中保存的图形对象,绘制
(5)shape类中的重绘方法
// 根据图形名字绘制对应的图形
public void drawshape(Graphics gr) {
switch (name) {
case "直线":
gr.setColor(color);
gr.drawLine(x1, y1, x2, y2);
break;
case "矩形":
gr.setColor(color);
gr.drawRect(Math.min(x1, x2), Math.min(y1, y2), Math.abs(x1 - x2), Math.abs(y1 - y2));
break;
case "椭圆":
gr.setColor(color);
gr.drawOval(Math.min(x1, x2), Math.min(y1, y2), Math.abs(x1 - x2), Math.abs(y1 - y2));
break;
case"橡皮擦":
gr.setColor(color);
gr.fillRect(x1, y1, 50, 50);
break;
}
}