JFC/Swing活學活用之建立自訂映像組件

來源:互聯網
上載者:User
建立

  引言

  本文將講述如何應用JFC/Swing內建的映像組件來建立完全自訂的基於映像的使用者介面。

  大多數Swing應用程式是通過標準VM提供的,或者是客戶提供的外觀和感覺(L&F)來擷取它們的外在展示。L&F是一個完整的體系架構,VM需要做很多內在的工作,並且它還不是完全自訂的。舉個例子來說吧,在基於L&F的前提下,我們可以建立一個按鈕,看起來有點像交通崗上的"紅燈",隨之而來的在你的應用中所有的按鈕就都有了這樣的"相貌"。然而有時我們所真正需要的就是一個看起來與映像完全一樣的按鈕,就像Web上面基於映像的按鈕一樣。

  為了讓大家更好的瞭解我們所要介紹的內容,先來看一下最終的顯示效果,如下圖所示:一個帶面板(Panel)的窗格(Frame)包含了一個標籤(Label),一個按鈕(Button),一個複選框(Checkbox)。面板、標籤和按鈕完全由映像繪製,完全沒有使用到標準的L&F。複選框是一個標準的Checkbox,但它將被設計成是透明的以搭配映像背景。



  第一步 建立背景Panel

  要完成這"天堂"般的工程,首先我們要做的是建立一個映像背景。因為這樣的組件可重用性(reusable)很高,所以我們建立了JPanel類的一個子類,叫做ImagePanel,參考下面的程式碼範例:

  範例程式碼1:

package com.demo.jcomponents;

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

/**
* 建立映像面板
* @author xiazhi
*/
public class ImagePanel extends JPanel
{
 /**
 * 繪圖物件
 */
 private Image img;

 /**
 * 建構函式
 *
 * @param img 映像對象
 */
 public ImagePanel(String img)
 {
  this(new ImageIcon(img).getImage());
 }

 /**
 * 建構函式
 *
 * @param img 映像對象
 */
 public ImagePanel(Image img)
 {
  this.img = img;

  // 定義映像尺寸
  Dimension size = new Dimension(img.getWidth(null), img.getHeight(null));
  setPreferredSize(size);
  setMinimumSize(size);
  setMaximumSize(size);
  setSize(size);

  // 定義布局方式為空白
  setLayout(null);
 }

 /**
 * 重畫畫布
 */
 public void paintComponent(Graphics g)
 {
  g.drawImage(img, 0, 0, null);
 }
}
  建構函式使用Image的執行個體作為參數,並將繪製的映像儲存在變數img中以備以後使用。接著調用setSize()和setPreferredSize()方法,並以映像的尺寸作為參數。這樣可以確保Panel的尺寸與映像的尺寸完全一致。接下來的操作非常重要,必須顯示地指定Panel的preferred、maximum和minimum尺寸,因為Panel類的父類和子類可能不會使用絕對布局方式。

  小提示:我們都知道,Swing是使用布局管理器(Layout Manager)來控制組件的位置,絕對布局的意思就是不使用布局管理器來控制組件的位置。(可以通過setLayout(null)方法來指定採用絕對布局方式)

  既然這樣,顯示指定的尺寸和位置將會被使用(可以通過setSize()和setLocation()方法)。當使用指定的布局管理器時,preferred,minimum和maximum尺寸可能會被使用。為了適應上面所有的情況,我們只要簡單的設定上面所提及的四個方法就可以了。

  現在,Panel已經設定了適當的尺寸,我們可以通過重載paintComponent()方法來繪製映像:

public void paintComponent(Graphics g)
{
 g.drawImage(img, 0, 0, null);
}
  小提示:在這裡我們重載了paintComponent()方法,而不是paint()方法,這是很重要的一點,要不然子類將不會被重新繪製。

  現在來測試一下我們工作的成果,我們將自訂的Panel添加到一個Frame中,然後顯示該Frame,參考下面的程式碼範例:

  範例程式碼2:

package com.demo.jcomponents;

import javax.swing.*;

/**
* 測試映像Panel組件
* @author xiazhi
*/
public class ImageTest1
{
 public static void main(String[] args)
 {

  ImagePanel panel = new ImagePanel(createImageIcon("images/background.png").getImage());

  JFrame frame = new JFrame("JFC/Swing:建立以映像為主題的組件");
  frame.getContentPane().add(panel);

  frame.pack();
  frame.setVisible(true);
 }

 protected static ImageIcon createImageIcon(String path)
 {
  java.net.URL imgURL = ImageTest1.class.getResource(path);
  if (imgURL != null)
  {
   return new ImageIcon(imgURL);
  }
  else
  {
   System.err.println("不能找到指定檔案: " + path);
   return null;
  }
 }
}
  程式運行後,顯示效果如下:

  第二步 建立映像Label

  現在背景的繪製工作已經完成了。接下來要將重點轉移到標籤"Activate Reactor"的製作上了。這裡僅僅是將一個靜態映像放置在背景的合適位置上。我們當然可以使用另外一個ImagePanel執行個體來實現這部分功能,但是由於"Activate Reactor"這個幾個文字實際上僅僅是一個標籤,所以我們建立了另外一個子類ImageLabel,參考下面的程式碼範例:

  範例程式碼3:

package com.demo.jcomponents;

import javax.swing.*;

/**
*映像標籤
* @author xiazhi
*/
public class ImageLabel extends JLabel
{
 /**
 * 建構函式
 *
 * @param img 映像對象
 */
 public ImageLabel(String img)
 {
  this(new ImageIcon(img));
 }

 /**
 * 建構函式
 *
 * @param icon 映像表徵圖對象
 */
 public ImageLabel(ImageIcon icon)
 {
  //設定標籤表徵圖
  setIcon(icon);
  //設定標籤表徵圖和文本之間的間隔
  setIconTextGap(0);
  //設定邊框
  setBorder(null);
  //設定文本
  setText(null);
  setSize(icon.getImage().getWidth(null), icon.getImage().getHeight(null));
 }
}
  與ImagePanel類似,我們需要將Label的尺寸與映像的尺寸相匹配。這裡我們只需要調用setSize()方法即可,因為Label自己會處理其它的設定。接下來,將表徵圖設定為我們指定的映像,這樣Label自己會處理映像的繪製。通過設定文本間隔為0,以及設定邊框為空白,設定文本為空白,將會去除映像的任何額外的空間,將會使Label與背景完美的齧合在一起。setOpaque(false)方法告訴Label不要自己繪製背景。如果用映像填充Label,那麼問題不大,但如果映像中含有透明地區(大部分PNG類型的映像都會這個樣子),那麼在透明地區背景將會顯示出來。

  現在來測試一下我們的成果吧!在上面測試的基礎上添加了一個Label,參考下面的程式碼範例:

  範例程式碼4:

ImageLabel label = new ImageLabel(createImageIcon("images/reactor.png"));
//定位Label
label.setLocation(29, 37);
//為Label增加提示資訊
label.setToolTipText("看到了嗎?");
//將Label添加到Panel中
panel.add(label);
  程式運行後,顯示效果如下所示:

  第三步 建立映像Button

  接下來是建立定製的Button。由於Button具有翻轉特性和狀態特性,所以繪製Button需要一些技巧。我們再次建立了一個JButton類的子類,參考下面的程式碼範例:

  範例程式碼5:

package com.demo.jcomponents;

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

/**
* 映像按鈕
* @author xiazhi
*/
public class ImageButton extends JButton
{
 /**
 * 建構函式
 *
 * @param img 映像執行個體
 */
 public ImageButton(String img)
 {
  this(new ImageIcon(img));
 }

 /**
 * 建構函式
 *
 * @param icon 映像表徵圖
 */
 public ImageButton(ImageIcon icon)
 {
  //設定表徵圖
  setIcon(icon);
  //設定空白間距
  setMargin(new Insets(0, 0, 0, 0));
  //設定文本與表徵圖之間的間隔
  setIconTextGap(0);
  //指定是否繪製邊框
  setBorderPainted(false);
  //設定邊框
  setBorder(null);
  //設定文本
  setText(null);
  setSize(icon.getImage().getWidth(null), icon.getImage().getHeight(null));
 }
}
  這段代碼幾乎與前面定製顯示的JLabel的代碼完全一樣。唯一的不同之處在於增加了setMargin()和setBorder()方法的調用。大多數L&F使用邊框(Border)和邊界(Margin)來指明Button是否已經被選取中。因為Label不能被選取擇,所以沒有上述方法。不管怎麼樣,我們只要將這兩個屬性關閉就可以了。

  現在來測試一下我們的成果吧!在上面測試的基礎上添加了一個Button,參考下面的程式碼範例:

  範例程式碼6:

final ImageButton button = new ImageButton(createImageIcon("images/button.png"));
//定位Button
button.setLocation(60,74);
//將Button添加到Panel中
panel.add(button);
  程式運行後,顯示效果如下所示:


  現在Button已經添加到Panel中,剩下的只要將翻轉和其它狀態屬性添到到Button中就可以了。幸運的是,這些工作不需要我們在子類中添加任何新的代碼。JButton已經提供了通過映像來表徵翻轉、按下、選擇、失效、失效選擇等屬性。我們只需要使用通常的set方法來添加各種狀態變數,參考下面的程式碼範例:

button.setPressedIcon(createImageIcon("images/button-down.png"));
button.setRolloverIcon(createImageIcon("images/button-over.png"));
button.setSelectedIcon(createImageIcon("images/button-sel.png"));
button
.setRolloverSelectedIcon(createImageIcon("images/button-sel-over.png"));
button.setDisabledIcon(createImageIcon("images/button-disabled.png"));
button
.setDisabledSelectedIcon(createImageIcon("images/button-disabled-selected.png"));
  添加上述代碼後,再次運行程式,顯示效果如下所示:



選擇

未選擇

  在這裡我們使用了帶有光圈的映像來表示Button被選中,將映像模糊化來表示Button被禁用,映像中間的矩形條用來表示Button被選中的狀態,除了有顏色的改變外,還有發光的效果。

  為了完整的示範所有的狀態,我們在Button的下部增加了一個標準的JCheckBox,通常情況下,它將會繪製一個灰色的背景(或者是帶條紋的背景在Mac機上),我們調用setOpaque(false)方法來強制要求它不要繪製。當父類沒有使用布局管理器時,調用checkbox.setSize(checkbox.getPreferredSize())方法是必須的,這樣可以使checkbox獲得合適的尺寸,就像本文樣本中的情形:

  範例程式碼7:

final JCheckBox checkbox = new JCheckBox("Disable");
checkbox.setLocation(70, 150);
//強制要求checkbox不要繪製自己的背景
checkbox.setOpaque(false);
//設定checkbox的尺寸
checkbox.setSize(checkbox.getPreferredSize());
//添加到Panel中
panel.add(checkbox);
//添加事件監聽器
checkbox.addActionListener(new ActionListener()
{
 public void actionPerformed(ActionEvent evt)
 {
  button.setEnabled(!checkbox.isSelected());
 }
});
  到此樣本程式已經完整了,程式運行後,整體的顯示效果如下所示:



  結束語

  至此,我們建立了完全定製的基於映像的組件,JFC/Swing有著龐大的結構,如何更好的去理解,理清組件之間的微妙關係,是學好Swing的關鍵。

相關文章

Beyond APAC's No.1 Cloud

19.6% IaaS Market Share in Asia Pacific - Gartner IT Service report, 2018

Learn more >

Apsara Conference 2019

The Rise of Data Intelligence, September 25th - 27th, Hangzhou, China

Learn more >

Alibaba Cloud Free Trial

Learn and experience the power of Alibaba Cloud with a free trial worth $300-1200 USD

Learn more >

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。