运用Java做一个画板
首先给出文件结构
参考代码如下:
Graph.javapackage Test.Package;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.Serializable;
import java.net.URL;
import java.util.ArrayList;
abstract public class Graph implements Serializable{ //定义抽象类,并实现其序列化接口
String type="line";
Color color=Color.BLACK; //线条颜色,默认为黑色
float strokeWidth=1.0f; //线条粗细,默认为1.0f
boolean fill=false; //填充
Point start=new Point(0,0);
Point end=new Point(0,0);
String string=""; // 插入字符串
String fontName="宋体"; //字符串格式
Integer fontSize=12; //字符串大小
public Graph(){}
public Graph(String type, Color color, float strokeWidth, boolean fill, Point start, Point end) {
this.type = type;
this.color = color;
this.strokeWidth = strokeWidth;
this.fill = fill;
this.start = start;
this.end = end;
}// 构造方法
void addPoint(){}
abstract void draw(Graphics2D g);// 抽象方法
}
class Line extends Graph{
public Line() {
super();
}
public Line(String type, Color color, float strokeWidth, boolean fill, Point start, Point end) {
super(type, color, strokeWidth, fill, start, end);
}
@Override
void draw(Graphics2D g) {
g.setPaint(color);
g.setStroke(new BasicStroke(strokeWidth));
g.drawLine(start.x,start.y,end.x,end.y);
}
} //画线
class Rect extends Graph{
public Rect() {
super();
}
public Rect(String type, Color color, float strokeWidth, boolean fill, Point start, Point end) {
super(type, color, strokeWidth, fill, start, end);
}
@Override
void draw(Graphics2D g) {
g.setPaint(color); // 设置颜色
g.setStroke(new BasicStroke(strokeWidth)); //设置线条粗细
int x=Math.min(start.x,end.x);
int y=Math.min(start.y,end.y); // 得到起始坐标
int width=Math.abs(start.x-end.x); // 宽
int height=Math.abs(start.y-end.y); // 高
if(fill)
g.fillRect(x,y,width,height);
else
g.drawRect(x,y,width,height);
}
} //画矩形
class Loves extends Graph{
public Loves() {
super();
}
public Loves(String type, Color color, float strokeWidth, boolean fill, Point start, Point end) {
super(type, color, strokeWidth, fill, start, end);
}
@Override
void draw(Graphics2D g) {
g.setColor(color);
g.setStroke(new BasicStroke(strokeWidth));
int x=Math.min(start.x,end.x);
int y=Math.min(start.y,end.y); // 得到起始坐标
int width = Math.abs(start.x - end.x); // 宽
int height = Math.abs(start.y - end.y); // 高
int[] xs=new int[360];
int[] ys=new int[360];
for(int i=0;i<360;i++){
xs[i]= (int) (x-width/10*(28*Math.pow(Math.sin(i),3)));
ys[i]= (int) (y-height/10*(20*Math.cos(i)-6*Math.cos(2*i)-3*Math.cos(3*i)-Math.cos(4*i) ));
}
if(fill){
g.fillPolygon(xs,ys,360);
}else{
g.drawPolygon(xs,ys,360);
}
}
} // 画爱心
class Arc extends Graph{
public Arc() {
super();
}
public Arc(String type, Color color, float strokeWidth, boolean fill, Point start, Point end) {
super(type, color, strokeWidth, fill, start, end);
}
@Override
void draw(Graphics2D g) {
g.setColor(color);
g.setStroke(new BasicStroke(strokeWidth));
int x = Math.min(start.x, end.x);
int y = Math.min(start.y, end.y); // 得到起始坐标
int width = Math.abs(start.x - end.x); // 宽
int height = Math.abs(start.y - end.y); // 高
g.drawArc(x,y,width,height,0,180);
}
} // 画圆弧
class String2 extends Graph{
public String2() {
super();
}
public String2(String type, Color color, float strokeWidth, boolean fill, Point start, Point end) {
super(type, color, strokeWidth, fill, start, end);
}
@Override
void draw(Graphics2D g) {
g.setColor(color);
g.setFont(new Font(fontName,Font.BOLD,fontSize));
g.drawString(string,start.x,start.y);
}
}// 写入字符串
class Triangle extends Graph{
Timer timer=null;
public Triangle() {
super();
}
public Triangle(String type, Color color, float strokeWidth, boolean fill, Point start, Point end) {
super(type, color, strokeWidth, fill, start, end);
}
@Override
void draw(Graphics2D g) {
g.setColor(color);
g.setStroke(new BasicStroke(strokeWidth));
int x = Math.min(start.x, end.x);
int y = Math.min(start.y, end.y); // 得到起始坐标
int width = Math.abs(start.x - end.x); // 宽
int height = Math.abs(start.y - end.y); // 高
int[] xs = {x + width / 2, x, x + width};
int[] ys = {y, y + height, y + height};
if(fill){
g.fillPolygon(xs,ys,3);
}else{
g.drawPolygon(xs, ys, 3); // 画三角形
}
}
} // 三角形
class FivePoint extends Graph{
public FivePoint() {
super();
}
public FivePoint(String type, Color color, float strokeWidth, boolean fill, Point start, Point end) {
super(type, color, strokeWidth, fill, start, end);
}
@Override
void draw(Graphics2D g) {
g.setColor(color);
g.setStroke(new BasicStroke(strokeWidth));
int x = Math.min(start.x, end.x);
int y = Math.min(start.y, end.y); // 得到起始坐标
int width = Math.abs(start.x - end.x); // 宽
int height = Math.abs(start.y - end.y); // 高
int xb= (int) (width/(2*Math.cos(Math.PI/5)));
int yb= (int) (height/(1+Math.cos(Math.PI/5)));
int zx=x+width/2;
int zy=y+yb;
// 根据五角星各个点的规律,得到五角星上相应的十个点的位置
int[] xs={(int) (zx-xb*Math.cos(Math.PI/10)),(int) (zx+xb*Math.cos(Math.PI/10)),(int) (zx-xb*Math.sin(Math.PI/5)),zx,(int) (zx+xb*Math.sin(Math.PI/5))};
int[] ys={(int) (zy-xb*Math.sin(Math.PI/10)),(int) (zy-xb*Math.sin(Math.PI/10)),(int) (zy+xb*Math.cos(Math.PI/5)),zy-yb,(int) (zy+xb*Math.cos(Math.PI/5))};
int[] xs1={xs[0],xs[0]+(xs[1]-xs[0])/3,xs[3],xs[0]+2*(xs[1]-xs[0])/3,xs[1]
,xs[3]+2*(xs[4]-xs[3])/3,xs[4],xs[3],xs[2],xs[3]-2*(xs[3]-xs[2])/3};
int[] ys1={ys[0],ys[0],ys[3],ys[0],ys[1],ys[3]+2*(ys[4]-ys[3])/3
,ys[4],ys[0]+2*(ys[4]-ys[0])/3,ys[2],ys[3]+2*(ys[2]-ys[3])/3};
if(fill){
g.fillPolygon(xs1,ys1,10);
}else{
g.drawPolygon(xs1,ys1,10);
}
}
} //五角星
class Oval extends Graph{
public Oval() {
super();
}
public Oval(String type, Color color, float strokeWidth, boolean fill, Point start, Point end) {
super(type, color, strokeWidth, fill, start, end);
}
@Override
void draw(Graphics2D g) {
g.setPaint(color);
g.setStroke(new BasicStroke(strokeWidth));
int x=Math.min(start.x,end.x);
int y=Math.min(start.y,end.y);
int width=Math.abs(start.x-end.x);
int height=Math.abs(start.y-end.y);
if(fill)
g.fillOval(x,y,width,height);
else
g.drawOval(x,y,width,height);
}
} //画椭圆
class Pencil extends Graph{
ArrayList<Point> points=new ArrayList<>();
public Pencil() {
super();
}
public Pencil(String type, Color color, float strokeWidth, boolean fill, Point start, Point end) {
super(type, color, strokeWidth, fill, start, end);
}
@Override
void draw(Graphics2D g) {
g.setPaint(color);
g.setStroke(new BasicStroke(strokeWidth));
if(points.size()>1){
Point s=points.get(0);
for (Point e:points) {
g.drawLine(s.x,s.y,e.x,e.y);
s=e;
}
}
}
@Override
void addPoint() {
points.add(end);
}
} //画笔
class Rubber extends Pencil{
public Rubber() {
super();
}
public Rubber(String type, Color color, float strokeWidth, boolean fill, Point start, Point end) {
super(type, Color.WHITE, 36.0f, false, start, end);
}
@Override
void draw(Graphics2D g) {
super.draw(g);
}
}//橡皮
class Image extends Graph{
BufferedImage Image=null;
public Image() {
super();
}
public Image(String type, Color color, float strokeWidth, boolean fill, Point start, Point end) {
super(type, color, strokeWidth, fill, start, end);
}
public void setImage(){
URL url=getClass().getResource("/images/12.png"); //获取钟面图像文件的url
try {
Image= ImageIO.read(url); //载入图像
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
void draw(Graphics2D g) {
g.setPaint(color);
g.setStroke(new BasicStroke(strokeWidth));
int x=Math.min(start.x,end.x);
int y=Math.min(start.y,end.y);
int width=Math.abs(start.x-end.x);
int height=Math.abs(start.y-end.y);
setImage();
g.drawImage(Image,x,y,width,height,null); // 绘制图片
}
}// 插入图片
class Image2 extends Graph{
public Image2() {
super();
}
public Image2(String type, Color color, float strokeWidth, boolean fill, Point start, Point end) {
super(type, color, strokeWidth, fill, start, end);
}
@Override
void draw(Graphics2D g) {
}
} /* 为Image类定义的类,如果直接保存具有Image对象的文件,会提示无法保存,
如果将Image对象转换成Image2对象之后就可以实现保存功能了*/
Main.java
package Test.Package;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.URL;
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
FrmMain frmMain=new FrmMain();
frmMain.setVisible(true);
}
}
class FrmMain extends JFrame{
String[] buttonStrings={"line","rect","oval","fivepoint","triangle","loves","pencil","color","string","arc","rubber","image"};
String[] tipStrings={"绘制直线","绘制矩形","绘制椭圆","绘制五角星","绘制三角形","绘制爱心","自由绘制","选择颜色","输入文字","圆弧","橡皮","贴纸"};
JMenuBar menuBar;
JToolBar toolBar;
JButton[] buttons; // 按钮数组
JLabel stausBar;
JComboBox cbxFont; // 字体下拉框
JComboBox cbxFontSize; // 字体大小下拉框
String fontName="宋体"; // 默认字体为宋体
Integer fontSize=12; //字体默认大小为12
JPanel fontPanel; // 字体面板
JTextField jtextField; // 文本框
JSlider slider;
JCheckBox fillCheckBox;
DrawPanel drawPanel; // 画板
String type="line";
Color color=Color.BLACK;
float strokeWidth=1.0f; // 线条粗细默认为1.0
boolean fill=false; // 默认为不填充
Point start=new Point(0,0);
Point end=new Point(0,0);
Graph graph;
ArrayList<Graph> graphList=new ArrayList<>();
public FrmMain(){
super("我的画板");
setBounds(400,200,1400,900); // 设置窗口的大小为800,600
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ButtonListener buttonListener=new ButtonListener(); // 按钮监控器对象
// 菜单栏
menuBar=new JMenuBar();
setJMenuBar(menuBar);
JMenu fileMenu=new JMenu("文件");
JMenu editMenu=new JMenu("编辑");
menuBar.add(fileMenu);
menuBar.add(editMenu);
JMenuItem newItem=new JMenuItem("新建");
JMenuItem saveItem=new JMenuItem("保存");
JMenuItem openItem=new JMenuItem("打开");
JMenuItem exitItem=new JMenuItem("退出");
newItem.setActionCommand("new");
saveItem.setActionCommand("save");
openItem.setActionCommand("open");
exitItem.setActionCommand("exit");
fileMenu.add(newItem);
fileMenu.add(saveItem);
fileMenu.add(openItem);
fileMenu.addSeparator(); //为菜单项之间添加一个分割线
fileMenu.add(exitItem);
JMenuItem undoItem=new JMenuItem("撤销");
undoItem.setActionCommand("undo");
editMenu.add(undoItem);
newItem.addActionListener(buttonListener);
saveItem.addActionListener(buttonListener);
openItem.addActionListener(buttonListener);
exitItem.addActionListener(buttonListener);
undoItem.addActionListener(buttonListener);
// 工具栏 ,默认为水平布局
toolBar=new JToolBar(JToolBar.HORIZONTAL);
add(toolBar,BorderLayout.NORTH); // 将工具栏添加到窗口中
buttons=new JButton[tipStrings.length];
for (int i=0;i<buttons.length;i++){
URL url=getClass().getResource("/images/"+buttonStrings[i]+".png");
ImageIcon imageIcon=new ImageIcon(url);
buttons[i]=new JButton(imageIcon); // 创建包含图标的按钮
buttons[i].setToolTipText(tipStrings[i]); //用来设置鼠标停在按钮上提示的信息
buttons[i].setActionCommand(buttonStrings[i]);
toolBar.add(buttons[i]);
buttons[i].addActionListener(buttonListener); // 为按钮添加监听器
}
fillCheckBox=new JCheckBox("填充"); // 复选框
toolBar.add(fillCheckBox);
fillCheckBox.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
fill=fillCheckBox.isSelected(); // 返回布尔类型
}
}); // 为复选框添加监听器
fontPanel=new JPanel();
cbxFont=new JComboBox(); // 字体
cbxFontSize=new JComboBox(); // 字体大小
fontPanel.add(cbxFont);
fontPanel.add(cbxFontSize);
toolBar.add(fontPanel);
GraphicsEnvironment ge=GraphicsEnvironment.getLocalGraphicsEnvironment();
String[] fontList=ge.getAvailableFontFamilyNames(); // 获得系统的字体数组
for(int i=0;i<fontList.length;i++)
cbxFont.addItem(fontList[i]); // 添加字体名称
cbxFont.setSelectedIndex(31);
for(int i=9;i<=72;i++) // 添加字体大小
cbxFontSize.addItem(new Integer(i).toString());
cbxFontSize.setSelectedIndex(3);
cbxFont.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
fontName= (String) cbxFont.getSelectedItem();
}
}); // 为下拉框设置监听器
cbxFontSize.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
fontSize= Integer.parseInt((String) cbxFontSize.getSelectedItem());
}
});
jtextField=new JTextField(); // 文本框
jtextField.setFont(new Font("宋体",Font.BOLD,20));
toolBar.add(jtextField);
// 滑动条
slider=new JSlider(1,20,1);
slider.setMajorTickSpacing(1);
slider.setPaintTicks(true);
slider.setSnapToTicks(true);
slider.setSize(200,30);
slider.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
strokeWidth=slider.getValue();
}
}); // 为滚动条添加监听器
JPanel panel=new JPanel();
panel.add(new JLabel("粗细"));
panel.add(slider); // 将组件slider添加到一个面板当中
toolBar.add(panel); // 将这个面板添加到工具栏当中
// 状态栏
stausBar=new JLabel();
stausBar.setText("欢迎使用画板");
add(stausBar,BorderLayout.SOUTH);
drawPanel=new DrawPanel(); // 画板
add(drawPanel,BorderLayout.CENTER);
try {
String IfClassName="com.sun.java.swing.plaf.windows.WindowsClassicLookAndFeel";
UIManager.setLookAndFeel(IfClassName);
SwingUtilities.updateComponentTreeUI(this);
} catch (Exception e) {
e.printStackTrace();
} // 设置图形界面风格
}
// 鼠标点击监听器类
class MouseA extends MouseAdapter{
@Override
public void mousePressed(MouseEvent e) {
stausBar.setText("鼠标按下["+e.getX()+","+e.getY()+"]");
start=e.getPoint();
end=e.getPoint();
createNewGraph();
if(type.equals("string") && e.getClickCount()==2){
graph.string=jtextField.getText();
} // 如果点击按钮之后,type="etring",并且点击次数为2,那么将文本框中的内容赋值给graph对象中的变量string
drawPanel.repaint(); // 重绘面板
}
@Override
public void mouseReleased(MouseEvent e) {
stausBar.setText("鼠标释放["+e.getX()+","+e.getY()+"]");
graph.end=e.getPoint();
drawPanel.repaint();
}
@Override
public void mouseExited(MouseEvent e) {
stausBar.setText("欢迎使用画板");
}
}
class MouseB implements MouseMotionListener{
@Override
public void mouseDragged(MouseEvent e) {
stausBar.setText("鼠标拖动["+e.getX()+","+e.getY()+"]");
graph.end=e.getPoint();
if(type.equals("pencil") || type.equals("rubber"))
graph.addPoint();
drawPanel.repaint();
}
@Override
public void mouseMoved(MouseEvent e) {
stausBar.setText("鼠标移动["+e.getX()+","+e.getY()+"]");
}
}
class ButtonListener implements ActionListener{
@Override
public void actionPerformed(ActionEvent e) {
String comStr=e.getActionCommand();
switch (comStr){
case "line":
case "rect":
case "oval":
case "fivepoint":
case "triangle":
case "loves":
case "pencil":
case "string":
case "rubber":
case "arc":
case "image":
type=comStr;
break;
case "color":
Color selectedColor=JColorChooser.showDialog(FrmMain.this,"选择颜色",color);
if(selectedColor!=null)
color=selectedColor;
break;
case "new":
graphList.clear();
drawPanel.repaint();
break;
case "save":
saveFile();
break;
case "open":
loadFile();
break;
case "exit":
System.exit(0);
case "undo": // 防止撤销报错
try{
graphList.remove(graphList.size()-1);
drawPanel.repaint();
}catch (Exception e2){
JOptionPane.showMessageDialog(FrmMain.this,"当前没有任何可以撤销的!");
}
default:
break;
}
}
}
// 绘图面板类
class DrawPanel extends JPanel{
public DrawPanel() {
setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR)); // 设置光标的形状,这里为十字光标
setBackground(Color.WHITE); // 设置面板的背景颜色为白色
addMouseListener(new MouseA());
addMouseMotionListener(new MouseB());
}
public Cursor createCursor(String fileName) {
java.awt.Image cursor = Toolkit.getDefaultToolkit().createImage(Toolkit.class.getResource(fileName));
return Toolkit.getDefaultToolkit().createCustomCursor(cursor, new Point(0, 27), "mycursor");
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Graph graph:graphList){
graph.draw((Graphics2D)g);
}
}
}
void createNewGraph(){
switch (type){
case "line":
graph=new Line(type,color,strokeWidth,fill,start,end);
break;
case "rect":
graph=new Rect(type,color,strokeWidth,fill,start,end);
break;
case "oval":
graph=new Oval(type,color,strokeWidth,fill,start,end);
break;
case "fivepoint":
graph=new FivePoint(type,color,strokeWidth,fill,start,end);
break;
case "triangle":
graph=new Triangle(type,color,strokeWidth,fill,start,end);
break;
case "loves":
graph=new Loves(type,color,strokeWidth,fill,start,end);
break;
case "pencil":
graph=new Pencil(type,color,strokeWidth,fill,start,end);
break;
case "string":
graph=new String2(type,color,strokeWidth,fill,start,end);
graph.fontName=fontName;
graph.fontSize=fontSize;
break;
case "arc":
graph=new Arc(type,color,strokeWidth,fill,start,end);
break;
case "rubber":
graph=new Rubber(type,color,strokeWidth,fill,start,end);
break;
case "image":
graph=new Image(type,color,strokeWidth,fill,start,end);
default:
break;
}
switch (type){ /*设置光标形状
如果绘制的type为line(线)、rect(矩形)、oval(椭圆)、fivepoint(五角星)、triangle(三角形)、loves(爱心)、image(图片,这里为贴纸)、arc(圆弧),那么光标形状为十字符号
如果绘制的type为pencil(画笔),那么光标形状为一支画笔(使用图片)
如果需要添加注解,那么光标形状为文字光标类型
如果type为rubber(橡皮),那么光标形状为一个正方形(使用图片)
*/
case "line":
case "rect":
case "oval":
case "fivepoint":
case "triangle":
case "loves":
case "image":
case "arc":
drawPanel.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
break;
case "pencil":
drawPanel.setCursor(drawPanel.createCursor("/images/pencil2.png"));
break;
case "string":
drawPanel.setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
break;
case "rubber":
drawPanel.setCursor(drawPanel.createCursor("/images/rubber2.png"));
default:
break;
}
graphList.add(graph);
}
void saveFile(){
ArrayList<Graph> graphList2=new ArrayList<>();
for(int i=0;i<graphList.size();i++){
Graph graph1=graphList.get(i);
String string=String.valueOf(graph1.getClass());
int index=string.lastIndexOf(".")+1;
if(string.substring(index).equals("Image")){
graph1=new Image2(graph1.type,graph1.color,graph1.strokeWidth,graph1.fill,graph1.start,graph1.end);
} // 这里如果直接将Image对象写到文件中,会报错,所以将它转换成Image2对象之后再写入
graphList2.add(graph1);
}
ObjectOutputStream output;
JFileChooser fileChooser=new JFileChooser();
fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
int result=fileChooser.showSaveDialog(this);
if(result==JFileChooser.CANCEL_OPTION)
return;
File file=fileChooser.getSelectedFile();
file.canWrite();
file.delete();
try {
output=new ObjectOutputStream(new FileOutputStream(file));
output.writeObject(graphList2);
output.close();
} catch (Exception e) {
JOptionPane.showMessageDialog(this,"保存文件出错!");
}
} // 保存文件
void loadFile(){
ArrayList<Graph> graphList2=null;
ObjectInputStream input;
JFileChooser fileChooser=new JFileChooser();
fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
int result=fileChooser.showOpenDialog(this);
if(result==JFileChooser.CANCEL_OPTION)
return;
File file=fileChooser.getSelectedFile();
file.canRead();
try {
input=new ObjectInputStream(new FileInputStream(file));
graphList2=(ArrayList<Graph>)input.readObject();
graphList.clear();
for (int i=0;i<graphList2.size();i++){
Graph graph1=graphList2.get(i);
String string=String.valueOf(graph1.getClass());
int index=string.lastIndexOf(".")+1;
if(string.substring(index).equals("Image2")){
graph1=new Image(graph1.type,graph1.color,graph1.strokeWidth,graph1.fill,graph1.start,graph1.end);
} // 如果读取的对象中有Image2对象,那么将它转换成Image对象
graphList.add(graph1);
}
drawPanel.repaint();
input.close();
} catch (Exception e) {
JOptionPane.showMessageDialog(this,"打开文件出错!");
} // 打开文件
}
}
运行结果如下:
讲明一点: 直接复制代码可能不会运行成功,需要在src下新建文件夹images,然后再这个文件夹下面放一些图片,当然图片命名请参考代码中的图片命名,另外,图片的大小也需要重新设置一下。还有保存好的图片要用这个代码程序打开,否则不会显示,命名不需要后缀名。