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
- 實現ActionListener這個介面。
- 向按鈕註冊(告訴它你要監聽事件)。
- 定義事件處理方法(實現介面的方法)。
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種方法:
在frame上放置widget:
加上按鈕、窗體、radio button 等。在widget上繪製2D圖形:
使用graphics物件來繪製圖形。在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的方法更多:
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是高度和寬度
}
}
}
這樣使用內部類可以讓外部類實現無法繼承的子類的方法。