1. 程式人生 > >java畫板的設計和創建

java畫板的設計和創建

rgs bsp 實現 getc 有意思 按鈕 在那 釋放 click

首先創建一個畫板類繼承容器類,這樣可以在畫板類中重寫容器的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畫板的設計和創建