1. 程式人生 > >Java圖形使用者介面

Java圖形使用者介面

JFrame

       JFrame是個代表螢幕上window的物件。可以把button、checkbox、text欄位等介面放在window上面。標準的menu也可以加到上面。

import javax.swing.*;
public class SimpleGui1 {

    public static void main(String[] args) {
        JFrame frame = new JFrame(); //建立frame
        JButton button = new JButton("click me");  //建立button

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //這一行程式會在window關閉時結束程式

        frame.getContentPane().add(button);

        frame.setSize(300, 300); //設定frame大小

        frame.setVisible(true);  //顯示frame
    }

}

實現按鈕功能

實現按鈕功能:

  • 按鈕要知道它的作用。
  • 按鈕要在按鍵事件發生時呼叫執行功能的方法。

取得使用者的事件

       在Java中,取得處理使用者操作事件的過程稱為even-handling。Java中有許多不同的事件型別,大多數都與GUI上使用者的操作有關。如果使用者按下了按鈕,就會產生事件。

監聽

       如果想要知道按鈕的事件,就要監聽事件的介面。
       監聽介面是介於監聽和事件源間的橋樑。

       Swing的GUI元件是事件的來源。以Java的術語說,事件來源是個可以將使用者操作(點選滑鼠、關閉視窗等)轉換成事件的物件。對Java而言,事件幾乎都是以物件來表示(事件類物件)。
       事件源(例如按鈕)會在使用者做出相關動作時(按下按鈕)產生事件物件。你的程式在大多數情況下是事件的接受方而不是建立方。也就是說,你會花較多的時間當監聽者而不是事件來源。
       每個事件型別都有相對應的監聽者介面,想要接收MuoseEvent的話就實現Mouse Listener這個介面。記得介面的規則:要實現介面就得宣告這件事,這代表你必須把介面中所有

方法都實現出來。

監聽和事件源的溝通

監聽

       如果類想要知道按鈕的ActionEvent,就要實現ActionListener這個介面。按鈕需要知道你關注的部分,因此要通過呼叫addActionListener(this)並傳入ActionListener的引用(下面的例子裡就是你自己的這個程式,所以用this)來向按鈕註冊。按鈕會在該事件發生時呼叫該介面上的方法。作為一個ActionListener,編譯器會確保你實現此介面的actionPerformed()。

事件源

       按鈕是ActionEvent的來源,因此它必須要知道有哪些物件是需要事件通知的。此按鈕有個addActionListener()方法可以提供對事件有興趣的物件(listener)一種表達此興趣的方法。
       當按鈕的addActionListener()方法被呼叫是(因為某個listener的呼叫),它的引數會被按鈕存到清單中。當用戶按下按鈕時,按鈕會通過呼叫清單上的每個監聽的actionPerformed()來啟動事件。

取得按鈕的ActionEvent

  1. 實現ActionListener這個介面。
  2. 向按鈕註冊(告訴它你要監聽事件)。
  3. 定義事件處理方法(實現介面的方法)。
import javax.swing.*;
import java.awt.event.*;
public class SimpleGui1B implements ActionListener {
    JButton button;

    public static void main(String[] args) {
        SimpleGui1B gui = new SimpleGui1B();
        gui.go();
    }

    public void go() {
        JFrame frame = new JFrame();
        button = new JButton("Click me");

        button.addActionListener(this); //向按鈕註冊

        frame.getContentPane().add(button);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(300, 300);
        frame.setVisible(true);
    }

    public void actionPerformed(ActionEvent event) { //實現介面中的方法,真正處理事件的方法
        button.setText("I've been clicked");
    }
}

圖形

在GUI上加東西的3種方法:

  1. 在frame上放置widget:
    加上按鈕、窗體、radio button 等。

  2. 在widget上繪製2D圖形:
    使用graphics物件來繪製圖形。

  3. 在widget上放置JPEG圖

建立繪圖元件

       如果要在螢幕上放上自己的圖形,最好的方式是自己創建出有繪圖功能的widget。把它放在frame上,就像按鈕或其他widget一樣,不同之處是它會按照你所要的方式繪製。

建立Jpanel的子類並覆蓋掉paintComponent()這個方法

       所有繪圖程式程式碼都在paintComponent()裡面。當你的panel所處的frame顯示的時候,paintComponent()就會被呼叫。使用者不能自己呼叫這個方法!它的引數是個跟實際螢幕有關的Graphics物件,使用者無法取得這個物件,必須交由系統來交給你。然而,還是可以呼叫repaint()類要求系統重新繪製顯示裝置,然後產生paintComponent()呼叫。

import java.awt.*;
import javax.swing.*;
public class MyDrawPanel extends JPanel {//建立JPanel的子類

    public void paintComponent(Graphics g) {//只能由系統呼叫
        g.setColor(Color.blue);

        g.fillRect(20, 50, 100, 100);
    }
        public static void main (String[] args) {
            JFrame frame = new JFrame(); //建立frame
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            MyDrawPanel s = new MyDrawPanel();
            frame.getContentPane().add(s);

            frame.setSize(300, 300);
            frame.setVisible(true);             
        }
}

其他在paintComponent()中可以做的事情

顯示JPEG

import java.awt.*;
import javax.swing.*;
public class MyDrawPanel extends JPanel {//建立JPanel的子類

    public void paintComponent(Graphics g) {//只能由系統呼叫
        Image image = new ImageIcon("224888289124380709.jpg").getImage();//注意這裡的根路徑是project目錄

        g.drawImage(image, 3, 4, this);
    }
        public static void main (String[] args) {
            JFrame frame = new JFrame(); //建立frame
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            MyDrawPanel s = new MyDrawPanel();
            frame.getContentPane().add(s);

            frame.setSize(300, 300);
            frame.setVisible(true);             
        }
}

在黑色背景上畫隨機色彩的圓圈

import java.awt.*;
import javax.swing.*;
public class MyDrawPanel extends JPanel {//建立JPanel的子類

    public void paintComponent(Graphics g) {//只能由系統呼叫
        g.fillRect(0, 0, this.getWidth(), this.getHeight());

        int red = (int) (Math.random() * 255);
        int green = (int) (Math.random() * 255);
        int blue = (int) (Math.random() * 255);

        Color randomColor = new Color(red, green ,blue); //隨機顏色
        g.setColor(randomColor); //設定畫的顏色
        g.fillOval(400, 400, 200, 200);
    }
        public static void main (String[] args) {
            JFrame frame = new JFrame(); //建立frame
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            MyDrawPanel s = new MyDrawPanel();
            frame.getContentPane().add(s);

            frame.setSize(300, 300);
            frame.setVisible(true);             
        }
}

使用Graphics2D物件

paintComponent()的引數被宣告為Graphics型別:

public void paintComponent(Graphics g)

因此引數g是個Graphics物件,由於多型,g也有可能是Graphics的子類。事實上,由g引數所引用的物件實際上是個Graphics2D的例項

如果要呼叫Graphics2D類的方法,就不能直接使用g引數。要將其轉換成Graphics2D變數:

Graphics2D g2d = (Graphics2D) g; 

Graphics2D的方法更多:

2d

import java.awt.*;
import javax.swing.*;
public class MyDrawPanel extends JPanel {//建立JPanel的子類

    public void paintComponent(Graphics g) {//只能由系統呼叫
        Graphics2D g2d = (Graphics2D) g;

        int red = (int) (Math.random() * 255);
        int green = (int) (Math.random() * 255);
        int blue = (int) (Math.random() * 255);

        Color startColor = new Color(red, green ,blue);

        red = (int) (Math.random() * 255);
        green = (int) (Math.random() * 255);
        blue = (int) (Math.random() * 255);

        Color endColor = new Color(red, green ,blue);

        GradientPaint gradient = new GradientPaint(400,400, startColor,600,600, endColor);//兩點之間座標漸變
        g2d.setPaint(gradient);
        g2d.fillOval(400, 400, 200, 200);
    }
        public static void main (String[] args) {
            JFrame frame = new JFrame(); //建立frame
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            MyDrawPanel s = new MyDrawPanel();
            frame.getContentPane().add(s);

            frame.setSize(800, 800);
            frame.setVisible(true);             
        }
}

在獲得事件時繪製圖形

GUI佈局:

frame預設有5個區域可以安置widget:

位置

按下按鈕圓圈就會改變顏色

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class MySimpleGui3C implements ActionListener {
    JFrame frame;
    public static void main(String[] args) {
        MySimpleGui3C gui = new MySimpleGui3C();
        gui.go();
    }

    public void go() {
        frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JButton button = new JButton("change colors");
        button.addActionListener(this);

        MyDrawPanel s = new MyDrawPanel();
        frame.getContentPane().add(BorderLayout.SOUTH, button);//兩個引數的add方法可以指定位置
        frame.getContentPane().add(BorderLayout.CENTER,s);

        frame.setSize(800, 800);
        frame.setVisible(true);     


    }

    public void actionPerformed(ActionEvent event) {
        frame.repaint();
    }

} 

內部類

一個類可以巢狀在另一個類的內部。內部類可以使用外部所有的方法與變數,就算是private的也一樣。

利用內部類實現兩個按鈕的程式

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class TwoButton {
    JFrame frame;
    JLabel label; //必須放在方法以外,否則是區域性變數不能用
    public static void main(String[] args) {
        TwoButton gui = new TwoButton();
        gui.go();
    }

    public void go() {
        frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JButton labelButton = new JButton("change label");
        labelButton.addActionListener(new LabelListener());

        JButton colorButton = new JButton("change color");
        colorButton.addActionListener(new ColorListener());

        label = new JLabel("I'm a label");
        MyDrawPanel drawPanel = new MyDrawPanel();

        frame.getContentPane().add(BorderLayout.SOUTH, colorButton);
        frame.getContentPane().add(BorderLayout.EAST, labelButton);
        frame.getContentPane().add(BorderLayout.CENTER, drawPanel);
        frame.getContentPane().add(BorderLayout.WEST, label);

        frame.setSize(800, 800);
        frame.setVisible(true);
    }

    class LabelListener implements ActionListener {//內部類,可以呼叫label
        public void actionPerformed(ActionEvent event) {
            label.setText("Ouch!");
        }
    }

    class ColorListener implements ActionListener {//內部類,可以呼叫frame
        public void actionPerformed(ActionEvent event) {
            frame.repaint();
        }
    }

}

以內部類執行動畫效果

import javax.swing.*;
import java.awt.*;

public class SimpleAnimation {
    int x = 70;
    int y = 70;

    public static void main(String[] args) {
        SimpleAnimation gui = new SimpleAnimation();
        gui.go();
    }

    public void go() {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        MyDrawPanel drawPanel = new MyDrawPanel();

        frame.getContentPane().add(drawPanel);
        frame.setSize(800, 800);
        frame.setVisible(true);

        for (int i = 0; i < 130; i++) {
            x++;//遞增座標值
            y++;//遞增座標值
            drawPanel.repaint();//重新繪製

            try {
                Thread.sleep(50);//加上延遲放緩過程
            } catch(Exception ex) { }
        }
    }

    class MyDrawPanel extends JPanel {
        public void paintComponent(Graphics g) {
            //去除痕跡
            g.setColor(Color.WHITE);
            g.fillRect(0, 0, this.getWidth(), this.getHeight());

            g.setColor(Color.green);
            g.fillOval(x, y, 40, 40);//x,y是橢圓左上角座標,40,40是高度和寬度
        }
    }

}

這樣使用內部類可以讓外部類實現無法繼承的子類的方法。