1. 程式人生 > >java圖形化Swing教程(二)

java圖形化Swing教程(二)

與多執行緒、泛型等不同,Swing主要在於使用。
下面主要放程式碼和註釋,少說話。

(一)共用程式碼

所有繼承JFrame的類都要有一個開始的框架來包含他們,這裡直接給出框架,以下幾點中不會重複書寫。

package Event;

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

/**
 * 
 * @author QuinnNorris
 * 共用程式碼
 */
public class BaseFrame {
    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
// 開啟一個執行緒,所有的Swing元件必須由事件分派執行緒進行配置,執行緒將滑鼠點選和按鍵控制轉移到使用者介面元件。 EventQueue.invokeLater(new Runnable() { // 匿名內部類,是一個Runnable介面的例項,實現了run方法 public void run() { JFrame frame = new *********(); // 將*號的地方換成你實現的JFrame的子類,來實現你的程式碼 frame.setExtendedState(Frame.MAXIMIZED_BOTH); // 將視窗最大化
frame.setTitle("Christmas"); // 設定視窗標題 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 選擇當用戶關閉框架的時候進行的操作 ,在有些時候需要將視窗隱藏,不能直接退出需要用到這個方法 frame.setVisible(true); // 將視窗可見化,這樣以便使用者在第一次看見視窗之前我們能夠向其中新增內容 } }); } }

輸出結果:得到一個基本的公用框架,以後的內容可以在其他類中書寫,放入這個框架中。

(二)按鈕事件

package Event;

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

/**
 * 
 * @author QuinnNorris 按鈕事件
 */
public class ButtonFrame extends JFrame {

    private JPanel buttonPanel;
    private static final int DEFAULT_WIDTH = 300;
    private static final int DEFAULT_HEIGHT = 200;

    public ButtonFrame() {
        setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
        // 設定預設寬度和高度

        buttonPanel = new JPanel();
        // 將類的例項域中的JPanel面板物件例項化

        makeButton("yellow",Color.YELLOW);
        makeButton("blue",Color.BLUE);
        makeButton("red",Color.RED);
        //呼叫makeButton方法來建立並新增按鈕

        add(buttonPanel);
        // 我們將這個新增好按鈕的面板新增到原框架中


    }

    /**
     * 通過方法來建立按鈕,並且完成關聯監視器和新增入面板的操作
     * @param name 建立按鈕的標識
     * @param backgroundColor 點選按鈕後改變的顏色,匿名內部類只能訪問final修飾的變數,所以要用final
     */
    public void makeButton(String name,final Color backgroundColor){

        JButton colorButton = new JButton(name);
        //通過不同的標識名,我們建立按鈕

        buttonPanel.add(colorButton);
        //我們將建立的按鈕新增到面板中

        //匿名內部類,建立一個ActionListener的例項
        colorButton.addActionListener(new ActionListener(){

            /**
             * 當按鈕點選的時候,會自動的呼叫actionPerformed方法
             */
            public void actionPerformed(ActionEvent event) {

                buttonPanel.setBackground(backgroundColor);
                // 呼叫setBackground方法,設定背景顏色
            }
        });
    }

}

輸出結果:在一個全屏視窗中,上方有三個不同顏色的按鈕,按下去後,背景顏色會隨之改變。

(三)更改主題

package Event;

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

import java.awt.event.*;

/**
 * 
 * @author QuinnNorris 更換主題
 */
public class MetalFrame extends JFrame {

    private JPanel buttonPanel;

    private static final int DEFAULT_WIDTH = 300;
    private static final int DEFAULT_HEIGHT = 200;

    public MetalFrame() {

        setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
        // 設定預設寬度和高度

        buttonPanel = new JPanel();
        // 將類的例項域中的JPanel面板物件例項化

        UIManager.LookAndFeelInfo[] infos = UIManager
                .getInstalledLookAndFeels();
        // 呼叫這個靜態方法,我們獲得所有主題

        makeButton(infos);
        // 呼叫makeButton方法來將主題實現

        add(buttonPanel);
        // 我們將這個新增好按鈕的面板新增到原框架中
    }

    /**
     * 通過方法來建立所有的主題按鈕,並且將他們關聯監視器
     * 
     * @param infos
     *            包含有所有型別主題的陣列
     */
    private void makeButton(final UIManager.LookAndFeelInfo[] infos) {

        for (UIManager.LookAndFeelInfo info : infos) {
            JButton button = new JButton(info.getName());
            // 用for-each迴圈來遍歷所有的主題

            final UIManager.LookAndFeelInfo innerInfo = info;
            // 將info複製並且定義位final型別,便於內部類的使用

            buttonPanel.add(button);
            // 我們將建立的按鈕新增到面板中

            // 匿名內部類,建立一個ActionListener的例項
            button.addActionListener(new ActionListener() {

                /**
                 * 當按鈕點選的時候,會自動的呼叫actionPerformed方法
                 */
                public void actionPerformed(ActionEvent ae) {

                    try {

                        UIManager.setLookAndFeel(innerInfo.getClassName());
                        // 呼叫setLookAndFeel方法,更改主題

                        SwingUtilities.updateComponentTreeUI(buttonPanel);
                        // 通過這個靜態方法,將更改的主題立即應用

                    } catch (Exception e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            });
        }

    }

}

輸出結果:視窗上方有五個主題按鈕,分別寫著他們各自主題的名字,點選後會立即更改所屬主題。

(四)按鈕與擊鍵動作

package Event;

import javax.swing.*;

import java.awt.*;
import java.awt.event.*;

/**
 * 
 * @author QuinnNorris 按鈕與擊鍵動作
 */
public class ActionFrame extends JFrame {

    private JPanel buttonPanel;

    private static final int DEFAULT_WIDTH = 300;
    private static final int DEFAULT_HEIGHT = 200;

    public ActionFrame() {

        setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
        // 設定預設寬度和高度

        buttonPanel = new JPanel();
        // 將類的例項域中的JPanel面板物件例項化

        Action yellowAction = new ColorAction("Yellow", Color.YELLOW);
        // 建立一個自己定義的ColorAction物件yellowAction

        buttonPanel.add(new JButton(yellowAction));
        // 建立一個按鈕,其屬性從所提供的 Action中獲取

        add(buttonPanel);
        // 我們將這個新增好按鈕的面板新增到原框架中

        InputMap imap = buttonPanel
                .getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
        // 我們將JPanel物件的InputMap設定為第二種輸入對映,並建立該物件

        imap.put(KeyStroke.getKeyStroke("ctrl Y"), "panel.yellow");
        // 在imap中通過呼叫擊鍵類KeyStroke的靜態方法設定擊鍵輸入ctrl+Y的組合
        // 第二個引數是一個標誌引數,將這對引數用鍵值對的形式存入imap

        ActionMap amap = buttonPanel.getActionMap();
        // 我們不能將InputMap直接和Action做對映,我們需要用ActionMap做過渡
        // 用JPanel中的getACtionMap方法獲得amap物件

        amap.put("panel.yellow", yellowAction);
        // 將imap中標記引數對應的擊鍵組合和相應的Action組合起來
    }

    public class ColorAction extends AbstractAction {

        /**
         * ColorAction的構造器,存放鍵值對
         * @param name 按鈕的名稱
         * @param c 按鈕對應點選後顯示的顏色
         */
        public ColorAction(String name, Color c) {
            putValue(Action.NAME, name);
            putValue(Action.SHORT_DESCRIPTION,
                    "Set panel color to " + name.toLowerCase());
            putValue("color", c);
            //在構造器中設定一些鍵值對對映,這些設定的屬性將會被JPanel讀取
        }

        /**
         * 當按鈕點選或擊鍵的時候,會自動的呼叫actionPerformed方法
         */
        public void actionPerformed(ActionEvent event) {
            Color c = (Color) getValue("color");
            buttonPanel.setBackground(c);
            // 呼叫setBackground方法,設定背景顏色
        }
    }
}

輸出結果:視窗上方有yellow按鈕,通過點選或者ctrl+Y都可以使背景變黃,滑鼠懸停在上面一段時間會顯示說明文字。

(五)滑鼠操作

package Event;

import javax.swing.*;

/**
 * 
 * @author QuinnNorris
 * 繼承JFrame的子類,將Component物件內容打包
 */
public class MouseFrame extends JFrame{

    public MouseFrame(){
        add(new MouseComponent());
        //向框架中新增一個JComponent的例項
        pack();
    }
}
package Event;

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

/**
 * 
 * @author QuinnNorris 滑鼠點選
 */
public class MouseComponent extends JComponent {

    private static final int SIDELENGTH = 100;
    // 定義創造的正方形的邊長

    private ArrayList<Rectangle2D> squares;
    // 宣告一個正方形集合

    private Rectangle2D current;

    // java類庫中用來描述矩形的類,它的物件可以看作是一個矩形

    /**
     * 構造器方法,做初始化工作
     */
    public MouseComponent() {

        squares = new ArrayList<>();
        current = null;

        addMouseListener(new MouseHandler());
        // 新增一個我們實現的類,這個類繼承了監測滑鼠點選情況的MouseListener

        addMouseMotionListener(new MouseMotionHandler());
        // 新增另一個實現類,這個類繼承了監測滑鼠移動情況的MouseMotionListener
    }

    /**
     * 我們覆蓋了這個以用來列印圖形,將會被自動呼叫
     * 
     * @param g
     *            Graphics是我們要使用的2D的一個父類
     */
    public void paintComponent(Graphics g) {

        Graphics2D g2 = (Graphics2D) g;
        // 轉換為我們需要使用的型別

        for (Rectangle2D r : squares)
            g2.draw(r);
        // 對陣列中的每個正方形,都進行繪製
    }

    /**
     * 判斷在這個座標上是否有圖形
     * 
     * @param p
     *            一對當前的x,y的座標值
     * @return 返回存在的圖形或者null
     */
    public Rectangle2D find(Point2D p) {

        for (Rectangle2D r : squares) {
            if (r.contains(p))
                //contains方法測定座標是否在圖形的邊界內

                return r;
        }
        // 如果在squares這個陣列中有這個位置的座標,表明這個位置上非空
        // 返回這個位置上的物件

        return null;
        // 否則如果什麼都沒有,返回null
    }

    /**
     * 在這個座標位置增加一個圖形
     * @param p 座標位置
     */
    public void add(Point2D p) {

        double x = p.getX();
        double y = p.getY();
        //獲取x和y的座標

        current = new Rectangle2D.Double(x - SIDELENGTH / 2,
                y - SIDELENGTH / 2, SIDELENGTH, SIDELENGTH);
        //用獲得的座標和既定的邊長構建一個新的正方形,並將其賦值給current

        squares.add(current);
        //將current新增到squares佇列中

        repaint();
        //重繪影象

    }

    /**
     * 將這個位置的圖形移除
     * @param s 代表所要移除的矩形物件
     */
    public void remove(Rectangle2D s) {
        if (s == null)
            return;
        //如果要移除的內容為空,直接返回

        if (s == current)
            current = null;
        //如果要移除的內容和current正指向的內容相同,則將current清空
        //避免發生不必要的錯誤

        squares.remove(s);
        //將s從squares的列表中直接刪去

        repaint();
        //重繪影象
    }

    /**
     * 
     * @author QuinnNorris 繼承了MouseAdapter類的滑鼠點選控制類
     */
    private class MouseHandler extends MouseAdapter {

        /**
         * 單擊滑鼠操作
         */
        public void mousePressed(MouseEvent event) {

            current = find(event.getPoint());
            // 將滑鼠單擊的這個點的座標的物件賦給current

            if (current == null)
                //如果這個點沒有物件,當前指向空的位置

                add(event.getPoint());
                //在這個點繪製正方形
        }

        /**
         * 滑鼠雙擊操作
         */
        public void mouseClicked(MouseEvent event) {

            current = find(event.getPoint());
            // 將滑鼠單擊的這個點的座標的物件賦給current

            if (current != null && event.getClickCount() >= 2)
                //如果這個點有物件,而且點選滑鼠的次數大於2

                remove(current);
                //移除current

        }
    }

    /**
     * 
     * @author QuinnNorris
     * 滑鼠移動拖動類
     */
    private class MouseMotionHandler implements MouseMotionListener {

        /**
         * 滑鼠游標移動到元件上,但未按下時呼叫
         */
        public void mouseMoved(MouseEvent event) {

            if (find(event.getPoint()) == null)
                //如果滑鼠所在位置不是空

                setCursor(Cursor.getDefaultCursor());
                //則將游標的影象設定為預設的影象
            else
                setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
                //如果當前位置有影象,則將游標樣式設定為手型
        }

        /**
         * 滑鼠游標在元件上按下,並拖動時呼叫
         */
        public void mouseDragged(MouseEvent event) {

            if (current != null) {
                //因為在呼叫這個方法之前肯定會呼叫點選滑鼠的方法
                //所以我們直接判斷:如果現在current不為空

                int x = event.getX();
                int y = event.getY();
                //獲取到座標

                current.setFrame(x - SIDELENGTH / 2, y - SIDELENGTH / 2,
                        SIDELENGTH, SIDELENGTH);
                //最後在滑鼠放下時進行圖形繪製

                repaint();
                //重繪影象
            }
        }
    }

}

輸出結果:在這個窗體中,單擊一次滑鼠會建立一個100畫素為邊長的小正方形。在正方形內,雙擊或更多次點選會刪除這個正方形。可以點住正方形並進行拖動,並且在指示正方形區域內時,滑鼠指標變為手型指標。