java畫板的設計和創建
首先創建一個畫板類繼承容器類,這樣可以在畫板類中重寫容器的paint方法。
public class DrawFrame extends JPanel
再創建一個類寫監聽器的程序,以及一個類來寫畫板的內容對象參數保存的方法(可以在最小化以及伸縮窗口的時候使畫面內容得到恢復)
public class DrawMouse implements MouseListener, ActionListener,MouseMotionListener
public class Shape
我們可以看到我們會用到鼠標監聽器,動作監聽器,以及鼠標動作監聽器。
我們先簡單地介紹一下他們的作用:
1.鼠標監聽器可以對於點擊,按下,釋放等事件做出響應,響應具體內容由程序員自己定義,發生相應的行為時即會觸發對應方法,由此可見,監聽器的作用很大,可以實現無限的可能。
2.動作監聽器我們目前用於響應對按鈕的點擊事件,讀取按鈕上的名稱或者顏色信息來實現相應的方法。
3鼠標動作監聽器可以實現對鼠標進入界面,離開界面,界面內拖拽等功能的實現,極大豐富了界面特殊功能的實現。
接下來進入主程序,主程序很簡單,只是一個窗體構造方法的調用。
public static void main(String[] args) {
DrawFrame frame = new DrawFrame();
frame.showUI();
}
javax.swing.JFrame jf = new javax.swing.JFrame();
jf.setSize(800, 800);
jf.getContentPane().setBackground(Color.WHITE);//設置背景色
jf.setTitle("畫板1.0");
jf.setDefaultCloseOperation(3);
// 設置居中顯示
jf.setLocationRelativeTo(null)
DrawMouse mouse = new DrawMouse();
jf.setLayout(new BorderLayout());
JPanel jp1= new JPanel();
jp1.setBackground(Color.green);
jf.add(jp1,BorderLayout.NORTH);
在showUI裏先是對一些基本組件的添加和創建,這裏就不再一一贅述。值得一提的是其中容器的添加以及邊框布局的規則。jf就是一個巨大的容器對象,一般來說,如果我們不定義流式布局,便會默認邊框布局。邊框布局可以把區域劃分為5個部分,north,south,center,east,west。我們把按鈕放在了上方,把畫板主題部分放在中間,這樣兩個區域便互不幹擾,是兩個獨立的容器。這些對象的父類便是JPanel類。
this.setBackground(Color.WHITE);
jf.add(this, BorderLayout.CENTER);
使用this即可調用,默認以容器類來創建對象(容器可以創建多個對象)。
String[] shape ={"直線","矩形","三角形","橢圓","任意邊形","曲線","橡皮擦","叠代圖像","遞歸"};
for(int i=0;i<shape.length;i++){
JButton jbu = new JButton(shape[i]);
jp1.add(jbu);
jbu.addActionListener(mouse);
}
Color[] color = {Color.RED,Color.BLUE,Color.BLACK};
for(int i=0;i<color.length;i++){
JButton jbu = new JButton();
jbu.setBackground(color[i]);
jbu.setPreferredSize(new Dimension(30, 30));
jp1.add(jbu);
jbu.addActionListener(mouse);
}
循環創建按鈕可以在多按鈕需求的時候剩下不少力氣。循環創建按鈕並為其添加動作監聽器。
jf.setVisible(true);
//獲取畫筆對象:圖形畫在那個組件上,畫筆就從該組件上獲取
//從窗體上獲取畫筆對象
Graphics g = this.getGraphics();
//給窗體添加鼠標監聽器方法
this.addMouseListener(mouse);
this.addMouseMotionListener(mouse);
mouse.setGr(g);
mouse.setArrayShape(arrayShape);
窗體可視化並獲取畫筆對象後我們添加鼠標監聽器。這裏用到了傳對象的方法,來使得在監聽器的類中可以使用畫筆和對象數組。對象數組的創建是為了保存每一個圖畫信息的坐標及顏色信息,便於復原。
private Graphics gr;
private int x1, y1, x2, y2, x3, y3;
private double x;
private double y;
private int xf,yf,xl,yl,x0,y0;
private String name;
private int flag=0;
private int flagq=0;
private int step=1;
private int flag_line=0;
private Color color = Color.black;
private double a=-1.2;
private double b=1.6;
private double c=-1;
private double d=-1.5;
private int index=0;
private Shape[] arrayShape;
在監聽器裏定義了很多屬性,這些屬性在編程實現特殊功能的時候十分有用,可作為開關,可用於傳遞對象,可作為方法內的參數來調用等,這裏不一一贅述。
public void mouseClicked(MouseEvent e) {
if ("任意邊形".equals(name)){
if(flag==0)
{
x0 = e.getX();
y0 = e.getY();
xf = e.getX();
yf = e.getY();
flag=1;
}
else
{
xl = e.getX();
yl = e.getY();
gr.drawLine(xf, yf, xl, yl);
set_shape(xf, yf, xl, yl,"直線",color);
xf=xl;
yf=yl;
if(e.getClickCount()==2)
{
flag=0;
gr.drawLine(x0, y0, xl, yl);
set_shape(x0, y0, xl, yl,"直線",color);
}
}
}
if("三角形".equals(name)){
System.out.println(step);
switch(step)
{
case 1:
x1=e.getX();
y1=e.getY();
step++;
break;
case 2:
x2=e.getX();
y2=e.getY();
gr.drawLine(x1, y1, x2, y2);
step++;
break;
case 3:
x3=e.getX();
y3=e.getY();
set_shape(x1,y1,x2,y2,"直線",color);
set_shape(x2,y2,x3,y3,"直線",color);
set_shape(x1,y1,x3,y3,"直線",color);
step=1;
gr.drawLine(x2, y2, x3, y3);
gr.drawLine(x1, y1, x3, y3);
break;
default:;
}
}
if("叠代圖像".equals(name))
{
x=e.getX();
y=e.getY();
iterate(x,y);
//set_shape((int)x, (int)y, 0, 0,"叠代圖像",color);
}
if("遞歸".equals(name))
{
int xa=1+(int)(Math.random()*800);
int ya=1+(int)(Math.random()*800);
int xb=1+(int)(Math.random()*800);
int yb=1+(int)(Math.random()*800);
int xc=1+(int)(Math.random()*800);
int yc=1+(int)(Math.random()*800);
int xp=1+(int)(Math.random()*800);
int yp=1+(int)(Math.random()*800);
int turn=1;
System.out.println("xa"+xa+"ya"+ya+"xb"+xb+"yb"+yb+"xc"+xc+"yc"+yc+"xp"+xp+"yp"+yp);
for(int i=0;i<10000;i++)
{
turn=1+(int)(Math.random()*3);
switch(turn)
{
case 1:
xp=(xa+xp)/2;
yp=(ya+yp)/2;
gr.drawLine(xp, yp, xp, yp);
break;
case 2:
xp=(xb+xp)/2;
yp=(yb+yp)/2;
gr.drawLine(xp, yp, xp, yp);
break;
case 3:
xp=(xc+xp)/2;
yp=(yc+yp)/2;
gr.drawLine(xp, yp, xp, yp);
break ;
}
}
}
}
在click事件裏獲取坐標值,再判斷當前name類型,實現畫三角形和畫任意邊形等操作。click 雙擊還可自動補全缺線,結束多邊形的繪制。因為鼠標事件的實現非常復雜,且與按下,釋放聯系緊密,單獨介紹不便於一一解釋,我們就提一些有趣的想法,然後剩下的就留讀者思考和設計創造了。
public void mousePressed(MouseEvent e) {
System.out.println("按下");
if("矩形".equals(name)||"橢圓".equals(name))
{
x1 = e.getX();
y1 = e.getY();
}
//System.out.println(name);
if ("曲線".equals(name))
{
flagq=1;
//System.out.println(flagq);
xf=e.getX();
yf=e.getY();
}
if("直線".equals(name))
{
x0=e.getX();
y0=e.getY();
x1=e.getX();
y1=e.getY();
flag_line=1;
}
}
public void mouseReleased(MouseEvent e) {
System.out.println("釋放");
if("矩形".equals(name)||"橢圓".equals(name))
{
x2 = e.getX();
y2 = e.getY();
}
/*if ("直線".equals(name)) {
// 畫線
gr.drawLine(x1, y1, x2, y2);
}*/
if ("矩形".equals(name)) {
gr.drawRect(Math.min(x1, x2), Math.min(y1, y2), Math.abs(x1 - x2), Math.abs(y1 - y2));
set_shape(Math.min(x1, x2), Math.min(y1, y2), Math.abs(x1 - x2), Math.abs(y1 - y2),"矩形",color);
}
if("橢圓".equals(name)){
gr.drawOval(Math.min(x1, x2),Math.min(y1, y2), Math.abs(x1-x2),Math.abs(y1-y2));
set_shape(Math.min(x1, x2),Math.min(y1, y2), Math.abs(x1-x2),Math.abs(y1-y2),"橢圓",color);
}
if ("曲線".equals(name))
{
flagq=0;
System.out.println(flagq);
}
if("直線".equals(name))
{
flag_line=0;
set_shape(x0,y0,x2,y2,"直線",color);
}
}
按下和釋放可以獲取兩個坐標,可以輕松地畫出直線,矩形等形狀,三角形和多邊形的實現用到了click事件(這裏關於x1,y1,x2,y2,x3,y3變量的使用十分巧妙,可以作為前一個點,和後一個點來理解,實現連續畫多線)。
public void actionPerformed(ActionEvent e) {
if("".equals(e.getActionCommand())){
//獲取當前事件源
JButton jbu = (JButton)e.getSource();
//獲取按鈕的背景色
color = jbu.getBackground();
//設置畫筆顏色
gr.setColor(color);
}else{
// 獲取按鈕內容
name = e.getActionCommand();
}
System.out.println("name = " + name);
}
按鈕功能的實現代碼貼出。
public void mouseDragged(MouseEvent e)
{
//System.out.print("x坐標:"+e.getX()+" y坐標:"+e.getY());
if (flagq==1)
{
xl=e.getX();
yl=e.getY();
gr.drawLine(xf, yf, xl, yl);
set_shape(xf, yf, xl, yl,"直線",color);
xf=xl;
yf=yl;
}
if("橡皮擦".equals(name))
{
x1=e.getX();
y1=e.getY();
gr.setColor(Color.white);
gr.fillRect(x1-5, y1-5, 10, 10);
gr.setColor(color);
//gr.clearRect(x1-5, y1-5, 10, 10);
set_shape(x1-5, y1-5, 10, 10,"橡皮擦",color);
}
if("直線".equals(name)&&flag_line==1)
{
x2=e.getX();
y2=e.getY();
gr.setColor(Color.white);
gr.drawLine(x0, y0, x1, y1);
gr.setColor(color);
gr.drawLine(x0, y0, x2, y2);
x1=x2;
y1=y2;
}
}
這段代碼有兩個地方很有意思,一個是直線的繪制。我最初使用的是press和release,這樣在鼠標拖動的過程中直線是不可見的,我們把其在鼠標拖動事件中實現,及在按下的時候保存坐標初值,然後不斷獲取鼠標位置,畫新的線,用背景色覆蓋原先的線,實現直線的實時顯示。還有一個就是畫曲線,及不斷保存前後點的坐標,畫很多短線,實現曲線的效果。
private Shape[] arrayShape=new Shape[9000];
public void set_shape(int x1,int y1,int x2,int y2,String name,Color color)
{
Shape shape=new Shape(x1,y1,x2,y2,name,color);
arrayShape[index++] = shape;
}
關於圖像的復原,我們的思路是把每一步設置為對象保存在shape中,然後當最小化再打開,或者拖拽窗體時,再調用這個數組取出其中的內容,實現畫面的恢復。
public Shape(int x1,int y1,int x2,int y2,String name,Color color)
{
this.x1=x1;
this.y1=y1;
this.x2=x2;
this.y2=y2;
this.name=name;
this.color=color;
}
public void drawShape(Graphics g)
{
switch(name)
{
case "直線":
g.setColor(color);
g.drawLine(x1, y1, x2, y2);
// System.out.println("畫");
break;
case "矩形":
g.setColor(color);
g.drawRect(x1, y1, x2, y2);
break;
case "橢圓":
g.setColor(color);
g.drawOval(x1, y1, x2, y2);
break;
case "橡皮擦":
g.setColor(color);
g.clearRect(x1, y1, x2, y2);
break;
}
public void paint(Graphics g)
{
super.paint(g);
for(int i=0;i<arrayShape.length;i++)
{
Shape shape=arrayShape[i];
if(shape!= null)
shape.drawShape(g);
else break;
}
}
這是涉及到參數保存和取出的方法。
以上代碼便實現了我們的畫板了,以畫板為基礎我們接下來還可以實現很多界面的設計,在畫板的學習過程中充分使用了監聽器這個工具,程序的可設計性大大提高了。
java畫板的設計和創建