画图板是大家电脑上的常用软件。下面我就总结一下在编写Java画图板代码是的重点知识。
一.图形界面
在编写画图板之前,我们首先要了解界面的编写。界面中有两个要素(类):
容器组件:是指在一个完整的包装容器中,除了容器主体以外的,构成这个包装容器的各种配件的总称。
包括:
JFrame:最顶级的容器组件,界面的窗体,不能依附在其他容器组件上
JPanel:最灵活的容器组件,界面上的面板
元素组件:是指包装容器中发挥不同作用的各种组件。
例如:
JButton:按钮组件,用于"push" 按钮的实现。
JTextField:文本框组件,是一个轻量级组件,它允许编辑单行文本。
JLable:文本显示组件,用于短文本字符串或图像或二者的显示区。
等等。
元素组件依附在容器组件上,通过相关方法调整其位置、颜色、大小等等,构成我们看见的界面。
在布局容器时,通常会使用布局器(类)来调整容器组件的布局。
常用的布局器有:
BorderLayout:边框布局,它可以对容器组件进行安排,并调整其大小,使其符合下列五个区域:北、南、东、西、中。
FlowLayout:流式布局,用于安排有向流中的组件,这非常类似于段落中的文本行。
GridLayout:网格布局,它以矩形网格形式对容器的组件进行布置。
等等。
让我们通过代码来演示一下。
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JSlider;
/**
* 定义一个画板类
* @author Darwin
*/
public class DrawingBoard extends JFrame{ //窗体类继承JFrame
public static String str="直线";
public static Color color=Color.BLACK;
public static Color c=Color.WHITE;
public static int n,m;
private ButtonGroup shapegroup=new ButtonGroup();
/**
* 程序入口主函数
*/
public static void main(String[] args){
//实例化一个画板对象
DrawingBoard jf=new DrawingBoard();
//调用初始化界面的方法
jf.initUI();
}
/**
* 初始化界面方法
*/
public void initUI(){
//设置窗体属性
this.setTitle("简单画板");
this.setSize(1000, 700);
this.setDefaultCloseOperation(3);
this.setLocationRelativeTo(null);
this.setLayout(new BorderLayout(5,5));//设置边框布局
//实例化一个面板类对象
JPanel jp = new JPanel();
//设置面板的大小
jp.setPreferredSize(new Dimension(180,700));
//将jp添加到边框布局的NORTH
this.add(jp,BorderLayout.WEST);
//实例化Line,Rect,Oval,Color四个按钮
JButton jb1 =new JButton("直线");
JButton jb2 =new JButton("矩形");
JButton jb3 =new JButton("椭圆");
JButton jb4 =new JButton("铅笔");
JButton jb5 =new JButton("刷子");
JButton jb6 =new JButton("橡皮");
//将按钮添加到jp1上
jp.add(jb1);
jp.add(jb2);
jp.add(jb3);
jp.add(jb4);
jp.add(jb5);
jp.add(jb6);
//将按钮添加到组中
this.shapegroup.add(jb1);
this.shapegroup.add(jb2);
this.shapegroup.add(jb3);
this.shapegroup.add(jb4);
this.shapegroup.add(jb5);
this.shapegroup.add(jb6);
//实例化一个面板类对象
JPanel jp1 = new JPanel();
//将jp1添加到边框布局的CENTER
this.add(jp1,BorderLayout.CENTER);
//设置jp1的颜色
jp1.setBackground(c);
//实例化一个面板对象
//定义JMenu组件
JMenu menuFile=new JMenu(" 文件(F) ");
JMenu menuEdit=new JMenu(" 编辑(E) ");
JMenu menuView=new JMenu(" 查看(V) ");
JMenu menuHelp=new JMenu(" 帮助(H) ");
//定义JMenuBar组件
JMenuBar menubar=new JMenuBar();
//加入JMenu
menubar.add(menuFile);
menubar.add(menuEdit);
menubar.add(menuView);
menubar.add(menuHelp);
//在窗体中加入JMenuBar组件
this.add(menubar, BorderLayout.NORTH);
//定义JMenuItem组件
JMenuItem newitem=new JMenuItem("新建(New)");
JMenuItem openitem=new JMenuItem("打开(Open)");
JMenuItem saveitem=new JMenuItem("保存(Save)");
JMenuItem exititem=new JMenuItem("退出(Exit)");
JMenuItem undoitem=new JMenuItem("撤消(Undo)");
JMenuItem cutitem=new JMenuItem("剪切(Cut)");
JMenuItem copyitem=new JMenuItem("复制(Copy)");
JMenuItem pasteitem=new JMenuItem("粘贴(Paste)");
JMenuItem allitem=new JMenuItem("全选(All)");
JMenuItem gitem=new JMenuItem("工具箱");
JMenuItem yitem=new JMenuItem("颜料盒");
JMenuItem zitem=new JMenuItem("状态栏");
JMenuItem aboutitem=new JMenuItem("关于画图");
//将JMenuItem组件加入菜单中
menuFile.add(newitem);
menuFile.add(openitem);
menuFile.add(saveitem);
menuFile.add(exititem);
menuEdit.add(undoitem);
menuEdit.add(cutitem);
menuEdit.add(copyitem);
menuEdit.add(pasteitem);
menuEdit.add(allitem);
menuView.add(gitem);
menuView.add(yitem);
menuView.add(zitem);
menuHelp.add(aboutitem);
//设置窗体为可见
this.setVisible(true);
}
}
这样我们就得到了下图界面。
二.实现画图
要实现用鼠标画图、通过按钮选择图形等操作,需要用监听器(接口)来接受鼠标、按钮传出的命令,并且完成之后的处理。
常用的监听器有:
MouseListener:鼠标监听器,用于接收组件上“感兴趣”的鼠标事件(按下、释放、单击、进入或离开)的侦听器接口。
ActionListener:动作监听器,用于接收操作事件(比如按钮)的侦听器接口。
MouseMotionListener:鼠标移动监听器,用于接收组件上的鼠标移动事件的侦听器接口。
KeyListener:键盘监听器,用于接收键盘事件(击键)的侦听器接口。
等等。
首先,我们在画板界面类里加入一个内部类,用来监听按钮的动作。
/**
* 匿名内部类
*/
ActionListener al=new ActionListener(){
public void actionPerformed(ActionEvent e) {
//获取按钮上显示的文本值
str=e.getActionCommand();
}
};
将各个按钮加入动作监听器。
//给事件源添加动作监听器方法,然后绑定事件处理类的对象
jb1.addActionListener(al);
jb2.addActionListener(al);
jb3.addActionListener(al);
jb4.addActionListener(al);
jb5.addActionListener(al);
jb6.addActionListener(al);
然后,我们编写一个类实现鼠标监听器和鼠标移动监听器:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.JFrame;
/**
* 绘制图形的处理类,该类实现MouseListener接口
* @author Darwin
*
*/
public class DrawingListener implements MouseListener,MouseMotionListener{
//定义四个变量,用来储存按下和释放的坐标值
private int x1,y1,x2,y2;
//定义一个Graphics类的对象,它是用来绘制图形的
private JFrame jf;
private Graphics g;
int m1,n1;
/**
* 构造方法
*/
public DrawingListener(JFrame jf,Graphics g){
this.jf=jf;
this.g=g;
}
/**
* 鼠标在事件源上按下时,执行事件的处理方法
*/
public void mousePressed(MouseEvent e) {
//获取鼠标在事件源上按下时的坐标
m1=x1=e.getX();
n1=y1=e.getY();
}
/**
* 鼠标在事件源上释放时,执行事件的处理方法
*/
public void mouseReleased(MouseEvent e) {
//获取鼠标在事件源上按下时的坐标
x2=e.getX();
y2=e.getY();
//设置颜色
g.setColor(DrawingBoard.color);
/**
* 判断是否是直线
*/
if(DrawingBoard.str.equals("直线")){
g.drawLine(x1, y1, x2, y2);//绘制直线
System.out.println("执行! x1="+x1+" y1="+y1+" x2="+x2+" y2="+y2);
}else if(DrawingBoard.str.equals("矩形")){
if(x1<x2&&y1<y2){
g.drawRect(x1, y1, Math.abs(x1-x2), Math.abs(y1-y2));//绘制矩形
}else if(x1<x2&&y1>y2){
g.drawRect(x1, y2, Math.abs(x1-x2), Math.abs(y1-y2));//绘制矩形
}else if(x1>x2&&y1>y2){
g.drawRect(x2, y2, Math.abs(x1-x2), Math.abs(y1-y2));//绘制矩形
}else if(x1>x2&&y1<y2){
g.drawRect(x2, y1, Math.abs(x1-x2), Math.abs(y1-y2));//绘制矩形
}
}else if(DrawingBoard.str.equals("椭圆")){
if(x1<x2&&y1<y2){
g.drawOval(x1, y1, Math.abs(x1-x2), Math.abs(y1-y2));//绘制矩形
}else if(x1<x2&&y1>y2){
g.drawOval(x1, y2, Math.abs(x1-x2), Math.abs(y1-y2));//绘制矩形
}else if(x1>x2&&y1>y2){
g.drawOval(x2, y2, Math.abs(x1-x2), Math.abs(y1-y2));//绘制矩形
}else if(x1>x2&&y1<y2){
g.drawOval(x2, y1, Math.abs(x1-x2), Math.abs(y1-y2));//绘制矩形
}
}
}
public void mouseDragged(MouseEvent e) {
int v=js2.getValue();
x2=e.getX();
y2=e.getY();
if(DrawingBoard.str.equals("铅笔")){
g.drawLine(m1, n1, x2, y2);
}else if(DrawingBoard.str.equals("刷子")){
for(int i=0;i<10;i++){
g.fillOval(x2, y2,v,v);
}
}else if(DrawingBoard.str.equals("橡皮")){
g.setColor(Color.WHITE);
for(int i=0;i<10;i++){
g.fillRect(x2, y2,v,v);
}
}
m1=x2;
n1=y2;
}
public void mouseClicked(MouseEvent arg0) {
}
public void mouseEntered(MouseEvent arg0) {
}
public void mouseExited(MouseEvent arg0) {
}
public void mouseMoved(MouseEvent e) {
}
}
设置窗体可见之后,取得面板上的画布,面板加上鼠标监听器
//获取事件源上的Graphics对象
g=jp1.getGraphics();
//实例化鼠标事件处理类的对象
DrawingListener dl=new DrawingListener(this,g);
//给事件源添加鼠标监听器方法,然后绑定事件处理类的对象
jp1.addMouseListener(dl);
jp1.addMouseMotionListener(dl);
这样,就实现了画图板的画图功能
三.重绘
由于现在的画图板在最小化或者被其他窗口挡住后会出现图像被覆盖的情况,所以我们要通过重写JFrame的paint方法来实现。
首先,我们需要自己写一个类来保存画的图形(包括图形的类型,坐标和颜色),用这个类的数组在画图的时候保存数据。
/**
* 定义用来储存图形的数组
* @author Darwin
*/
public class MyShape {
//设置属性
private Color c;
private int type,x1,y1,x2,y2;
//type:1--直线;2--矩形;3--椭圆;4--实心矩形;5--实心圆
public MyShape(Color c,int type,int x1,int y1,int x2,int y2){
this.c=c;
this.type=type;
this.x1=x1;
this.y1=y1;
this.x2=x2;
this.y2=y2;
}
public Color getc(){
return this.c;
}
public int gettype(){
return this.type;
}
public int getx1(){
return this.x1;
}
public int gety1(){
return this.y1;
}
public int getx2(){
return this.x2;
}
public int gety2(){
return this.y2;
}
}
/**
* 定义一个队列,实现MyShape数组的加入和取出
* @author Darwin 2013:04:12
*/
public class MyList {
//队列中初始用来保存MyShape对象的数组
private MyShape[] mA=new MyShape[0];
/**
* 向队列加入新数组
* @param ms 传入的数组
*/
public void add(MyShape ms){
MyShape[] mB=new MyShape[mA.length+1];
mB[mA.length]=ms;
for(int i=0;i<mA.length;i++){
mB[i]=mA[i];
}
mA=mB;
}
/**
* 取出队列中的数组
* @param index 要取出数组中元素的序数
* @return 是一个MyShape对象
*/
public MyShape get(int index){
MyShape ms=mA[index];
return ms;
}
/**
* 获得队列的长度
* @return 数组中元素的个数
*/
public int size(){
return mA.length;
}
}
然后在paint方法中画出数组中储存的图形。
/**
* 重写paint方法
*/
public void paint(Graphics g){
super.paint(g);
drawshapes();
}
/**
* 画出数组中图形
*/
private void drawshapes(){
for(int i=0;i<ml.size();i++){
MyShape shape=ml.get(i);
if(shape.gettype()==1){
g.setColor(shape.getc());
g.drawLine(shape.getx1(), shape.gety1(), shape.getx2(), shape.gety2());
}else if(shape.gettype()==2){
g.setColor(shape.getc());
g.drawRect(shape.getx1(), shape.gety1(), shape.getx2(), shape.gety2());
}else if(shape.gettype()==3){
g.setColor(shape.getc());
g.drawOval(shape.getx1(), shape.gety1(), shape.getx2(), shape.gety2());
}else if(shape.gettype()==4){
g.setColor(shape.getc());
g.fillRect(shape.getx1(), shape.gety1(), shape.getx2(), shape.gety2());
}else if(shape.gettype()==5){
g.setColor(shape.getc());
g.fillOval(shape.getx1(), shape.gety1(), shape.getx2(), shape.gety2());
}
}
}
经过以上三个步骤,再添加一些其他的功能,改善一下界面美工,一个完整的画图板就写出来了。