Skip to content

Latest commit

 

History

History
410 lines (288 loc) · 12.6 KB

swing.md

File metadata and controls

410 lines (288 loc) · 12.6 KB

Swing 簡介

SwingJava平臺下的傳統GUI庫。 Swing是Java標準庫的一部分,包含於Java SE中。

與上代圖形技術AWT的比較:

  • AWT使用各平臺原生控件,在各平臺上的原生控件上做了基礎的封裝。
  • Swing控件完全由Java繪製。
  • AWT控件因平臺而異,相同控件在各OS中表現不盡相同。
  • Swing控件在各OS下具有統一的外觀樣式與功能。
  • AWT使用原生控件,執行效率高。
  • Swing使用自繪控件,執行效率低下(在現代版本的Java中已有較大改善)。

Scala Swing

Scala標準庫中提供了Java Swing對應的Scala API封裝:Scala Swing

在sbt項目中添加Scala Swing依賴:

libraryDependencies += "org.scala-lang.modules" %% "scala-swing" % "版本號"

常用控件

Java Swing提供的控件位於javax.swing包路徑下,命名均以字母J起始。

Scala Swing對Java Swing提供的多數控件進行了淺層的封裝,使之更符合Scala的API風格。 Scala Swing控件位於scala.swing路徑下,名稱爲對應的Java Swing控件去掉首字母J

容器類控件:

Java Swing 控件名稱 Scala Swing 控件名稱 控件簡介
JFrame Frame 頂層窗體控件
JDialog Dialog 對話框窗體
JPanel Panel 面板
JScrollPane ScrollPane 滾動面板

JFrame/Frame一般做爲頂層容器,可以獨立做爲對話框顯示,但JPanel/Panel不能獨立做爲對話框。

文本控件:

Java Swing 控件名稱 Scala Swing 控件名稱 控件簡介
JLabel Label 文本標籤
JTextField TextField 單行文本框
JTextArea TextArea 多行文本框
JPasswordField PasswordField 密碼文本框(可設定回顯字符)

按鈕控件:

Java Swing 控件名稱 Scala Swing 控件名稱 控件簡介
JButton Button 普通按鈕
JCheckBox CheckBox 複選框
JRadioButton RadioButton 單選框
JComboBox ComboBox 組合框(下拉列表框)

多個JRadioButton/RadioButton需要添加到同一個ButtonGroup中才能實現單選效果。

菜單欄:

Java Swing 控件名稱 Scala Swing 控件名稱 控件簡介
JMenubar Menubar 菜單欄
JMenu Menu 菜單欄上的按鈕
JMenuItem MenuItem 點擊菜單按鈕彈出的菜單項

常用的表單組件都有對應的菜單版本,比如JRadioButtonMenuItemJCheckBoxMenuItem等。 向JMenuadd()菜單項時,雖然可以選擇普通組件,但普通組件不能觸發菜單效果(點按之後菜單不收回)。

代碼風格

在Java Swing中,常見的創建控件、配置控件的方式爲先構建對象實例,之後調用實例的成員方法設置控件屬性。

使用Java Swing創建窗口,在窗口中添加控件:

JFrame frame = new JFrame();
JLabel label = new JLabel();

label.setText("Test");
label.setMinimumSize(new Dimension(80, 40));

frame.add(label);
frame.setSize(new Dimension(200, 100));
frame.setVisible(true);

在Scala Swing中,創建控件、配置控件通常同步進行,使用匿名類特性,在創建控件時直接在構造方法中設定控件的各類屬性。

Scala Swing的API更貼近Scala的語言風格。 使用Scala Swing構建配置控件語法更加類似DSL,可讀性更高。

上述Java Swing例子使用Scala Swing代碼如下:

val frame = new Frame {
  contents = new Label {
    text = "Test"
    minimumSize = new Dimension(80, 40)
  }
  size = new Dimension(200, 100)
  visible = true
}

Border

javax.swing.border.Border類型定義了邊框的接口。

設置邊框

常見GUI容器類如JFrameJPanel等都可以通過setBorder()方法來設置邊框:

public void setBorder(Border border);

BorderFactory

工廠類BorderFactory提供了一系列創建各類常見樣式邊框的靜態方法。

  • EmptyBorder

    EmptyBorder爲僅佔據面板空間的透明邊框。 通過使用EmptyBorder,可以實現控制控件邊界空隙的效果(類似於CSS中的Margin屬性)。

    創建EmptyBorder的靜態方法:

     // 創建默認樣式的 EmptyBorder
     public static Border createEmptyBorder();
     // 創建指定邊距的 EmptyBorder
     public static Border createEmptyBorder(int top, int left, int bottom, int right);
  • TitledBorder

    TitledBorder爲框線上帶有標題的邊框。 Swing中並未提供默認的GroupBox控件,但可以對JPanel等控件設定TitledBorder來實現類似效果。

    創建TitledBorder的靜態方法:

     public static TitledBorder createTitledBorder(Border border, String title,
     		int titleJustification, int titlePosition, Font titleFont, Color titleColor);

JTextArea

JTextField僅適用於顯示簡單的單行文本,涉及到多行文本時,應使用JTextArea控件:

JTextArea textArea = new JTextArea();

可以設置文本域自動換行

textArea.setLineWrap(true);

當文本域內容太多無法全部顯示時,可以使用JScrollPane控件,將文本域添加到其中:

JScrollPane scrollPane = new JScrollPane(textArea);

當文本無法全部顯示時會出現滾動條

MessageBox

Qt類似,Swing也提供了彈出MessageBox的靜態方法,即JOptionPane.showMessageDialog()

// 調出標題爲"Message"的信息消息對話框
static void showMessageDialog(Component parentComponent, Object message);
// 調出對話框,顯示使用由messageType參數確定的默認圖標的message
static void showMessageDialog(Component parentComponent,
		Object message, String title, int messageType);
// 調出一個顯示信息的對話框,指定了所有參數
static void showMessageDialog(Component parentComponent,
		Object message, String title, int messageType, Icon icon);
  • parentComponent參數爲父對話框。
  • messageType參數控制對話框的內置按鈕,可取值DEFAULT_OPTIONYES_NO_OPTION等。
  • message參數爲在對話框中顯示的消息內容。

使用showInputDialog()showConfirmDialog()等方法可以用於顯示其他用途的窗口,參數類似。

事件機制

Java沒有類似C#的語言級別事件機制。

Java Swing的事件機制採用Observer模式。 Scala Swing在事件處理上採用了Reactor模式,與Java Swing風格不同。

Java Swing 事件機制

Java Swing的事件機制主要包括以下部分:

  1. 監聽器,包含對事件的處理邏輯:

    所有的的監聽源都實現了java.util.EventListener接口。 EventListener接口是空接口,Java Swing根據不同的事件類型定義了一系列繼承於EventListener的子接口。

    不同的監聽器接口定義了不同的抽象方法,當對應的監聽事件觸發時,對應方法會被調用。 通過重寫監聽器接口的抽象方法來實現事件處理邏輯。

  2. 事件源,即觸發事件的控件:

    事件源是某個具體的控件對象。 控件對象通過綁定監聽器對象在事件觸發時調用對應監聽器對象的重寫方法。

    Java Swing中的控件都提供了命名類似的方法用於與監聽器交互:

    // 綁定到指定監聽器實例
    public synchronized void addXxxListener(XxxListener listener);
    // 移除到指定監聽器實例的綁定
    public synchronized void removeXxxListener(XxxListener listener);
    // 獲取當前控件已綁定的所有監聽器實例
    public synchronized XxxListener[] getXxxListeners();

    不同的控件類型根據其控件職能會擁有不同類型監聽器的交互方法。

  3. 事件,包含特定的事件信息:

    所有的事件都繼承自java.util.EventObject

    事件保存了具體的某一次事件發生時的事件信息。 事件做爲監聽器抽象方法的參數,當事件觸發時,對應的事件信息做爲參數傳入。

    以按鈕控件的ActionEvent爲例,實現ActionEvent的處理需要以下步驟:

    1. 構建ActionEvent監聽器,監聽器實現ActionListener接口,重寫抽象方法actionPerformed()
    2. 按鈕控件對象調用addActionListener()方法,將控件綁定監聽器。

    如下代碼所示:

    // 構建監聽器,ActionListener只有單個抽象方法,爲函數式接口
    ActionListener action = e -> ...;
    
    JButton button = new JButton();
    
    // 將控件綁定監聽器
    button.addActionListener(action);

常見監聽器類型應用:

  1. KeyListener (鍵盤按鍵監聽器)

    通過鍵盤監聽器可屏蔽指定按鍵輸入。

    實現KeyListener接口,重寫keyTyped()方法。 對KeyEvent類型的事件參數調用getKeyChar()方法獲取輸入的字符,判斷輸入內容。 對需要屏蔽的輸入使用setKeyChar('\0')轉化爲空輸入。

    如下所示:(只接受數字輸入)

    KeyListener keyListener = e -> {
    	if (e.getKeyChar() < '0' || e.getKeyChar() > '9') e.setKeyChar('\0');
    }

Scala Swing 事件機制

Scala Swing中,事件採用集中式處理,所有被監聽的控件發出的各類事件會被彙總統一處理。

Scala Swing所有控件的基類scala.swing.UIElement都間接混入了事件發佈者特質scala.swing.Publisher

trait UIElement extends Proxy with LazyPublisher {
  ...
}

private[swing] trait LazyPublisher extends Publisher {
  ...
}

Publisher特質繼承於反應器特質scala.swing.Reactor,該特質定義了用於發佈事件的publish()方法:

trait Publisher extends Reactor {
  ...
  def publish(e: Event) { ... }
  ...
}

Reactor特質定義了與訂閱者的交互方法,使用listenTo()添加訂閱者,deafTo()移除訂閱者。 Reactor特質定義了字段reactions,類型爲scala.swing.Reactions

trait Reactor {
  ...
  val reactions: Reactions = ...
  def listenTo(ps: Publisher*) = ...
  def deafTo(ps: Publisher*) = ...
  ...
}

Reactions爲抽象類,繼承於自身單例對象中定義的類型別名Reactions.Reaction(實際類型爲偏函數PartialFunction[Event, Unit])。 Reactions抽象類定義了用於增減偏函數的方法+-()-=()

object Reactions {
  ...
  type Reaction = PartialFunction[Event, Unit]
  ...
}

abstract class Reactions extends Reactions.Reaction {
  ...
  def += (r: Reactions.Reaction): this.type
  def -= (r: Reactions.Reaction): this.type
  ...
}

reactions字段添加自定義的事件處理偏函數來處理UI事件。 reactions字段添加的偏函數參數爲scala.swing.event.Event,返回值類型爲Unit

Event特質是所有Scala Swing事件類型的基類。 事件類型與Java Swing中類似,但使用了Scala的樣例類特性,便於在事件處理偏函數中使用。

ActionEvent爲例,在Scala Swing中實現ActionEvent事件的處理:

val button = new Button

// 監聽控件
listenTo(button)

// 偏函數添加 ActionEvent 事件處理邏輯
reactions += {
  case ActionEvent(source) => ...
}

MVC

Swing中的組件採用了MVC的設計模式,對於JListJComboBoxJTable等控件均可通過組裝Model構建對象並顯示內容。

ComboBox

獲取目標Map的Key集合:

Set set = map.keySet();

接着將集合轉化爲對象數組:

Object[] object = set.toArray();

接着構造一個用對象數組初始化的DefaultComboBoxModel對象,並以此構建JComoBox對象:

JComboBox comboBox = new JComboBox(new DefaultComboBoxModel(object));

JTable

構建一個JTable主要有兩種方式:

JTable(Object[][] rowData, Object[] columnNames);
JTable(TableModel dm);

即使用Object數組確定表格模型或是使用TableModel類構建表格模型。 使用對象數組構建表格模型可以先從數組庫中讀取對應數據,然後將數據存儲在對象數組中。

使用TableModel的基本步驟:

  1. 構建TableMode對象。
  2. 使用TableMode類的成員方法setValueAt(Object aValue, int rowIndex, int columnIndex)設定表格模型每個位置的數據。
  3. 使用JTable構造函數或在已有的JTable實例調用setModel(TableModel dataModel)成員方法創建表格。