問題描述
- 至少提供自由繪畫、直線、橢圓、矩形四種模式
- 可插入文本
- 背景色與前端顏色可選
- 圖形繪製可選擇填充模式與非填充模式
- 繪畫的作品可進行保存與加載
總體框架
主體框架即DrawBoard繼承自JFrame,上設:
- 2個JPanel(menu與tool,內含數個JButton);
- 1個DrawPanel(繼承自JPanel),即畫板主體;
- 1個TextArea,在狀態變更時顯示相應信息。
效果如圖:
-
上方JPanel爲菜單,可進行:
- 打開/保存文件(序列化)
- 選擇前端顏色
- 選擇是否填充圖形
- 設置背景顏色
-
左方JPanel爲工具,可供用戶在中央畫板進行繪畫,可選模式爲:
- 自由繪畫
- 直線
- 橢圓
- 矩形
-
可在中央畫布單擊鼠標右鍵插入文本(下方TextArea有提醒)
實現思路
共7個類,如下圖:
- Dshape與Dstr存儲已生成的Shape與String;
- DArray由兩個ArrayList組成,還有一個Color記錄背景色,類別分別爲Dshape與Dstr,相當於存儲器,是存儲以及獲取文件時序列化的對象;
- MA繼承自MouseAdapter,是DrawPanel最主要的一個監聽器,4種繪畫模式都要用到,即在鼠標按下時確定起點,釋放時確定終點;
- MMA繼承自MouseMotionAdapter,是DrawPanel的另一個監聽器,僅自由繪畫模式會用到,即在鼠標拖動時記錄劃過的點作爲上一條線段的終點及下一條線段的起點,多條線段做出自由繪畫的感覺;
- DrawPanel作爲主體,記錄當前的前端顏色、填充狀態、圖形類別,並通過重寫paintComponent方法以繪製已記錄的和正在繪製的圖形;
- MTLis繼承自ActionListener,是DrawBoard的監聽器,根據觸發的按鈕進行操作;
- DrawBoard繼承自JFrame,起框架作用。
源代碼
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.io.*;
import java.util.ArrayList;
import javax.swing.*;
class Dshape implements Serializable{
private static final long serialVersionUID = 1L;
Shape s;
Color c;
boolean f;//填充狀態
String m=new String();//繪畫模式
private void writeObject(java.io.ObjectOutputStream out) throws IOException
{
out.defaultWriteObject();
}
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException
{
in.defaultReadObject();
}
}
class Dstr implements Serializable{
private static final long serialVersionUID = 1L;
String s;
Font f;
Color c;
int x,y;//座標
private void writeObject(java.io.ObjectOutputStream out) throws IOException
{
out.defaultWriteObject();
}
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException
{
in.defaultReadObject();
}
}
class DArray implements Serializable{
private static final long serialVersionUID = 1L;
ArrayList<Dshape> shapelist=new ArrayList<Dshape>();
ArrayList<Dstr> strlist=new ArrayList<Dstr>();
Color backColor;//背景色
private void writeObject(java.io.ObjectOutputStream out) throws IOException
{
out.defaultWriteObject();
}
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException
{
in.defaultReadObject();
}
}
class MA extends MouseAdapter{
DrawPanel dp;
MA(DrawPanel dp)
{
super();
this.dp=dp;
}
@Override
public void mousePressed(MouseEvent e){//鼠標按下
if(dp.tempshape!=null) {
dp.da.shapelist.add(dp.tempshape);
}
switch(dp.model)
//據繪製模式對“當前繪製圖形(tempshape)”進行初始化
{
case "Draw":
case "Line":
{
dp.tempshape=new Dshape();
Line2D l=new Line2D.Double();
l.setLine(e.getX(), e.getY(), e.getX(), e.getY());
dp.tempshape.c=dp.currColor;
dp.tempshape.s=l;
dp.tempshape.f=dp.fill;
dp.tempshape.m="Line";
break;
}
case "Ellipse":
{
dp.tempshape=new Dshape();
Ellipse2D ep=new Ellipse2D.Double();
ep.setFrame(e.getX(), e.getY(), 0, 0);
dp.tempshape.c=dp.currColor;
dp.tempshape.s=ep;
dp.tempshape.f=dp.fill;
dp.tempshape.m="Ellipse";
break;
}
case "Rectangle":
{
dp.tempshape=new Dshape();
Rectangle2D ra=new Rectangle2D.Double();
ra.setFrame(e.getX(), e.getY(), 0, 0);
dp.tempshape.c=dp.currColor;
dp.tempshape.s=ra;
dp.tempshape.f=dp.fill;
dp.tempshape.m="Rectangle";
break;
}
}
}
@Override
public void mouseReleased(MouseEvent e){//鼠標鬆開
switch(dp.model)
//完成tempshape的繪製
{
case "Draw":
case "Line":
{
Line2D l=(Line2D)(dp.tempshape.s);
l.setLine(l.getX1(), l.getY1(), e.getX(), e.getY());
dp.repaint();
break;
}
case "Ellipse":
{
Ellipse2D ep=(Ellipse2D)(dp.tempshape.s);
ep.setFrame(ep.getX(), ep.getY(), e.getX()-ep.getX(), e.getY()-ep.getY());
dp.repaint();
break;
}
case "Rectangle":
{
Rectangle2D ra=(Rectangle2D)(dp.tempshape.s);
ra.setFrame(ra.getX(), ra.getY(), e.getX()-ra.getX(), e.getY()-ra.getY());
dp.repaint();
break;
}
}
}
@Override
public void mouseClicked(MouseEvent e){//鼠標點擊
//插入文本
if(e.getButton()==MouseEvent.BUTTON3)
{
String inputValue = JOptionPane.showInputDialog("Please input text");
Font f=new Font("宋體",20,20);
Dstr ms=new Dstr();
ms.s=inputValue;
ms.c=dp.currColor;
ms.f=f;
ms.x=e.getX();
ms.y=e.getY();
dp.da.strlist.add(ms);
dp.repaint();
}
}
}
class MMA extends MouseMotionAdapter{
private static final long serialVersionUID = 1L;
DrawPanel dp;
MMA(DrawPanel dp)
{
super();
this.dp=dp;
}
@Override
public void mouseDragged(MouseEvent e) {//鼠標拖動
//僅自由繪畫模式觸發
if(dp.model.equals("Draw"))
{
if(dp.tempshape!=null) {
Line2D l=(Line2D)(dp.tempshape.s);
l.setLine(l.getX1(), l.getY1(), e.getX(), e.getY());
dp.da.shapelist.add(dp.tempshape);
}
dp.tempshape=new Dshape();
Line2D l=new Line2D.Double();
l.setLine(e.getX(), e.getY(), e.getX(), e.getY());
dp.tempshape.c=dp.currColor;
dp.tempshape.s=l;
dp.repaint();
}
}
}
class DrawPanel extends JPanel{
DArray da= new DArray();
Dshape tempshape;
Color currColor;
boolean fill=false;
String model=new String();
DrawPanel()
{
super();
MA ma=new MA(this);
this.addMouseListener(ma);
MMA mma=new MMA(this);
this.addMouseMotionListener(mma);
//設置監聽器
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d=(Graphics2D) g;
for(Dshape ms:da.shapelist) {
g2d.setColor(ms.c);
if(ms.f&&!ms.m.equals("Line"))
g2d.fill(ms.s);
else
g2d.draw(ms.s);
}//繪製已記錄的圖形
for(Dstr ms:da.strlist) {
g2d.setColor(ms.c);
g2d.setFont(ms.f);
g2d.drawString(ms.s,ms.x,ms.y);
}//插入已記錄的文本
if(tempshape!=null) {
g2d.setColor(tempshape.c);
if(tempshape.f&&!tempshape.m.equals("Line"))
g2d.fill(tempshape.s);
else
g2d.draw(tempshape.s);
}//記錄當前繪製的圖形(若非null)
}
}
class MTLis implements ActionListener{
DrawBoard bo;
MTLis(DrawBoard bo)
{
super();
this.bo=bo;
}
@Override
public void actionPerformed(ActionEvent ae) {
// TODO Auto-generated method stub
if(ae.getSource().getClass()==JButton.class)
{
JButton mid=(JButton)ae.getSource();
if(mid.getText().equals("Open"))
{
JFileChooser jf=new JFileChooser();
jf.showOpenDialog(bo);
String s=jf.getSelectedFile().toString();
File f=new File(s);
try {
read(f);
} catch (ClassNotFoundException | IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
bo.dpl.repaint();
bo.dpl.setBackground(bo.dpl.da.backColor);
bo.message.append("File open "+s+"\n");
}//加載文件並將其繪製出來,同時設置背景色
if(mid.getText().equals("Save"))
{
if(bo.dpl.tempshape!=null)
bo.dpl.da.shapelist.add(bo.dpl.tempshape);
JFileChooser jf=new JFileChooser();
int n=jf.showSaveDialog(bo);
String s=jf.getSelectedFile().toString();
File f=new File(s);
try {
write(f);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
bo.message.append("File save "+s+"\n");
}//記錄當前繪製圖形,序列化保存
if(mid.getText().equals("Color")) {
JColorChooser jc=new JColorChooser();
Color c=jc.showDialog(bo, null, null);
bo.dpl.currColor=c;
bo.message.append("Color set "+c.toString()+"\n");
}//更改前端顏色
if(mid.getText().equals("Fill"))
{
bo.dpl.fill=!bo.dpl.fill;
bo.message.append("Fill change "+bo.dpl.fill+"\n");
}//更改填充狀態
if(mid.getText().equals("BackgroundColor")) {
JColorChooser jc=new JColorChooser();
Color c=jc.showDialog(bo, null, null);
bo.dpl.setBackground(c);
bo.dpl.da.backColor=c;
bo.message.append("BackgroundColor set "+c.toString()+"\n");
}//更改背景色
if(mid.getText().equals("Draw"))
{
bo.dpl.model="Draw";
bo.message.append("Model change "+bo.dpl.model+"\n");
}
if(mid.getText().equals("Line"))
{
bo.dpl.model="Line";
bo.message.append("Model change "+bo.dpl.model+"\n");
}
if(mid.getText().equals("Ellipse"))
{
bo.dpl.model="Ellipse";
bo.message.append("Model change "+bo.dpl.model+"\n");
}
if(mid.getText().equals("Rectangle"))
{
bo.dpl.model="Rectangle";
bo.message.append("Model change "+bo.dpl.model+"\n");
}//更改繪畫模式
}
}
void write(File f) throws IOException
{
FileOutputStream fo=new FileOutputStream(f);
@SuppressWarnings("resource")
ObjectOutputStream oo=new ObjectOutputStream(fo);
oo.writeObject(bo.dpl.da);
}//序列化寫
void read(File f) throws IOException, ClassNotFoundException
{
FileInputStream fi=new FileInputStream(f);
@SuppressWarnings("resource")
ObjectInputStream oi=new ObjectInputStream(fi);
DArray dp=(DArray)oi.readObject();
bo.dpl.da=dp;
}//序列化讀
}
public class DrawBoard extends JFrame{
JPanel menu;
JButton open,save,color,fill,bgc;
JPanel tool;
JButton draw,line,el,rect;
DrawPanel dpl;
JTextArea message;
JScrollPane frame;
DrawBoard(String title)
{
super();
this.setTitle(title);
menu=new JPanel(new GridLayout(1,5));
open=new JButton("Open");
save=new JButton("Save");
color=new JButton("Color");
fill=new JButton("Fill");
bgc=new JButton("BackgroundColor");
menu.add(open);
menu.add(save);
menu.add(color);
menu.add(fill);
menu.add(bgc);
tool=new JPanel(new GridLayout(4,1));
draw=new JButton("Draw");
line=new JButton("Line");
el=new JButton("Ellipse");
rect=new JButton("Rectangle");
tool.add(draw);
tool.add(line);
tool.add(el);
tool.add(rect);
dpl=new DrawPanel();
String m="Please click right mouse button where you wanna to input text\n";
message=new JTextArea(m,3,5);
frame = new JScrollPane(message);
this.getContentPane().add("North",menu);
this.getContentPane().add("West",tool);
this.getContentPane().add("Center",dpl);
this.getContentPane().add("South",frame);
addli();
this.setSize(800,500);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setVisible(true);
}
void addli()
{
MTLis m =new MTLis(this);
open.addActionListener(m);
save.addActionListener(m);
color.addActionListener(m);
fill.addActionListener(m);
bgc.addActionListener(m);
draw.addActionListener(m);
line.addActionListener(m);
el.addActionListener(m);
rect.addActionListener(m);
}//加入監聽器
public static void main(String[]arg)
{
DrawBoard d=new DrawBoard("Test");
}
}