swing組件介紹

學習swing組件,主要有三個內容
一是組件的顯示,二是對組件支持的事件進行偵聽,三是是自定義組件
1.JFrame
JFrame是主窗口,它和JDialog,JApplet的地位並列.但是,一個JFrame可以添加JDialog和JApplet進去它的內容面板,而反過來就不行
下面來看JFrame的例子
=================================================
package blog.swing;
import javax.swing.*;
import java.awt.event.*;

class  JFrameDemo
{
 JFrame mainFrame;
 
public JFrameDemo() {
  mainFrame 
= new JFrame ( "JFrameDemo Title" ); //創建一個JFrame 
  mainFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );//設置關閉動作
  mainFrame.setSize( 300,300 );//設置窗口大小
  mainFrame.setLocationRelativeTo(null);//使窗口顯示在屏幕中央

  mainFrame.addWindowListener( 
new WindowListener(){
   
public void windowOpened( WindowEvent e ){ System.out.println( "window opened" ); }
   
public void windowClosing( WindowEvent e ){ System.out.println( "window closing" ); }
   
public void windowClosed( WindowEvent e ){ System.out.println( "window closed" ); }
   
public void windowIconified( WindowEvent e ){ System.out.println( "window iconified" ); }
   
public void windowDeiconified( WindowEvent e ){ System.out.println( "window deiconified" ); }
   
public void windowActivated( WindowEvent e ){ System.out.println( "window activated" ); }
   
public void windowDeactivated( WindowEvent e ){ System.out.println( "window deactivated" ); }
  });
  mainFrame.addWindowFocusListener( 
new WindowFocusListener(){
   
public void windowGainedFocus( WindowEvent e ){ System.out.println( "gained focus" ); }
   
public void windowLostFocus( WindowEvent e ){ System.out.println( "lost focus" ); }
  });
  mainFrame.addWindowStateListener( 
new WindowStateListener(){
   
public void windowStateChanged( WindowEvent e ){ System.out.println( "state changed" ); }
  });

  mainFrame.setVisible( 
true );
 }
 
public static void main(String[] args) 
 {
  
new JFrameDemo();
 }
}

==========================================================
這裏出現了三個不同的窗口事件偵聽器,並實現了它們所有的方法
WindowListener和WindowFocusListener都可以對窗口失去,獲得焦點進行偵聽,不同的是,非幀窗口和對話框窗口不能接收WindowListener的windowActivated和windodwDeactivated事件
雖然可以用WindowListener對窗口的一些狀態進行偵聽,但是WindowStateListener提供了更多的支持.例如,WindowStateListener可以處理還原窗口的事件,可以判斷一個窗口是不是在垂直和水平兩個方向都可以最大化(命令提示符窗口只可以在垂直方向上最大化),而這些都是WindowListener都無能爲力
2.JLabel
JLabel是一標籤.在它的文本里嵌入html標籤,可以簡單實現一個超鏈接組件
package blog.swing;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.io.*;
class  JLabelDemo
{
 JFrame mainFrame;
 JLabel simpleLabel;
 
public JLabelDemo() {
  mainFrame 
= new JFrame ( "JLabelDemo" );
  simpleLabel 
= new JLabel ("<html><a href=aaa>百度搜索</a></html>");//嵌入了html標籤
  simpleLabel.addMouseListener( new MouseAdapter(){//添加鼠標事件偵聽器,當單擊標籤時,打開網頁
   public void mouseClicked( MouseEvent e ){
    
try{
     Runtime.getRuntime().exec(
"cmd /c start [url]http://www.baidu.com[/url]");
    }
catch( IOException ee ){
     ee.printStackTrace();
    }
   }
  });
  simpleLabel.setCursor( 
new Cursor(Cursor.HAND_CURSOR) );//設置手形鼠標
  mainFrame.getContentPane().add( simpleLabel );//將標籤添加到窗口
  mainFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
  mainFrame.pack();
//使窗口自動根據添加了的組件調整大小
  mainFrame.setLocationRelativeTo(null);
  mainFrame.setVisible( 
true );
 }
 
public static void main(String[] args) 
 {
  
new JLabelDemo();
 }
}
3.JButton
JButton是一個按鈕.它和JLabel一樣的簡單
package blog.swing;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.io.*;

class JButtonDemo 
{
 JFrame mainFrame;
 JButton simpleButton;
 
public JButtonDemo() {
  mainFrame 
= new JFrame ( "JButtonDemo" );
  simpleButton 
= new JButton("百度搜索");  
  mainFrame.getContentPane().add( simpleButton );
  simpleButton.addActionListener( 
new ActionListener(){//添加動作偵聽器,當按鈕被按下時執行這裏的代碼以打開網頁
   public void actionPerformed( ActionEvent e){
    
try{
     Runtime.getRuntime().exec(
"cmd /c start [url]http://www.baidu.com[/url]");
    }
catch( IOException ee ){
     ee.printStackTrace();
    }
   }
  });
  simpleButton.setCursor( 
new Cursor(Cursor.HAND_CURSOR) );
  mainFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
  mainFrame.pack();
  mainFrame.setLocationRelativeTo(
null);
  mainFrame.setVisible( 
true );
 }
 
public static void main(String[] args) 
 {
  
new JButtonDemo();
 }
}

4.JTextField
一個文本框
package blog.swing;
import javax.swing.*;
import java.awt.event.*;

class JTextFieldDemo 
{
 JFrame mainFrame;
 JTextField simpleTextField;
 
public JTextFieldDemo() {
  mainFrame 
= new JFrame ( "JTextFieldDemo" );
  simpleTextField 
= new JTextField(20);//構造寬度爲20個字符的文本框  
  mainFrame.getContentPane().add( simpleTextField );
  simpleTextField.addActionListener( 
new ActionListener(){//在輸入字符後按回車執行行代碼,在標準輸出窗口輸出它的內容
   public void actionPerformed( ActionEvent e){
    System.out.println( simpleTextField.getText() );
   }
  });
  mainFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
  mainFrame.pack();
  mainFrame.setLocationRelativeTo(
null);
  mainFrame.setVisible( 
true );
 }
 
public static void main(String[] args) 
 {
  
new JTextFieldDemo();
 }
}

5.JTextArea
文本區域,與文本框不同的是它是多行的
package blog.swing;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;

class JTextAreaDemo 
{
 JFrame mainFrame;
 JTextArea simpleTextArea;
 JButton appendButton;
 
public JTextAreaDemo() {
  mainFrame 
= new JFrame ( "JTextAreaDemo" );
  simpleTextArea 
= new JTextArea(10,20);//創建一個顯示10行20列的文本域
  simpleTextArea.setLineWrap(true);//設置它自動換行
  simpleTextArea.setWrapStyleWord(true);//設置它自動換行時根據單詞換行,而不是根據字符
  appendButton = new JButton ("append text to the text area");
  mainFrame.getContentPane().add( simpleTextArea,BorderLayout.PAGE_START );
  mainFrame.getContentPane().add( appendButton,BorderLayout.PAGE_END );
  appendButton.addActionListener( 
new ActionListener(){
   
public void actionPerformed( ActionEvent e){
    simpleTextArea.append(
"button appended text here");
    System.out.println( simpleTextArea.getText() );
   }
  });
  mainFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
  mainFrame.pack();
  mainFrame.setLocationRelativeTo(
null);
  mainFrame.setVisible( 
true );
 }
 
public static void main(String[] args) 
 {
  
new JTextAreaDemo();
 }
}

6.JPasswordField
package blog.swing;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;

class JPasswordFieldDemo 
{
 JFrame mainFrame;
 JPasswordField simplePasswordField;
 
public JPasswordFieldDemo() {
  mainFrame 
= new JFrame ( "JPasswordFieldDemo" );
  simplePasswordField 
= new JPasswordField(10);
  simplePasswordField.setEchoChar(
'*');//設定要顯示的字符
  mainFrame.getContentPane().add( simplePasswordField );
  simplePasswordField.addActionListener( 
new ActionListener(){//回車時執行的動作
   public void actionPerformed( ActionEvent e){
    
char[] input = simplePasswordField.getPassword();
    
forchar c : input )
     System.out.print( c );
   }
  });
  mainFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
  mainFrame.pack();
  mainFrame.setLocationRelativeTo(
null);
  mainFrame.setVisible( 
true );
 }
 
public static void main(String[] args) 
 {
  
new JPasswordFieldDemo();
 }
}

7.JPanel
一個面板.一般用作控制組件的佈局.
package blog.swing;
import javax.swing.*;

class JPanelDemo 
{
 JFrame mainFrame;
 JPanel simplePanel;
 JButton simpleButton;
 JLabel simpleLabel;
 
public JPanelDemo() {
  mainFrame 
= new JFrame ( "JPanelDemo" );
  simplePanel 
= new JPanel();
  simpleButton 
= new JButton ("button");
  simpleLabel 
= new JLabel ("label");
  simplePanel.add( simpleLabel );
  simplePanel.add( simpleButton );
  mainFrame.getContentPane().add( simplePanel );
  mainFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
  mainFrame.pack();
  mainFrame.setLocationRelativeTo(
null);
  mainFrame.setVisible( 
true );
 }
 
public static void main(String[] args) 
 {
  
new JPanelDemo();
 }
}

8.JCheckBox
複選框
package blog.swing;
import javax.swing.*;
import java.awt.event.*;

class JCheckBoxDemo implements ItemListener
{
 JFrame mainFrame;
 JPanel mainPanel;
 JCheckBox simpleCheckBox1;
 JCheckBox simpleCheckBox2;
 
public JCheckBoxDemo() {
  mainFrame 
= new JFrame ( "JCheckBoxDemo" );
  mainPanel 
= new JPanel ();
  simpleCheckBox1 
= new JCheckBox("checkbox1");
  simpleCheckBox1.setMnemonic(
'1');
  simpleCheckBox1.addItemListener(
this);
  simpleCheckBox2 
= new JCheckBox("checkbox2");
  simpleCheckBox2.setMnemonic(
'2');
  simpleCheckBox2.addItemListener(
this);
  mainPanel.add(simpleCheckBox1);
  mainPanel.add(simpleCheckBox2);
  mainFrame.getContentPane().add( mainPanel );
  mainFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
  mainFrame.pack();
  mainFrame.setLocationRelativeTo(
null);
  mainFrame.setVisible( 
true );
 }
 
public void itemStateChanged( ItemEvent e ){
  JCheckBox cb 
= (JCheckBox)e.getSource();
  
if( cb== simpleCheckBox1 )
   System.out.println( 
"simpleCheckBox1" );
  
else
   System.out.println( 
"simpleCheckBox2" );
 }
 
public static void main(String[] args) 
 {
  
new JCheckBoxDemo();
 }
}

9.JRadioButton
單選按鈕.單選按鈕要用到ButtonGroup.添加到同一個ButtonGroup的單選按鈕表示在它們之間只可選其一.不同ButtonGroup裏的單選按鈕相互之間的選擇不受影響.
package blog.swing;
import javax.swing.*;
import java.awt.event.*;

class JRadioButtonDemo implements ItemListener
{
 JFrame mainFrame;
 JPanel mainPanel;
 ButtonGroup buttonGroup;
 JRadioButton simpleRadioButton1;
 JRadioButton simpleRadioButton2;
 
public JRadioButtonDemo() {
  mainFrame 
= new JFrame ( "JRadioButtonDemo" );
  mainPanel 
= new JPanel ();
  simpleRadioButton1 
= new JRadioButton("RadioButton1");
  simpleRadioButton1.setMnemonic(
'1');
  simpleRadioButton1.addItemListener(
this);
  simpleRadioButton2 
= new JRadioButton("RadioButton2");
  simpleRadioButton2.setMnemonic(
'2');
  simpleRadioButton2.addItemListener(
this);
  buttonGroup 
= new ButtonGroup();
  buttonGroup.add(simpleRadioButton1);
  buttonGroup.add(simpleRadioButton2);
  mainPanel.add(simpleRadioButton1);
  mainPanel.add(simpleRadioButton2);
  mainFrame.getContentPane().add( mainPanel );
  mainFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
  mainFrame.pack();
  mainFrame.setLocationRelativeTo(
null);
  mainFrame.setVisible( 
true );
 }
 
public void itemStateChanged( ItemEvent e ){
  JRadioButton cb 
= (JRadioButton)e.getSource();
  
if( cb== simpleRadioButton1 )
   System.out.println( 
"simpleRadioButton1" );
  
else
   System.out.println( 
"simpleRadioButton2" );
 }
 
public static void main(String[] args) 
 {
  
new JRadioButtonDemo();
 }
}

10.JScrollPane
JScrollPane由四個角,兩個頭部,和一個視口組成.四個角和兩個頭部都是由Component組成.四個角並不是總是顯示出來的.左上角只有當兩個頭部同時存在才顯示,右下角只有兩個滾動條同時出現纔會出現.其他兩個角可同理知道什麼時候會出現.視口是顯示內容的部分,由JViewport對象表示.而真正顯示的內容,由JViewport的view表示
package blog.swing;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;

class JScrollPaneDemo 
{
 JFrame mainFrame;
 JScrollPane simpleScrollPane;
 JTextArea simpleTextArea;
 JButton changeViewport;
 
public JScrollPaneDemo() {
  mainFrame 
= new JFrame ( "JScrollPaneDemo" );
  simpleTextArea 
= new JTextArea(10,20);
  simpleScrollPane 
= new JScrollPane( simpleTextArea );//創建一個滾動窗格,裏面顯示的內容是文本區域
  simpleScrollPane.setRowHeaderView( new JLabel ("this is a row header") );//設置行標題
  simpleScrollPane.setColumnHeaderView( new JLabel ("this is a column header") );//設置列標題
  simpleScrollPane.setCorner( JScrollPane.LOWER_RIGHT_CORNER,new JButton("corner") );//設置右下角
  simpleScrollPane.setCorner( JScrollPane.UPPER_LEFT_CORNER,new JButton("corner") );//設置左上角
  changeViewport = new JButton ("changeViewport");
  changeViewport.addActionListener( 
new ActionListener(){//當單擊按鈕時,滾動窗口顯示的內容變爲另一個文本區域
   public void actionPerformed( ActionEvent e){
    simpleScrollPane.getViewport().setView( 
new JTextArea("changeViewpot") );
   }
  });
  mainFrame.getContentPane().add( simpleScrollPane,BorderLayout.PAGE_START );
  mainFrame.getContentPane().add( changeViewport,BorderLayout.PAGE_END );
  mainFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
  mainFrame.pack();
  mainFrame.setLocationRelativeTo(
null);
  mainFrame.setVisible( 
true );
 }
 
public static void main(String[] args) 
 {
  
new JScrollPaneDemo();
 }
}

現在開始講相對複雜一點的組件
11.JList
JList是一個列表.這裏將首次引入Model這個概念,中文翻譯是模型,不好理解,其實就是數據的意思.JList用ListModel保存它的數據.簡單應用可以用實現了ListModel的AbstraceListModel的子類DefaultListModel來保存它的數據(儘管它的名字有Default,但是隻有你使用了它創建JList,它才存在.即如果你沒有用DefaultListModel創建JList,則你用getModel()返回的ListModel不能強制轉換爲DefaultListModel).
很多組件的Model都有這種結構,即 XXXModel--AbstractXXXModel--DefaultXXXModel
下面講一下DefaultListModel,它的方法基本上和Vector<E>一樣,另外添加了一個Object getElementAt(int index)
添加元素: void add(int index, Object element),void addElement(Object obj),void insertElementAt(Object obj, int index)
刪除元素: Object remove(int index),boolean removeElement(Object obj),void removeElementAt(int index),
    void removeAllElements(),void removeRange(int fromIndex, int toIndex),void clear()
查詢元素: Object elementAt(int index),Object get(int index),Object getElementAt(int index), Object firstElement(),Object lastElement()
修改元素: Object set(int index, Object element),void setElementAt(Object obj, int index)
JList自身沒有對單個元素處理的方法,只有void setListData(Object[] listData),void setListData(Vector<?> listData),void setModel(ListModel model)
來設置全部元素的方法
當要對單個元素進行處理時,用ListModel getModel()返回Model後,再對Model操作
通過void addListDataListener(ListDataListener l)可以在ListModel被修改時被通知
和JList相關的還有ListSelectionModel,它管理JList的選擇項.它沒有AbstractListSelectionModel這個子類,而直接有一個DefaultListSelectionModel這個實現類,
JList默認也是使用的這個實現類. 和上面講的DefaultListModel不同,這個Model不用你自己創建就已存在.
通過void addListSelectionListener(ListSelectionListener x)可以在選擇改變時被通知.
void setSelectionMode(int selectionMode)可以設置多選(待續或非連續)或是單選
DefaultListSelectionModel沒有返回選擇了的元素的方法,它只負責去選擇哪些項
修改選擇項的方法:
void addSelectionInterval(int index0, int index1),
void removeSelectionInterval(int index0, int index1)
void setSelectionInterval(int index0, int index1)
void clearSelection()
下面這兩個方法在ListModel被更改時更改選擇項會更方便,因爲不用根據ListModel的變動計算索引
void removeIndexInterval(int index0, int index1)
void insertIndexInterval(int index, int length, boolean before)
另外的一些方法
int getMaxSelectionIndex(),int getMinSelectionIndex()
boolean isSelectedIndex(int index),boolean isSelectionEmpty()
很多在DefaultListSelectionModel裏的方法,在JList自身裏也有.例如上面的XXXSelectionInnterval(...)和addListSelectionListener(...)
另外它還有:
int getSelectedIndex(),int[] getSelectedIndices()
Object getSelectedValue(), Object[] getSelectedValues()
void setSelectedIndex(int index),void setSelectedIndices(int[] indices)
void setSelectedValue(Object anObject, boolean shouldScroll)
最後一個內容是,自定義JList
默認JList顯示的內容是String.在創建它的時候,如果你把其他非String的對象傳給它要它顯示,它會調用toString,然後顯示返回的String.
通過void setCellRenderer(ListCellRenderer cellRenderer)可以自定義JList顯示內容的類型.
參數是一個"渲染器".很多組件在自定義的時候都是用渲染器來實現的.它只一個方法:
Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus)
下面舉例說明一下它如何工作:假設你調用了setCellRenderer,而且把"label"這個String傳給JList要它顯示.那麼"label"這個值就會傳給上面這個方法的value這個值.
這時你可以用"label"構造一個組件,然後返回.例如,你可以JLabel label = new JLabel( (String)value ); return label.這樣,在JList就會顯示一個JLabel了.
package blog.swing;
import java.awt.*;
import javax.swing.event.*;
import javax.swing.*;
class  JListCustomDemo
{
 JFrame mainFrame;
 JList simpleList;
 
public JListCustomDemo(){
  mainFrame 
= new JFrame ("JListCustomDemo");

  
final DefaultListModel model = new DefaultListModel();
  model.addElement(
"button1");
  model.addElement(
"button2");
  simpleList 
= new JList(model);
  simpleList.setCellRenderer( 
new CustomListCellRenderer() );

  simpleList.addListSelectionListener( 
new ListSelectionListener(){
   
public void valueChanged( ListSelectionEvent e){
    System.out.println( model.getElementAt( simpleList.getSelectedIndex() ) );
   }
  });

  mainFrame.add(simpleList);
  mainFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
  mainFrame.setLocationRelativeTo(
null);
  mainFrame.pack();
  mainFrame.setVisible( 
true );
 }
 
public static void main(String[] args) 
 {
  
new JListCustomDemo();
 }
}
class CustomListCellRenderer implements ListCellRenderer{
 
public Component getListCellRendererComponent(
  JList list,
  Object value,
  
int index,
  
boolean isSelected,
  
boolean cellHasFocus
  ){
  
return new JButton( (String)value );
 }
 
}

 
package blog.swing;
import java.util.*;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;

class JListDemo 
{
 JFrame mainFrame;
 JList simpleList;
 JButton changeSelections;
 
public JListDemo() {
  mainFrame 
= new JFrame ( "JListDemo" );
/*  Vector<String> listData = new Vector<String>();
  listData.add("data1");
  listData.add("data2");
  listData.add("data3");
  listData.add("data4");
  simpleList  = new JList( listData );
*/
  DefaultListModel dlm 
= new DefaultListModel();
  dlm.addElement(
"data1");
  dlm.addElement(
"data2");
  dlm.addElement(
"data3");
  dlm.addElement(
"data4");
  simpleList 
= new JList( dlm );
  changeSelections 
= new JButton ("changeSelections");
  changeSelections.addActionListener( 
new ActionListener(){
   
public void actionPerformed( ActionEvent e){
    DefaultListSelectionModel dlsm 
= (DefaultListSelectionModel)simpleList.getSelectionModel();
    
//dlsm.addSelectionInterval(0,1);
    
//dlsm.removeSelectionInterval(0,1);
    dlsm.setSelectionInterval(0,1);
/*    DefaultListModel dlm = (DefaultListModel)simpleList.getModel();    
    dlm.remove(0);
    dlm.remove(1);
*/
   }
  });
  mainFrame.getContentPane().add( simpleList,BorderLayout.PAGE_START );
  mainFrame.getContentPane().add( changeSelections,BorderLayout.PAGE_END );
  mainFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
  mainFrame.pack();
  mainFrame.setLocationRelativeTo(
null);
  mainFrame.setVisible( 
true );
 }
 
public static void main(String[] args) 
 {
  
new JListDemo();
 }
}

 12.JComboBox
組合框和JList很相似,它們都是用ListModel來保存數據.默認它是使用實現了ListModel的子接口ComboBoxModel的DefaultComboBoxModel來保存數據的.與JList的情況不同,一個JComboBox總是有一個Model來保存數據的,而JList則不然.
DefaultComboBoxModel的方法:
添加元素:addElement(Object object),insertElementAt(Object object,int index)
刪除元素:removeElement(Object object),removeElementAt(int index),removeAllElements()
獲取元素:getElementAt(int index)
和選擇有關的:getSelectedItem(),setSelectedItem(Object object)
此外還有getSize(),getIndexOf(Object object)
JComboBox自身也有一些處理項的方法:
void addItem(Object anObject),void insertItemAt(Object anObject, int index)
void removeItem(Object anObject),void removeItemAt(int anIndex),void removeAllItems() 
Object getItemAt(int index)
int getItemCount()
以上基本上是把DefaultComboBoxModel裏的方法的Element改爲Item
int getSelectedIndex(),Object getSelectedItem(),Object[] getSelectedObjects()
通過在JComboBox上添加ActionListener,可以在選擇改變了的時候作出響應.
最後是自定義組合框.通過調用和JList一樣的void setRenderer(ListCellRenderer aRenderer) 就可以自定義組合框.
下面的例子示範了在JComboBox裏顯示圖片
package blog.swing;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.util.*;

class JComboBoxDemo 
{
    JFrame mainFrame;
    JComboBox simpleComboBox;
    
public JComboBoxDemo() {
        mainFrame 
= new JFrame ( "JComboBoxDemo" );
        Vector
<String> cbData = new Vector<String>();
        cbData.add(
"images/Pig.gif");
        cbData.add(
"images/Bird.gif");
        cbData.add(
"images/Dog.gif");
        cbData.add(
"images/Cat.gif");
        simpleComboBox 
= new JComboBox( cbData);
        simpleComboBox.setPreferredSize( 
new Dimension(200,130) );
        simpleComboBox.setMaximumRowCount(
2);
        simpleComboBox.setRenderer( 
new CustomComboBoxRenderer() );
        mainFrame.getContentPane().add( simpleComboBox );
        simpleComboBox.addActionListener( 
new ActionListener(){
            
public void actionPerformed( ActionEvent e){
                System.out.println( 
"selection changed" );
                System.out.println( simpleComboBox.getSelectedItem() );
            }
        });
        simpleComboBox.setCursor( 
new Cursor(Cursor.HAND_CURSOR) );
        mainFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        mainFrame.pack();
        mainFrame.setLocationRelativeTo(
null);
        mainFrame.setVisible( 
true );
    }

    
public static void main(String[] args) 
    {
        
new JComboBoxDemo();
    }

    
class CustomComboBoxRenderer extends JLabel implements ListCellRenderer{
        CustomComboBoxRenderer(){
            setOpaque(
true);
            setHorizontalAlignment(CENTER);
            setVerticalAlignment(CENTER);

        }
        
public Component getListCellRendererComponent(
            JList list,
            Object value,
            
int index,
            
boolean isSelected,
            
boolean cellHasFocus)
        {
            
if (isSelected) {
                setBackground(list.getSelectionBackground());
                setForeground(list.getSelectionForeground());
            } 
else {
                setBackground(list.getBackground());
                setForeground(list.getForeground());
            }
            String imageFileName 
= (String)value;
            ImageIcon labelIcon 
= new ImageIcon( imageFileName );
            setText( imageFileName.substring(imageFileName.lastIndexOf(
'/')+1) );
            setIcon( labelIcon );
            
return this;
        }
    }
}
 13.JFileChooser
JFileChooser代表一個打開/保存文件對話框
三個較簡單的構造函數:
JFileChooser(),JFileChooser(File currentDirectory),JFileChooser(String currentDirectoryPath)
構造對象以後,調用int showOpenDialog(Component parent)或int showSaveDialog(Component parent)顯示打開/保存對話框
調用int showDialog(Component parent, String approveButtonText) 顯示自定義打開/保存按鈕文字的對話框
三個方法的返回值都是整型.當按下打開/保存時,返回APPROVE_OPTION,否則返回CANCEL_OPTION
可以用javax.swing.filechooser.FileFilter來過濾不需要顯示的文件.它只有兩個方法
boolean accept(File f)  通過通過判斷f的後綴來決定顯示與否.要顯示則返回true,否則返回false
String getDescription() 返回對這個過濾器的描述
和FileFilter有關的方法:
void setFileFilter(FileFilter filter),void addChoosableFileFilter(FileFilter filter)
當我們選擇的文件改變(但是未按下打開或保存按鈕)時,會有java.beans.PropertyChangeEvent產生
通過void addPropertyChangeListener(PropertyChangeListener listener)可以對此事件進行偵聽
PropertyChangeEvent的方法有:
Object getNewValue(), Object getOldValue(), String getPropertyName()
Object getPropagationId(),void setPropagationId(Object propagationId)
通過void setAccessory(JComponent c)可以向JFileChooser添加自定義的部分.
其他有用的方法:
File getSelectedFile(),File[] getSelectedFiles()
void setFileSelectionMode(int):FILES_ONLY,DIRECTORIES_ONLY,FILES_AND_DIRECTORIES
void setMultiSelectionEnabled(boolean),setAcceptAllFileFilterUsed(boolean)
void setCurrentDirectory(File) ,void setFileHidingEnabled(boolean)
 
package blog.swing;
import java.awt.*;
import javax.swing.*;
import javax.swing.filechooser.FileFilter;
import java.io.File;
import java.beans.*;

class JFileChooserDemo 
{
    JFileChooser simpleFileChooser;
    JScrollPane previewScrollPane;
    JLabel previewLabel;
    
public JFileChooserDemo() {
        simpleFileChooser 
= new JFileChooser();        
        previewLabel 
= new JLabel ();
        previewLabel.setHorizontalAlignment(SwingConstants.CENTER);
        previewScrollPane 
= new JScrollPane ( previewLabel );
        previewScrollPane.setPreferredSize(
new Dimension(100,10));
        simpleFileChooser.setAccessory( previewScrollPane );
        simpleFileChooser.addChoosableFileFilter( 
new GifFileFilter() );
        simpleFileChooser.addChoosableFileFilter( 
new PngFileFilter() );
        simpleFileChooser.addChoosableFileFilter( 
new JpgFileFilter() );
        simpleFileChooser.addPropertyChangeListener( 
new PropertyChangeListener(){
            
public void propertyChange( PropertyChangeEvent e ){
                
if ( JFileChooser.SELECTED_FILE_CHANGED_PROPERTY.equals( e.getPropertyName() ) ){
                    File newSelectedFile 
= (File)e.getNewValue();
                    
if( newSelectedFile != null){
                        ImageIcon icon 
= new ImageIcon( newSelectedFile.getPath() );
                        previewLabel.setIcon( icon );
                    }
                }
            }
        });
        simpleFileChooser.showOpenDialog(
null);
        
//simpleFileChooser.showDialog(null,"自定義按鈕文字");
    }
    
class GifFileFilter extends FileFilter{
        
public boolean accept( File f ){
            
return f.getName().endsWith(".gif");    
        }
        
public String getDescription(){
            
return "Gif files(.gif)";
        }
    }
    
class PngFileFilter extends FileFilter{
        
public boolean accept( File f ){
            
return f.getName().endsWith(".png");    
        }        
        
public String getDescription(){
            
return "Png files(.png)";
        }
    }
    
class JpgFileFilter extends FileFilter{
        
public boolean accept( File f ){
            
return f.getName().endsWith(".jpg");
        }        
        
public String getDescription(){
            
return "Jpg files(.jpg)";
        }
    }
    
public static void main(String[] args) 
    {
        
new JFileChooserDemo();
    }
}
 14.JColorChooser
一個顏色選擇器.它的構造方法有:
JColorChooser(),JColorChooser(Color initialColor),JColorChooser(ColorSelectionModel model)
創建了對象以後,可以調用將它添加到JFrame,JPanel等其他容器裏面.
也可調用它的靜態方法static Color showDialog(Component component, String title, Color initialColor )來創建一個模態的對話框.
它用ColorSelectionModel來管理選擇的顏色.通過調用它的void addChangeListener(ChangeListener listener)可以在選擇的顏色變化時作出反應.
調用void setSelectedColor(Color color)更改選擇的顏色,Color getSelectedColor()取得選擇的顏色
JColorChooser自身的獲取顏色的方法是Color getColor().
JColorChooser由顏色選擇面板和預覽面板組成,所以自定義它,就是對這兩部分作文章
void setPreviewPanel(JComponent) 可以設定預覽面板.setPreviewPanel( new JPanel() )可以移除預覽面板,將參數設爲null,即setPreviewPanel(null)可以將預覽面板設回默認.
void setChooserPanels(AbstractColorChooserPanel[])和void addChooserPanel(AbstractColorChooserPanel)分別可以設置和添加顏色選擇面板
void removeChooserPanel(AbstractColorChooserPanel)可以刪除顏色選擇面板
AbstractColorChooserPanel代表差一個顏色選擇面板,它在javax.swing.colorchooser包裏.它有五個抽象方法,而其中兩個是目前不用的.所以只需定義下面三個:
String getDisplayName()面板顯示的文本.
void buildChooser() 負責創建一個顏色選擇面板
void updateChooser() 負責更新顯示顏色面板
 
package blog.swing;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import javax.swing.event.*;

class JColorChooserDemo 
{
    JFrame mainFrame;
    JColorChooser simpleColorChooser;
    JLabel sampleLabel;
    
public JColorChooserDemo() {
        mainFrame 
= new JFrame ( "JColorChooserDemo" );
        sampleLabel 
= new JLabel ("sample");
        simpleColorChooser 
= new JColorChooser();
        simpleColorChooser.getSelectionModel().addChangeListener( 
new ChangeListener(){
            
public void stateChanged( ChangeEvent e ){
                sampleLabel.setForeground( simpleColorChooser.getColor() );
            }
        });
        mainFrame.getContentPane().add( simpleColorChooser,BorderLayout.PAGE_START );
        mainFrame.getContentPane().add( sampleLabel,BorderLayout.PAGE_END );
        mainFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        mainFrame.pack();
        mainFrame.setLocationRelativeTo(
null);
        mainFrame.setVisible( 
true );
    }
    
public static void main(String[] args) 
    {
        
new JColorChooserDemo();
    }
}
 
package blog.swing;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import javax.swing.event.*;
import javax.swing.colorchooser.*;
class JColorChooserCustomDemo 
{
    JFrame mainFrame;
    JColorChooser simpleColorChooserCustom;
    JLabel sampleLabel;
    
public JColorChooserCustomDemo() {
        mainFrame 
= new JFrame ( "JColorChooserCustomDemo" );
        sampleLabel 
= new JLabel ("sample");
        simpleColorChooserCustom 
= new JColorChooser();
        simpleColorChooserCustom.getSelectionModel().addChangeListener( 
new ChangeListener(){
            
public void stateChanged( ChangeEvent e ){
                sampleLabel.setForeground( simpleColorChooserCustom.getColor() );
            }
        });
        AbstractColorChooserPanel accps[] 
= { new CustomColorChooserPanel(), 
                
new CustomColorChooserPanel()};
        simpleColorChooserCustom.setChooserPanels(accps);
        mainFrame.getContentPane().add( simpleColorChooserCustom,BorderLayout.PAGE_START );
        mainFrame.getContentPane().add( sampleLabel,BorderLayout.PAGE_END );
        mainFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        mainFrame.pack();
        mainFrame.setLocationRelativeTo(
null);
        mainFrame.setVisible( 
true );
    }
    
class CustomColorChooserPanel extends AbstractColorChooserPanel implements ActionListener{        
        JButton redButton;
        JButton greenButton;
        JButton blueButton;        
        
public CustomColorChooserPanel(){            
            
this.redButton = new JButton("red");
            
this.greenButton = new JButton("green");
            
this.blueButton = new JButton("blue");
            redButton.addActionListener(
this);
            greenButton.addActionListener(
this);
            blueButton.addActionListener(
this);
        }
        
public void actionPerformed(ActionEvent ae) {
            
if((JButton)ae.getSource() == redButton){
                getColorSelectionModel().setSelectedColor(Color.red);
            }
else{
                
if((JButton)ae.getSource() == greenButton){
                    getColorSelectionModel().setSelectedColor(Color.green);
                }
                
else{
                    getColorSelectionModel().setSelectedColor(Color.blue);
                }
            }
        }
        
public void buildChooser(){
            add(redButton);
            add(greenButton);
            add(blueButton);
        }
        
public void updateChooser(){}
        
public String getDisplayName(){
            
return "CustomPanel";
        }
        
public Icon getSmallDisplayIcon() {
            
return null;
        }
        
public Icon getLargeDisplayIcon() {
            
return null;
        }
    }
    
public static void main(String[] args) 
    {
        
new JColorChooserCustomDemo();
    }
 15.JSlider
JSlider是一個滑動條.其實它還是比較容易使用的
構造方法比較多:
JSlider(),JSlider(int orientation),JSlider(int min, int max)
JSlider(int min, int max, int value) ,JSlider(int orientation, int min, int max, int value)
通過void addChangeListener(ChangeListener l) 可以在它的值改變時作出反應
最需要操作的是它的刻度.下面是和刻度有關的方法:
void setMajorTickSpacing(int n),void setMinorTickSpacing(int n) ,void setLabelTable(Dictionary labels)
void setPaintLabels(boolean b),void setPaintTicks(boolean b)
另外一些常用方法:
setValue(int n),void setOrientation(int orientation),void setMinimum(int minimum),void setMaximum(int maximum)
int getValue() ,int getOrientation()
 
package blog.swing;
import javax.swing.*;
import java.awt.event.*;
import javax.swing.event.*;
import java.util.*;

class JSliderDemo 
{
    JFrame mainFrame;
    JSlider simpleSlider;
    
public JSliderDemo() {
        mainFrame 
= new JFrame ( "JSliderDemo" );
        simpleSlider 
= new JSlider(SwingConstants.VERTICAL);

        Hashtable sliderLabelHashTable 
= simpleSlider.createStandardLabels(10);
        
for(int i=0; i<sliderLabelHashTable.size()*10; i+=10){
            sliderLabelHashTable.put(
new Integer(i),new JLabel("label " + i));
        }
        simpleSlider.setLabelTable(sliderLabelHashTable);
        simpleSlider.setPaintLabels(
true);
        simpleSlider.setMinorTickSpacing(
5);
        simpleSlider.setMajorTickSpacing(
10);
        simpleSlider.setPaintTicks(
true);
        
        mainFrame.getContentPane().add( simpleSlider );
        simpleSlider.addChangeListener( 
new ChangeListener(){
            
public void stateChanged( ChangeEvent e){
                System.out.println( simpleSlider.getValue() );
            }
        });
        mainFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        mainFrame.pack();
        mainFrame.setLocationRelativeTo(
null);
        mainFrame.setVisible( 
true );
    }
    
public static void main(String[] args) 
    {
        
new JSliderDemo();
    }
}
 16.JLayeredPane
JFrame,JApplet,JDialog,JInternalFrame其實是由好幾部分組成的
JFrame,JApplet,JDialog,JInternalFrame
   |__JRootPane:根層
 |__GlassPane(Component):GlassPane是用組件實現的,沒有JGlassPane
 |__JLayeredPane:分層.在這裏可以定義組件的疊放次序
  |__ContentPane:ContentPane和GlassPane一樣,只一個抽象層,沒有對應的類.在它們上面可以放組件
  |__JMenuBar
但是,我們一般不直接使用JRootPane的JLayeredPane,而是自己定義一個.
它只有一個構造方法,無參的JLayeredPane()
用Component add(Component comp, int index)將組件添加到其上並指定層級,層級大的組件顯示在小的上面.
以後可以動態改變所在層:
void moveToBack(Component c),void moveToFront(Component c):這兩個方法改變的是層內的位置,而不是層間的位置
void setPosition(Component c, int position):設置層內的位置.0表示最上面,-1表示最下面
void setLayer(Component c, int layer)
void setLayer(Component c, int layer, int position) 設置組件的層級,position是指在層內的位置.
 
package blog.swing;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import javax.swing.event.*;

class JLayeredPaneDemo 
{
    JFrame mainFrame;
    JLayeredPane layeredPane;
    JLabel blackLabel;
    JComboBox layerList;
    
public JLayeredPaneDemo() {
        mainFrame 
= new JFrame ( "JLayeredPaneDemo" );
        layeredPane 
= new JLayeredPane();
        layeredPane.setPreferredSize( 
new Dimension(200,300) );
        Color[] colors 
= { Color.red, Color.green, Color.yellow, Color.blue };
        
forint i=0; i<4; i++){
            JLabel label 
= createLabel(i,colors[i]);
            layeredPane.add( label, 
new Integer(i) );
        }
        blackLabel 
= new JLabel ( "lll" );
        blackLabel.setBounds( 
15,40,120,120);
        blackLabel.setOpaque( 
true );
        blackLabel.setBackground( Color.black );
        layeredPane.add( blackLabel, 
new Integer(1), 0 );
        layeredPane.addMouseMotionListener( 
new MouseInputAdapter(){
            
public void mouseMoved( MouseEvent e ){
                blackLabel.setBounds( e.getX(),e.getY(),
120,120 );
            }
        } );

        String layerListItem[] 
= { "PUT THE BLACK LABEL AT LAYER 0",
            
"PUT THE BLACK LABEL AT LAYER 1","PUT THE BLACK LABEL AT LAYER 2",
            
"PUT THE BLACK LABEL AT LAYER 3","PUT THE BLACK LABEL AT LAYER 4" };
        layerList 
= new JComboBox( layerListItem );
        layerList.addActionListener( 
new ActionListener(){
            
public void actionPerformed( ActionEvent e ){
                layeredPane.setLayer( blackLabel,layerList.getSelectedIndex() );
            }
        } );
        mainFrame.getContentPane().add( layerList ,BorderLayout.PAGE_END);
        mainFrame.getContentPane().add( layeredPane, BorderLayout.PAGE_START);
        mainFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        mainFrame.setSize( 
400,400 );
        mainFrame.setLocationRelativeTo(
null);
        mainFrame.setVisible( 
true );
    }
    JLabel createLabel(
int i,Color color){
        JLabel label 
= new JLabel ( ""+i);
        label.setOpaque( 
true );
        label.setBounds( i
*60,i*60,140,140 );
        label.setBackground( color );
        
return label;
    }
    
public static void main(String[] args) 
    {
        
new JLayeredPaneDemo();
    }
}
 17.JInternalFrame
JFrame不能添加JFrame到自已的內容面板.那麼,如何實現多文檔程序呢?用JInternalFrame可以實現.
一般的做法是,把JInternalFrame添加到JDesktopPane,然後把JDesktopPane作爲JFrame的內容面板(ContentPane)
JInternalFrame(String title[, boolean resizable[, boolean closable[, boolean maximizable[, boolean iconifiable]]]])
[]裏面的表示可以省略
 
package blog.swing;
import java.awt.*;
import javax.swing.*;

class  JInternalFrameDemo
{
    JFrame mainFrame;
    JDesktopPane desktop;
    
public JInternalFrameDemo(){
        mainFrame 
= new JFrame ("JInternalFrame");
        desktop 
= new JDesktopPane();

        
for(int i=0; i<4; i++){
            JInternalFrame internalFrame 
= new JInternalFrame();
            internalFrame.setVisible( 
true );
            internalFrame.setLocation(i
*40,i*40);
            internalFrame.getContentPane().add( 
new JButton ("button") );
            internalFrame.pack();
            desktop.add(internalFrame);
        }
//        desktop.setDragMode(JDesktopPane.LIVE_DRAG_MODE);
        desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
        mainFrame.setContentPane(desktop);
        mainFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        mainFrame.setSize(
400,400);
        mainFrame.setLocationRelativeTo(
null);
        mainFrame.setVisible( 
true );
    }

    
public static void main(String[] args) 
    {
        
new JInternalFrameDemo();
    }
}
 18.GlassPane
GlassPane可以用來截獲輸入事件(鍵盤和鼠標).沒有JGlassPane
可以調用JFrame的void setGlassPane(Component glassPane)來設置GlassPane
默認GlassPane是不可見的,要調用getGlassPane().setVisible(true)使其可見
 
package blog.swing;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;

class GlassPaneDemo 
{
    JFrame mainFrame;
    JPanel mainPanel;
    JButton button;
    
public GlassPaneDemo() {
        mainFrame 
= new JFrame (  );    
        mainPanel 
= new JPanel ();
        button 
= new JButton ("button");
        
//mainFrame.setGlassPane( mainPanel );
        mainPanel.add( button );
        mainFrame.getContentPane().add( mainPanel );
        mainFrame.setGlassPane( 
new MyGlassPane() );
        mainFrame.getGlassPane().setVisible( 
true );
        mainFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        mainFrame.setSize( 
300,400 );
        mainFrame.setLocationRelativeTo(
null);
        mainFrame.setVisible( 
true );
    }
    
private class MyGlassPane extends JComponent {
        Point point 
= new Point(10,10);
        
public MyGlassPane(){
            addMouseListener( 
new MouseAdapter(){
                
public void mouseClicked( MouseEvent e ){
                    point 
= new Point( e.getX(),e.getY() );
                    repaint();
                }
            } );
        }
        
public void paintComponent( Graphics g ){
            g.setColor( Color.red );
            g.fillOval( point.x,point.y,
20,20 );
        }
    }
    
public static void main(String[] args) 
    {
        SwingUtilities.invokeLater( 
new Runnable(){
            
public void run(){
                
new GlassPaneDemo();
            }
        });
    }
}
 19.JProgressBar
進度條.當一個任務要較長時間來完成時,我們可以用一個進度條來表示任務的完成進度.
在講進度條的用法之前,我們先來看javax.swing.SwingWorker類的用法.我們將用這個類來模擬我們的"較長的任務".
在java中,組件是在一個用戶界面線各里繪製的.如果我們把一個用時較長的任務放到這個線程來實現(例如我們把一個用時較長的任務放到一個按鈕的
actionPerformed(...)),那麼用戶界面將會僵死(例如包含那個按鈕的窗口的菜單將暫不可用,而要等actionPerform完成返回後纔可用).
通過SwingWorker,我們可以把這個較長的任務放到另外一個線程來實現,這樣用戶界面就不會僵死了.
這個SwingWorker是jdk1.6才引進的,.之前也有一個SwingWorker.但是它們有所不同:舊的SwingWorker是可重用的,而新的不能;另外它們的方法的名字也不一樣.
SwingWorker主要有六個方法doInBackground,get,done,publish,process,execute
SwingWorker是一個泛型類,有兩個類型參數.第一類型參數就是doInBackground和get的返回值的類型,而第二個類型參數是publish的形參類型.......
我們的較長任務是在doInBackground裏完成的,doInBackground的返回值可以用get取得.get有無參和無參兩個版本,參數代表的是等待doInBackground完成的時間,無參表示直到doInBackground完成,get才返回.
get要等到doInBackground完成才知道任務完成情況.怎麼了解任務的執行過程呢?publish可以做到這一點.publish的參數個數是任意的,但是,每一個參數的類型都必須是SwingWorker的第二個類型參數指定的類型.
我們用publish向外界發佈任務執行的情況,而用process來收集這些情況.process是在事件分發線程中執行的.在它被執行之前,SwingWorker的publish可能已經執行多次,所以process的參數是一個List,這樣就可以包含所有publish了的情況.
done是在doInBackground執行完成之後執行的.
execute是使doInBackground開始執行.
以上的方法只有doInBackground是必須自己實現的,其他都是可選的.
下面是一個例子.在這個例子中有兩個按鈕.第一個按鈕使SwingWorker開始工作,第二個按鈕調用get方法取得doInBackground的返回值.
在SwingWorker開始工作以後但是還沒有結束前按下第二個按鈕,可以看到界面僵死了,這是因爲我們在按鈕的actionPerformed(在事件分發線程裏調用)裏調用了get,而無參的get在doInBackground返回前是不會返回的.
在doInBackground完成之後,我們再按下第一個按鈕,程序並沒有變化.這是因爲SwingWorker是不可重用的.所以我們用匿名內部類來實現我們的SwingWorker.
在程序中我們還用到了publish和process.在process中,我們輸出publish的結果.按下第二個按鈕之前,process每次只輸出一個值,而在doInBackground返回之前按下第二個按鈕,因爲process是在事件分發線程裏執行的,而get阻塞了事件分發線程,所以process不再輸出了,而是等到最後連續輸出數個值.
 
import javax.swing.*;
import java.awt.event.*;
import java.util.*;
import java.util.concurrent.ExecutionException;

class SwingWorkerTest 
{
    JFrame mainFrame;
    JPanel mainPanel;
    JButton button;
    JButton getButton;
    
public SwingWorkerTest() {
        mainFrame 
= new JFrame (  );
        mainPanel 
= new JPanel ();
        
final javax.swing.SwingWorker<Integer,Integer> worker = 
                        
new javax.swing.SwingWorker<Integer,Integer>(){
            
public Integer doInBackground(){
                
int coutn = 0;
                
while( (coutn++)<10 ){
                    
try{
                        System.out.println( 
"doInBackground() is doing a long job" );
                        Thread.sleep(
1000);
                        publish( 
new Integer( (int)(Math.random()*1000) ) );
                    }
catch( InterruptedException e ){
                        e.printStackTrace();
                    }
                }
                
return new Integer(3);
            }
            @Override
            
public void process(List<Integer> integers){
                
int i = 0;
                Iterator iterator 
= integers.iterator();
                
while( iterator.hasNext() ){
                    i
++;
                    Integer integer 
= (Integer)iterator.next();
                    System.out.println( 
"在process輸出publish的值"+i+"   "+integer );
                }
            }
        };
        button 
= new JButton ("start");
        button.addActionListener( 
new ActionListener(){
            
public void actionPerformed( ActionEvent e){
                worker.execute();
            }
        });
        getButton 
= new JButton ("Get");
        getButton.addActionListener( 
new ActionListener(){
            
public void actionPerformed( ActionEvent e){
                
try{
                    System.out.println( 
"doInBackground的返回值: "+worker.get() );
                }
catch( InterruptedException ie ){
                    ie.printStackTrace();
                }
catch( ExecutionException ee ){
                    ee.printStackTrace();
                }
            }
        });
        mainPanel.add(button);
        mainPanel.add(getButton);
        mainFrame.getContentPane().add( mainPanel );
        mainFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        mainFrame.pack();
        mainFrame.setLocationRelativeTo(
null);
        mainFrame.setVisible( 
true );
    }
    
public static void main(String[] args) 
    {
        
new SwingWorkerTest();
    }
}
 除了用JProgressBar來顯示進度,我們還可以用ProgressMonitor來實現.
ProgressMonitor提供了創建進度條的簡便方法,它顯示的進度條出現在一個對話框裏
它只有一個構造方法:ProgressMonitor(Component parentComponent, Object message, String note, int min, int max)
message和note參數都是和進度條一起顯示在對話框裏,不同的是,note是可變的,而message不可以.min和max是進度的最小值和最大值
這個對話框並不是在任務一開始就顯示出來的,而是等500個百萬分之一秒再出來,這個"500"可以用void setMillisToPopup(int millisToPopup)來設定,參數的單位是百萬分之一秒;而且,如果它計算得知這個任務用時不超過2000個百萬分之一秒,那麼這個對話框就永遠不會出來.這個"2000",可以用void setMillisToDecideToPopup(int)來設定,參數的單位也是百萬分之一秒
它其他的方法有:
int getMillisToPopup()
void setMinimum(int m),void setMaximum(int m),void setNote(String note),void setProgress(int nv)
int getMinimum() , int getMaximum() ,String getNote() ,沒有int getProgress()
boolean isCanceled()
這裏再介紹SwingWorker的幾個方法:
setProgress:設置任務的進度
getProgress:得到任務的進度
可以用addPropertyChangeListener(PropertyChangeLitener)對上面兩個方法的調用作出響應
cancel(boolean):取消任務
isCancelled():判斷任務是否已被取消
下面是一個例子.
 
package blog.swing;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.util.Random;
import java.beans.*;

class ProgressMonitorDemo
{
    JFrame mainFrame;
    ProgressMonitor simpleProgressMonitor;
    JButton startButton;
    Worker worker;
    
public ProgressMonitorDemo() {
        mainFrame 
= new JFrame ( "ProgressMonitorDemo" );
        startButton 
= new JButton ("Start");
        startButton.addActionListener( 
new ActionListener(){
            
public void actionPerformed( ActionEvent e){
                simpleProgressMonitor 
= new ProgressMonitor(mainFrame,"正在執行任務","",0,100);
                simpleProgressMonitor.setMillisToDecideToPopup(
0);
                simpleProgressMonitor.setMillisToPopup(
0);
                worker 
= new Worker();
                worker.addPropertyChangeListener( 
new PropertyChangeListener(){
                    
public void propertyChange( PropertyChangeEvent e ){
                        
if"progress".equals( e.getPropertyName() )){
                            
int progress = (Integer)e.getNewValue();
                            simpleProgressMonitor.setProgress( progress );
                            String message 
= String.format("%d%% completed",progress);
                            simpleProgressMonitor.setNote(message);
                        }
                    }
                });
                worker.execute();
                startButton.setEnabled(
false);
            }
        } );
        mainFrame.getContentPane().add( startButton,BorderLayout.LINE_START );
        mainFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        mainFrame.pack();
        mainFrame.setLocationRelativeTo(
null);
        mainFrame.setVisible( 
true );
    }

    
public static void main(String[] args) 
    {
        
new ProgressMonitorDemo();
    }
    
class Worker extends javax.swing.SwingWorker<Void,Void>{
        
public Void doInBackground(){
            
int progress = 0;
            Random r 
= new Random();
            
while( progress<=100 && !isCancelled() ){
                progress 
+= r.nextInt(10);
                setProgress( Math.min(progress,
100) );
                
try{
                    Thread.sleep( r.nextInt(
1000) );
                }
catch( InterruptedException e ){
                    e.printStackTrace();
                }
            }
            
return null;
        }
        
public void done(){
            startButton.setEnabled(
true);
        }
    }
}
 20.JTabbedPane
選項卡.
構造方法:JTabbedPane() ,JTabbedPane(int tabPlacement) ,JTabbedPane(int tabPlacement, int tabLayoutPolicy)
添加選項卡:
void addTab(String title, Component component)
void addTab(String title, Icon icon, Component component)
void addTab(String title, Icon icon, Component component, String tip)
void insertTab(String title, Icon icon, Component component, String tip, int index)
刪除選項卡:
void remove(int index)
void removeAll()
void removeTabAt(int index)
修改選項卡上顯示的組件:
void setComponentAt(int index, Component component)
Component getComponentAt(int index)
設置外觀:
void setTabPlacement(int tabPlacement):JTabbedPane.TOP, JTabbedPane.BOTTOM ,JTabbedPane.LEFT,JTabbedPane.RIGHT
void setTabLayoutPolicy(int tabLayoutPolicy) :JTabbedPane.WRAP_TAB_LAYOUT ,JTabbedPane.SCROLL_TAB_LAYOUT
void setTitleAt(int index, String title)
void setToolTipTextAt(int index, String toolTipText)
void setIconAt(int index, Icon icon)
void setBackgroundAt(int index, Color background) 
void setForegroundAt(int index, Color foreground) 
查找選項卡:
int indexAtLocation(int x, int y)
int indexOfComponent(Component component)
int indexOfTab(Icon icon)
int indexOfTab(String title)
和選擇有關的:
int getSelectedIndex()
void setSelectedIndex(int index)
Component getSelectedComponent()
void setSelectedComponent(Component c)
自定義標籤上的組件:
void setTabComponentAt(int index, Component c);
Component getTabComponentAt(int index);
 
package blog.swing;
import javax.swing.*;
import java.awt.Color;
class JTabbedPaneDemo 
{
    JFrame mainFrame;
    JTabbedPane simpleTabbedPane;
    
public JTabbedPaneDemo() {
        mainFrame 
= new JFrame ( "JTabbedPaneDemo" );
        simpleTabbedPane 
= new JTabbedPane();
        simpleTabbedPane.setTabLayoutPolicy( JTabbedPane.SCROLL_TAB_LAYOUT );
        simpleTabbedPane.addTab(
"Tab1",new JLabel ("Component1"));
        simpleTabbedPane.addTab(
"Tab2",new JLabel ("Component2"));
        simpleTabbedPane.addTab(
"Tab3",new JLabel ("Component3"));
        simpleTabbedPane.addTab(
"Tab4",new JLabel ("Component4"));
        
for(int i=0; i<4; i++){
            simpleTabbedPane.setTabComponentAt( i,
new JButton (""+i));
            simpleTabbedPane.setBackgroundAt(i,Color.white);
        }
        mainFrame.getContentPane().add( simpleTabbedPane );
        mainFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        mainFrame.pack();
        mainFrame.setLocationRelativeTo(
null);
        mainFrame.setVisible( 
true );
    }
    
public static void main(String[] args) 
    {
        
new JTabbedPaneDemo();
    }
}
 21.JFormattedTextField
在講JFormattedTextField之前,先講用於指定格式的類:
Locale,NumberFormat,DecimalFormat,DecimalFormatSymbols,DateFormat,SimpleDateFormat,DateFormatSymbols
根據地區/語言(Locale)的不同,各種數字,日期的格式會有所不同.例如902333這個數字在德國會寫作902.333,而在美國寫作902,333
創建Locale可以使用它的構造方法,也可以使用它的一些常量.例如下面兩個語句是等價的:
Locale locale1 = new Locale("zh","CN");
Locale locale2 = Locale.CHINA;
上面用到的"zh"(小寫)和"CN"(大寫)分別遵循着一定的規定,在下面的鏈接可以找到這些搭配:
[url]http://www.ics.uci.edu/pub/ietf/http/related/iso639.txt[/url]
[url]http://www.chemie.fu-berlin.de/diverse/doc/ISO_3166.html[/url]
你可以用任意的"xx"和"XX"搭配來創建Locale,但是,並不是所有都是有意義的,即Locale不一定可被上面的XXXFormat使用.
使用下面的程序可以得到DateFormat可以使用的組合:
 
package blog.swing;
import java.util.Locale;
import java.text.DateFormat;
class AvailableLocale 
{
    
public static void main(String[] args) 
    {
        Locale[] locales 
= DateFormat.getAvailableLocales();
        
for( Locale locale : locales ){
            System.out.println( locale.toString());
            
//System.out.println( locale.getDisplayName() );
        }
    }
}
如果你不設定Locale,XXXFormat將使用默認的Locale.這個默認的Locale是和你所用的系統有關的
用Locale.getDefault()可以得到默認的Locale
NumberFormat可以用於數字,貨幣和百分數的格式化(根據不同的Locale).對於數字,貨幣和百分數,分別調用靜態方法getNumberInstanc(Locale),
getCurrencyInstance(Locale),getPercentInstance(Locale)來取得實例,再用String format(double)來返回格式化後的字符串.
DecimalFormat是NumberFormat的子類,它對格式提供了更多的控制.在構造它的時候可以指定數字顯示格式.它不可以直接指定Locale.要指定Locale的時候,可以把一個NumberFormat強制轉換爲DecimalFormat,再調用applyPattern(String pattern)來指定數字格式.
同樣它用String format(double)來返回格式化後的字符串.
可以用DecimalFormatSymbols來指定數字裏面的各個符號,例如小數點.在DecimalFormat的構造方法裏傳入DecimalFormatSymbols就可以了.DecimalFormatSymbols還可以指定Locale,所以用了DecimalFormatSymbols就不用將一個NumberFormat轉換爲Decimalformat以指定Locale了
 
package blog.swing;
import java.util.Locale;
import java.text.NumberFormat;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
class NumberFormatDemo{
    
public static void main( String[] args ){
        
double number = 96356.127;
        
double currency = 56832.523;
        
double percent = 0.72;
        String out;
        Locale locales[] 
= { Locale.CHINA,Locale.US,Locale.GERMANY };
        NumberFormat formats 
= NumberFormat.getNumberInstance();
        
forint i=0; i<3; i++){
            formats 
= NumberFormat.getNumberInstance( locales[i] );
            out 
= formats.format( number );
            System.out.println( out
+"  "+locales[i].getDisplayName() );
            formats 
= NumberFormat.getCurrencyInstance( locales[i] );
            out 
= formats.format( currency );
            System.out.println( out
+"  "+locales[i].getDisplayName() );
            formats 
= NumberFormat.getPercentInstance( locales[i] );
            out 
= formats.format( percent );
            System.out.println( out
+"  "+locales[i].getDisplayName() );
        }
        DecimalFormat df 
= new DecimalFormat();
        String pattern 
= "@#,###.##";
        df.applyPattern( pattern );
        out 
= df.format(number);
        System.out.println( out );
        pattern 
= "#@###.####";
        df.applyPattern( pattern );
        out 
= df.format(number);
        System.out.println( out );
        df 
= (DecimalFormat)formats;
        df.applyPattern(
"#,###.##");
        out 
= df.format(number);
        System.out.println( out );
        DecimalFormatSymbols dfss 
= new DecimalFormatSymbols(Locale.GERMANY);
        dfss.setDecimalSeparator(
'^');
        df.setDecimalFormatSymbols( dfss );
        df.applyPattern(
"00,000.000");
        out 
= df.format(number);
        System.out.println( out );
    }
}
pattern的格式應滿足:
pattern    := subpattern{;subpattern}
subpattern := {prefix}integer{.fraction}{suffix}
prefix     := '\\u0000'..'\\uFFFD' - specialCharacters
suffix     := '\\u0000'..'\\uFFFD' - specialCharacters
integer    := '#'* '0'* '0'
fraction   := '0'* '#'*
上面講的都是和數字有關的,下面講的是和日期和時間有關的
和日期,時間有關的格式用DateFormat,它的用法和NumberFormat差不多,也是調用靜態方法來取得實例,再調用String format(Date)來返回格式化後的字符串
這些靜態方法有:
DateFormat getDateInstance(),DateFormat getDateInstance(int style),DateFormat getDateInstance(int style, Locale aLocale)
DateFormat getTimeInstance(),DateFormat getTimeInstance(int style),DateFormat getTimeInstance(int style, Locale aLocale)
DateFormat getDateTimeInstance(),DateFormat getDateTimeInstance(int style),DateFormat getDateTimeInstance(int style, Locale aLocale)
第一個參數指定顯示的風格,根據第二個參數的不同,這些風格也有所不同.可以取的值有:
DEFAULT,LONG,MEDIUM,SHORT,FULL,它們都是DateFormat的靜態常量.
對應於數字的DecimalFormat,在日期時間方面,有一個SimpleDateFormat
與DecimalFormat不同的是,SimpleDateFormat在構造的時個就可指定Locale了.
與數字的DeciamlFormatSymblos對應,在日期時間方面,有一個DateFormatSymbols.
 
package blog.swing;
import java.util.Locale;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Date;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.text.DateFormatSymbols;
class  DateTimeFormatDemo
{
    
public static void main(String[] args) 
    {
        Calendar calendar 
= new GregorianCalendar();
        Date date 
= calendar.getTime();
        Locale[]  locales 
= { Locale.CHINA,Locale.US,Locale.GERMANY };
        String[] patterns 
= { "yy-MM-dd","E yyyy/MM/dd","yy.MM.dd.hh.mm.ss" };
        DateFormat formats;
        SimpleDateFormat sdf;
        String out;
        
forint i=0; i<3; i++ ){
            formats 
= DateFormat.getDateInstance(DateFormat.DEFAULT,locales[i]);
            out 
= formats.format( date );
            System.out.println( out );
            formats 
= DateFormat.getTimeInstance(DateFormat.LONG,locales[i]);
            out 
= formats.format( date );
            System.out.println( out );
            formats 
= DateFormat.getDateTimeInstance(DateFormat.FULL,DateFormat.FULL,locales[i]);
            out 
= formats.format( date );
            System.out.println( out );
            sdf 
= new SimpleDateFormat(patterns[i],locales[i]);
            out 
= sdf.format( date );
            System.out.println( out
+"  "+patterns[i] );
            System.out.println( 
"=====================" );
        }
        DateFormatSymbols dfss 
= new DateFormatSymbols(Locale.CHINA);
        sdf 
= new SimpleDateFormat();
        String[] capitalDays 
= {
                    
"","SUN-星期日""MON-星期一""TUE-星期二""WED-星期三",
                        
"THU-星期四""FRI-星期五""SAT-星期六"};
        dfss.setShortWeekdays(capitalDays);
        sdf.applyPattern(
"E");
        sdf.setDateFormatSymbols( dfss );
        out 
= sdf.format(date);
        System.out.println( out );
    }
}
 下面是一個JFormattedTextField的例子.在這個例子中的三個JFormattedTextField分別使用了NumberFormat的三個靜態方法取得的NumberFormat,當它們失去焦點時,它們顯示的文本就會被格式化再重新顯示,如果輸入的是無效的文本,則文本被重設爲上次的有效文本.
 
package blog.swing;
import java.awt.GridLayout;
import java.awt.Container;
import javax.swing.BorderFactory;
import javax.swing.*;
import java.text.NumberFormat;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeEvent;

class JFormattedTextFieldDemo implements PropertyChangeListener
{
    JFrame mainFrame;
    JPanel mainPanel;
    JFormattedTextField priceFormattedTextField;
    JFormattedTextField discountFormattedTextField;
    JFormattedTextField paymentFormattedTextField;
    JLabel priceLabel;
    JLabel discountLabel;
    JLabel paymentLabel;
    NumberFormat priceFormat;
    NumberFormat discountFormat;
    NumberFormat paymentFormat;
    
public JFormattedTextFieldDemo() {
        mainFrame 
= new JFrame ( "JFormattedTextFieldDemo" );
        mainPanel 
= new JPanel ( new GridLayout(3,2) );
        mainPanel.setBorder(BorderFactory.createEmptyBorder(
10101010));

        priceFormat 
= NumberFormat.getNumberInstance();
        priceFormattedTextField 
= new JFormattedTextField(priceFormat);
        priceFormattedTextField.setValue(
72.023);
        priceFormattedTextField.addPropertyChangeListener(
"value",this);
        priceLabel 
= new JLabel ("Price");
        priceLabel.setLabelFor( priceFormattedTextField );
        mainPanel.add( priceLabel );
        mainPanel.add( priceFormattedTextField );

        discountFormat 
= NumberFormat.getPercentInstance();
        discountFormattedTextField 
= new JFormattedTextField(discountFormat);
        discountFormattedTextField.setValue(
0.75);
        discountFormattedTextField.addPropertyChangeListener(
"value",this);
        discountLabel 
= new JLabel ("Discount");
        discountLabel.setLabelFor( discountFormattedTextField );
        mainPanel.add( discountLabel );
        mainPanel.add( discountFormattedTextField );

        paymentFormat 
= NumberFormat.getCurrencyInstance();
        paymentFormattedTextField 
= new JFormattedTextField(paymentFormat);
        paymentFormattedTextField.setEditable( 
false );
        paymentFormattedTextField.addPropertyChangeListener(
"value",this);
        paymentLabel 
= new JLabel ("Payment");
        paymentLabel.setLabelFor( paymentFormattedTextField );
        mainPanel.add( paymentLabel );
        mainPanel.add( paymentFormattedTextField );

        mainFrame.getContentPane().add( mainPanel );
        mainFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        mainFrame.pack();
        mainFrame.setLocationRelativeTo(
null);
        mainFrame.setVisible( 
true );
    }
    
public void propertyChange( PropertyChangeEvent e ){
        
double price = ((Number)priceFormattedTextField.getValue()).doubleValue();
        
double discount = ((Number)discountFormattedTextField.getValue()).doubleValue();
        paymentFormattedTextField.setValue( price
*discount );
    }
    
public static void main(String[] args) 
    {
        
new JFormattedTextFieldDemo();
    }
}
除了使用上面講到的java.text包中的各種formatter,還可以使用javax.swing.text.MaskFormatter來限制用戶可以輸入的字符
 
package blog.swing;
import javax.swing.*;
import javax.swing.text.MaskFormatter;
import java.text.ParseException;
class  MaskFormatterDemo
{
    JFrame mainFrame;
    JFormattedTextField simpleFormattedTextField;
    MaskFormatter mask;
    
public MaskFormatterDemo(){
        mainFrame 
= new JFrame ( "MaskFormatterDemo" );
        
try{
            mask 
= new MaskFormatter("####");
            simpleFormattedTextField 
= new JFormattedTextField( mask );        
            mainFrame.getContentPane().add( simpleFormattedTextField );
        }
catch( ParseException e ){
            e.printStackTrace();
        }
        mainFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        mainFrame.pack();
        mainFrame.setLocationRelativeTo(
null);
        mainFrame.setVisible( 
true );
    }
    
public static void main(String[] args) 
    {
        
new MaskFormatterDemo();
    }
}
22.JSpinner
微調組件.
微調組件由Editor,微調按鈕,和它的Model組成.
在構造JSpinner時,可以指定它的Model.
swing提供了三個Model:
SpinnerListModel,SpinnerNumberModel,SpinnerDateModel
結構是SpinnerModel
  |_AbstractSpinnerModel
   |_SpinnerListModel,SpinnerNumberModel,SpinnerDateModel
對應有三個Editor:
JSpinner.ListEditor,JSpinner.NumberEditor,JSpinner.DateEditor,三個都是JSpinner.DefaultEditor的子類
JSpinner.DefaultEditor
 |_JSpinner.ListEditor,JSpinner.NumberEditor,JSpinner.DateEditor
可以看到,有很大的空間可以自定義一個JSpinner
當你需要自定義它的Editor時,你可以用void setEditor(JComponent editor),也可以用JSpinner.DefaultEditor.getTextField()來取得JFormattedTextField,然後對這個JFormattedTextField來調用方法.
可以通過addChangeListener對JSpinner值的改變作出反應.
 
package blog.swing;
import java.awt.GridLayout;
import java.awt.Color;
import javax.swing.*;
import javax.swing.event.ChangeListener;
import javax.swing.event.ChangeEvent;
import java.util.Date;
import java.util.Calendar;
import java.text.DecimalFormat;

class JSpinnerDemo implements ChangeListener
{
    JFrame mainFrame;
    JPanel mainPanel;
    JSpinner listSpinner;
    JSpinner numberSpinner;
    JSpinner dateSpinner;
    
public JSpinnerDemo() {
        mainFrame 
= new JFrame ( "JSpinnerDemo" );
        mainPanel 
= new JPanel ( new GridLayout(3,1) );
        
        String[] listData 
= { "SpinnerListModel","SpinnerNumberModel","SpinnerDateModel" };
        
//使用自定義的Model來實現cycle
        SpinnerModel listModel = new CustomSpinnerListModel(listData);
        listSpinner 
= new JSpinner( listModel );
        listSpinner.addChangeListener( 
this );
        mainPanel.add(listSpinner);

        SpinnerModel numberModel 
= new SpinnerNumberModel(1.0,0.0,2.0,0.1);
        numberSpinner 
= new JSpinner( numberModel );
        numberSpinner.addChangeListener( 
this );
        
//通過取得JFormattedTextField來自定義Editor
        JSpinner.DefaultEditor editor = (JSpinner.DefaultEditor)numberSpinner.getEditor();
        JFormattedTextField ftf 
= editor.getTextField();
        ftf.setForeground( Color.red );
        mainPanel.add( numberSpinner );

        Calendar calendar 
= Calendar.getInstance();
        Date initDate 
= calendar.getTime();
        calendar.add(Calendar.YEAR,
-100);
        Date earliestDate 
= calendar.getTime();
        calendar.add(Calendar.YEAR,
200);
        Date latestDate 
= calendar.getTime();
        SpinnerModel dateModel 
= new SpinnerDateModel(initDate,earliestDate,latestDate,Calendar.YEAR);
        dateSpinner 
= new JSpinner( dateModel );
        dateSpinner.addChangeListener( 
this );
        
//通過setEditor來自定義Editor
        dateSpinner.setEditor( new JSpinner.DateEditor(dateSpinner,"yyyy-MM-dd") );
        mainPanel.add(dateSpinner);

        mainPanel.setBorder( BorderFactory.createEmptyBorder(
10,10,10,10) );
        mainFrame.getContentPane().add( mainPanel );
        mainFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        mainFrame.pack();
        mainFrame.setLocationRelativeTo(
null);
        mainFrame.setVisible( 
true );
    }
    
public void stateChanged( ChangeEvent e ){
        JSpinner spinner 
= (JSpinner)e.getSource();
        System.out.println( spinner.getValue() );
    }
    
//自定義的Model,實現Cycle.
    class CustomSpinnerListModel extends SpinnerListModel{
        Object[] values;
        CustomSpinnerListModel( Object[] values ){
            
super(values);
            
this.values = values;
        }
        
public Object getPreviousValue(){
            Object value 
= super.getPreviousValue();
            
return value != null ? value : values[values.length-1];
        }
        
public Object getNextValue(){
            Object value 
= super.getNextValue();
            
return value != null ? value : values[0];
        }
    }
    
public static void main(String[] args) 
    {
        
new JSpinnerDemo();
    }
}
 23.JTree
這個組件太複雜了,只能很簡單很簡單地介紹一下.
一樹由根節點和子節點組成.它們都是由DefaultMutableTreeNode表示
根節點是必須的,子節點可有可無.
傳給DefaultMutableTreeNode的構造方法的是一個Object.在構造JTree的時候,會調用這個Object的toString()取得顯示在JTree上的節點的文本.
調用void add(MutableTreeNode newChild)來增加一個子節點.
在構造JTree時,將用DefaultMutableTreeNode表示的根傳入構造方法JTree(TreeNode root) ,這樣就可以構造一棵樹.
JTree用TreeSelectionModel來維護它的選擇項,默認使用的是它的實現類DefaultTreeSelectionModel.
通過它的void addTreeSelectionListener(TreeSelectionListener x)可以對選擇作出反應.
自定義JTree的外觀
void setRootVisible(boolean rootVisible) 可以設置是否隱藏根節點
void setShowsRootHandles(boolean newValue) 設置是否顯示節點前面的加號
void putClientProperty(Object key,Object value)設置節點之間的連線的樣式
要自定義節點的圖標,可以使用DefaultTreeCellRenderer,它是JLabel的一個字類
void setClosedIcon(Icon icon)設置非展開時的圖標
void setOpenIcon(Icon newIcon) 設置節點展開時的圖標
void setLeafIcon(Icon newIcon) 設置葉節點的圖標
它也有一般Renderer類有的方法:
Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus)
通過這個方法可以定義每個節點所顯示的組件
JTree的void setEditable(boolean)可以設置是否可以就地編輯該樹
通過JTree的數據Model TreeModel的void addTreeModelListener(TreeModelListener l),我們可以在發生編輯的時候作出反應.
簡單的應用可以使用TreeModel的實現類DefaultTreeModel.它的構造和JTree一樣,也是把樹的根節點傳進去,DefaultTreeModel(TreeNode root).
創建了它以後,就可以用JTree的另外一個構造方法JTree(TreeModel newModel)來構造一棵樹.
 
package blog.swing;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import javax.swing.event.*;
import javax.swing.tree.*;
class JTreeDemo 
{
    JFrame mainFrame;
    JScrollPane scrollPane;
    JTree simpleTree;
    JButton addButton;
    JButton removeButton;
    JTextField insertField;
    JPanel panel;
    
public JTreeDemo() {
        mainFrame 
= new JFrame ( "JTreeDemo" );
        DefaultMutableTreeNode swing 
= new DefaultMutableTreeNode("Swing");
        buildTree(swing);
        simpleTree 
= new JTree(swing);
        simpleTree.getSelectionModel().addTreeSelectionListener(
new TreeSelectionListener(){
            
public void valueChanged( TreeSelectionEvent e ){
                System.out.println( 
"selection changed" );
            }
        });
        simpleTree.setRootVisible(
false);
        simpleTree.setShowsRootHandles(
false);
        simpleTree.putClientProperty(
"JTree.lineStyle","Horizontal");
        simpleTree.putClientProperty(
"JTree.lineStyle","None");
        simpleTree.setCellRenderer( 
new CustomTreeCellRenderer() );
        simpleTree.setEditable( 
true );
        simpleTree.getModel().addTreeModelListener(
new TreeModelListener(){
                
public void treeNodesChanged(TreeModelEvent e) {
                    System.out.println(
"node changed");
                }
                
public void treeNodesInserted(TreeModelEvent e) {
                    System.out.println( 
"node inserted" );
                }
                
public void treeNodesRemoved(TreeModelEvent e) {
                    System.out.println(
"node removed");
                }
                
public void treeStructureChanged(TreeModelEvent e) {
                    System.out.println( 
"strutrued changed" );
                }
            });
        scrollPane 
= new JScrollPane( simpleTree );

        addButton 
= new JButton ("add");
        addButton.addActionListener( 
new ActionListener(){
            
public void actionPerformed( ActionEvent e){
                TreePath parentPath 
= simpleTree.getSelectionPath();
                
if( parentPath != null ){
                    DefaultMutableTreeNode parentNode 
= (DefaultMutableTreeNode)parentPath.getLastPathComponent();
                    DefaultTreeModel model 
= (DefaultTreeModel)simpleTree.getModel();
                    DefaultMutableTreeNode child 
= new DefaultMutableTreeNode( insertField.getText() );
                    model.insertNodeInto( child , parentNode,
0 );
                    simpleTree.scrollPathToVisible( 
new TreePath( child.getPath() ) ); 
                }
            }
        });
        removeButton 
= new JButton ("remove");
        removeButton.addActionListener( 
new ActionListener(){
            
public void actionPerformed( ActionEvent e){
                TreePath path 
= simpleTree.getSelectionPath();
                
if( path != null ){
                    DefaultMutableTreeNode removeNode 
= (DefaultMutableTreeNode)path.getLastPathComponent();
                    DefaultTreeModel model 
= (DefaultTreeModel)simpleTree.getModel();
                    model.removeNodeFromParent( removeNode );
                }
            }
        });
        insertField 
= new JTextField(20);
        panel 
= new JPanel ( new GridLayout(15,1) );
        panel.add(insertField);
        panel.add( addButton );
        panel.add( removeButton );

        mainFrame.getContentPane().add( panel,BorderLayout.LINE_START );
        mainFrame.getContentPane().add( scrollPane,BorderLayout.LINE_END  );
        mainFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        mainFrame.pack();
        mainFrame.setLocationRelativeTo(
null);
        mainFrame.setVisible( 
true );
    }
    
private void buildTree(DefaultMutableTreeNode root){
        DefaultMutableTreeNode parent;
        DefaultMutableTreeNode child;
        parent 
= new DefaultMutableTreeNode("Containers");
        child 
= new DefaultMutableTreeNode("JFrame");
        parent.add(child);
        child 
= new DefaultMutableTreeNode("JPanel");
        parent.add(child);
        child 
= new DefaultMutableTreeNode("JDialog");
        parent.add(child);
        root.add(parent);
        parent 
= new DefaultMutableTreeNode("Components");
        child 
= new DefaultMutableTreeNode("JButton");
        parent.add(child);
        child 
= new DefaultMutableTreeNode("JLabel");
        parent.add(child);
        child 
= new DefaultMutableTreeNode("JList");
        parent.add(child);
        root.add(parent);
    }
    
private class CustomTreeCellRenderer extends DefaultTreeCellRenderer{
        
public CustomTreeCellRenderer(){
            
/*setLeafIcon( new ImageIcon("images/leaf.gif") );
            setOpenIcon( new ImageIcon("images/expand.gif") );
            setClosedIcon( new ImageIcon("images/unexpand.gif") );
*/
        }
        
public Component getTreeCellRendererComponent(
            JTree tree, Object value,
            
boolean sel, boolean expanded,
            
boolean leaf, int row, boolean hasFocus){
            JButton button 
= new JButton ( value.toString() );
            
if( leaf )
                button.setIcon(
new ImageIcon("images/leaf.gif") );
            
else{
                
if( expanded )
                    button.setIcon(
new ImageIcon("images/expand.gif") );
                
else
                    button.setIcon(
new ImageIcon("images/unexpand.gif") );
            }
            
return button;
        }
    }
    
public static void main(String[] args) 
    {
        
new JTreeDemo();
    }
}
 
未完,待續......
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章