目前分類:JAVA Swing (32)

瀏覽方式: 標題列表 簡短摘要

最近在寫一個列表,每個按鈕都有自己的 popup menu,但是每一個 popup menu 卻都是一樣的,如果每次都做一個新的實在很浪費空間。所以就想到只做一個,再利用 JPopupMenu 來取得觸發的按鈕,來達到分辨不同元件的功能。

JMenuItem menuItem = new JMenuItem("test");
menuItem.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        System.out.println(e.getSource()); // JMenuItem
        System.out.println(((JMenuItem)e.getSource()).getParent()); // JPopupMenu
        System.out.println(((JPopupMenu)((JMenuItem)e.getSource()).getParent()).getInvoker()); // JLabel
        System.out.println(((JLabel)((JPopupMenu)((JMenuItem)e.getSource()).getParent()).getInvoker()).getText());
    }
});
JPopupMenu popupMenu;
popupMenu.add(menuItem);

台南小新 發表在 痞客邦 PIXNET 留言(0) 人氣()

JTextField 的 event 主要的有二個 CaretEvent 及 DocumentEvent。CaretEvent 用於通知感興趣的參與者事件源中的文本插入符已發生更改。DocumentEvent 用於文檔更改通知的介面。它提供一些高層級資訊,比如更改型別、在哪兒發生更改,以及更多具體的結構性更改(插入和移除哪些 Element)。

CaretListener 的 caretUpdate 主要是用於游標改變時,並不能真正得知 JTextField 內文是否有改變,所以應用上比較沒有用處。

台南小新 發表在 痞客邦 PIXNET 留言(0) 人氣()

JCalendar 是一個好用的日期選擇元件,而且還是免費的。下載網址:http://www.toedter.com/en/jcalendar/index.html

今天在寫 MVC 架構,為了把 view 的資料經由 control 傳遞到 model 裏,所以需要監聽 JCalendar 的事件。看了一下原始程式裏面的範例,發現 propertyChange 的存在,就測試了一下 PropertyChangeListener。

台南小新 發表在 痞客邦 PIXNET 留言(0) 人氣()

這幾天在寫 JAVA AP 時,發現了一個問題。就是我直接在 ContentPane 的 add 和 removeAll 來更換 JFrame 的內容。而 add 一個直接 new 的物件,造成被 removeAll 的舊物件一直沒有被釋放。查了一堆資料,只發現 JFrame/JDialog 可以利用 dispose(java.awt.Window) 來釋放所有本機螢幕資源,並將它們標記為不可顯示。所以目前不可以把 JPanel 一直重覆 new


台南小新 發表在 痞客邦 PIXNET 留言(0) 人氣()

今天在為了把 JDialog 置中在 JFrame 裏,查到可以用 setLocationRelativeTo 函數置中,但是要如何取得 JFrame 的實體呢?雖然可以在外面建立 JDialog 時,順便呼叫 setLocationRelativeTo 來置中,但是總希望在 JDialog 內自己呼叫置中即可。所以去查了一下,JDialog(Frame owner) 把 owner 設定給誰了。

public JDialog(Frame owner, String title, boolean modal) {
    super(owner == null? SwingUtilities.getSharedOwnerFrame() : owner, title, modal);
......
}
↓
public Dialog(Window owner, String title, ModalityType modalityType) {
    super(owner);
......
}
↓
public Window(Window owner) {
    this(owner == null ? (GraphicsConfiguration)null : owner.getGraphicsConfiguration());
    ownedInit(owner);
}
private void ownedInit(Window owner) {
    this.parent = owner;
    if (owner != null) {
        owner.addOwnedWindow(weakThis); 
    }
}

經由上述的 trace code 發現,owner 最後會被指定到 parent 裏,所以只要呼叫 getParent() 即可取得 owner 了。但是如果是透過 JDialog() 取得實體,則 owner 會被指定成 null。

台南小新 發表在 痞客邦 PIXNET 留言(0) 人氣()

JFrame 置中有二種方法:
  1. 方法一
    setSize(800, 600);
    setLocationRelativeTo(null);
    
  2. 方法二
    Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
    setSize(800, 600);
    setLocation((d.width-getWidth())/2, (d.height-getHeight())/2);
    
JDialog 置中有二種狀況:
  1. 狀況一:從外面設定
    JDialog dialog = new JDialog();
    dialog.setSize(300, 200);
    dialog.setLocationRelativeTo(frame);
    
  2. 方法二:從裏面設定
    new DialogTest(SwingUtilities.getWindowAncestor(c)).setVisible(true);
    public class DialogTest extends JDialog {
        public DialogTest() {
            this(null);
        }
        public DialogTest(Window owner) {
            super(owner);
            this.setSize(300, 200);
            this.setLocationRelativeTo(this.getParent());
        }
    }
    
JOptionPane 置中只有一種方法,也就是設定 parentComponent 即可,以下是 trace code 的結果。可以發現最終要呼叫 setLocationRelativeTo 函數來設定位置,所以一定要設定 parentComponent,否則永遠都是置中在營幕中間。
public static int showOptionDialog(Component parentComponent,
        Object message, String title, int optionType, int messageType,
        Icon icon, Object[] options, Object initialValue) 
        throws HeadlessException {
......
JDialog dialog = pane.createDialog(parentComponent, title, style);
......
}
↓↓↓↓↓↓
private JDialog createDialog(Component parentComponent, String title, int style) {
......
initDialog(dialog, style, parentComponent);
......
}
↓↓↓↓↓↓
private void initDialog(final JDialog dialog, int style, Component parentComponent) {
......
dialog.setLocationRelativeTo(parentComponent);
......
}

透過 jnlp 執行範例程式

台南小新 發表在 痞客邦 PIXNET 留言(0) 人氣()

每次在寫 button 的 actionPerformed 時,經常會遇到一個問題,就是為了取得 JFrame instance 而煩惱。因為 actionPerformed 通常位於 ActionListener 內,而 ActionListener 通常是一個 inner class,透過 button 的 addActionListener 加入。此時為了控制一些 JFrame 內的元件,經常需要取得 JFrame 的 instance 才可以。當然,傳遞 window event 也是一種應用。

最簡單的方式就是透過 ActionEvent.getSource() 先取得 JButton 的 instance,再利用 SwingUtilities.windowForComponent 找出 JButton 所屬的 Window instance 即可。

台南小新 發表在 痞客邦 PIXNET 留言(0) 人氣()

這次再把一個 class 又拆成三個 class,這樣應該算是重構吧,只是還沒進階到 unit test 的階段,還要加油加油。

這次主要是在加入 WindowListener 時,發現在 addWindowListener(new DualBoot()) 會造成 JApplet 產生而浪費很多資源,如果把 WindowListener 獨立成一個 class 又顯得太多餘,我又不喜歡搞一堆 inner class。最後幹脆分成三個 class,這樣也比較符合單一職責原則(Single Responsibility Principle),並且在 applet 及 frame 內加入變體的 Singleton Pattern。為什麼叫做變體呢?因為理論上 applet 及 frame 只有一個單獨存在的個體,而且 applet 的建構函數是用 browser 調用,我也沒法子阻止。所以只是單純建立 getInstance 函數而已,並沒有隱藏建構函數。另外把 applet 的建構函數拿掉,之前都忘了呼叫 super(),雖然都沒事發生,但是為了確保一些問題,還是採用標準 applet 步驟,把所有建立元件動作放到 init()。

台南小新 發表在 痞客邦 PIXNET 留言(0) 人氣()

在前一篇把雙啟動功能分割成二個 class 來做,主要是考量在 Loader.class 可以專心做一些有關 applet 及 window 的變化。其實是可以將兩者合而為一,因為二者都是以 contentPane 為主體,所以可以直接把 JApplet 的 contentPane 指定給 JFrame 即可。

透過 jnlp 執行範例程式

台南小新 發表在 痞客邦 PIXNET 留言(0) 人氣()

點我執行範例程式

一般人寫 java 程式不是 frame(application) 就是 applet,兩者總是分開撰寫。其實 JFrame 跟 JApplet 兩者是很相似的。都包含一個 JRootPane,也都是把元件加到 contentPane 裏。所以就可以利用這個特性,做一個 class Main extends JPanel,另外再做一個 class Loader extends JApplet,包含 void main(String[] args)。當透過 java.exe 執行時,就會執行 main 函數;當透過 appletviewer.exe 或 browser 執行時,就會執行 Loader 函數。這樣一來就可以做出可以同時以 applet 及 frame(application) 執行的 JAR。

台南小新 發表在 痞客邦 PIXNET 留言(0) 人氣()

JTaskPane 內由 JTaskPaneGroup 組成,而 JTaskPaneGroup 則由 AbstractAction 所組成。很不幸的是 AbstractAction 卻沒有 addMouseListener 函數,也就等於無法加入 JPopupMenu 功能。

經由查看原始碼才發現,原來 AbstractAction 都會被轉換成 JLinkButton,那何不一開始就改成 JLinkButton 呢?原來 JLinkButton 是繼承自 JButton,預設會畫一個大外框,造成二個 JLinkButton 間距太大不好看。而 L2FProd 的做法是在 BasicTaskPaneGroupUI.java 內寫一個 createAction 新增一個 JLinkButton 並覆寫 updateUI 來重設 JButton 的外觀。

台南小新 發表在 痞客邦 PIXNET 留言(0) 人氣()

點我執行範例程式

我實做的 statusbar 主要是利用 JToolBar 來做的。主要就是把 border 去掉,floatable 關閉,再畫上陰影及右下方的三角形。其中最難的就是 JToolBar 的子元件會跟上方陰影重疊,為了把它們二者分離,花了我半天時間去實驗,最後才發現只要修改 getInsets 函數就好了。

台南小新 發表在 痞客邦 PIXNET 留言(0) 人氣()

點我執行範例程式

JTaskPane 設定

  1. JTaskPane 裏面最主要的成份為 JTaskPaneGroup 及 AbstractAction。JTaskPane 包含 JTaskPaneGroup,而 JTaskPaneGroup 包含 AbstractAction。
  2. JTaskPaneGroup 雖然設計是要加入 AbstractAction 元件,但其實很多 Swing 元件都有包含一個 Action 元件,所以 Swing 元件都可以加入 JTaskPaneGroup。
  3. JTaskPane 背景顏色設定需同時設定 TaskPane.background,TaskPane.useGradient,TaskPane.backgroundGradientStart,TaskPane.backgroundGradientEnd,利用 UIManager.put() 來設定。
  4. TaskPane.background 是用來設定 JTaskPaneGroup 圓角缺空的顏色。
  5. TaskPane.useGradient 是用來設定是否有漸層。
  6. TaskPane.backgroundGradientStart 是用來設定漸層開始的顏色。
  7. TaskPane.backgroundGradientEnd 是用來設定漸層結束的顏色。
  8. 如果要使用固定顏色顯示,最好是把 TaskPane.useGradient 設定成 true,TaskPane.backgroundGradientStart 及 TaskPane.backgroundGradientEnd 設定成同顏色。
  9. 當 TaskPane.useGradient 設定成 false 時,不知為啥,修改幾次背景色後會亂掉。

JTaskPaneGroup 設定

  1. setAnimated() 展開/收起時是否有動畫,預設值為 true。
  2. setCollapsable() 是否可以收起,預設值為 true。
  3. setExpanded() 設定展開或收起,預設值為 true。
  4. setIcon() 設定圖示。
  5. setSpecial() 設定為特殊窗格,也就是畫起來跟別人不太相同,預設值為 false。
JTaskPane taskPane = new JTaskPane();

JTaskPaneGroup lookandfeel_group = new JTaskPaneGroup();
lookandfeel_group.setTitle("LookAndFell");
lookandfeel_group.setSpecial(true);
lookandfeel_group.setCollapsable(false);
lookandfeel_group.setExpanded(false);

LookAndFeelInfo lookAndFeelInfo[] = UIManager.getInstalledLookAndFeels();
for(int i=0; i

台南小新 發表在 痞客邦 PIXNET 留言(0) 人氣()

2010-12-17_154156.png

要製做 JNLP 前,必須先為 JAR 簽名,如果沒有簽名,就無法執行,如上圖所示。主要步驟為先利用 keytool 製做憑證,再利用 jarsigner 把憑證寫入 JAR 裏。二支程式都包含在 JDK 裏,JRE 只有 keytool 沒有 jarsigner,所以一定要安裝 JDK 才可以。

台南小新 發表在 痞客邦 PIXNET 留言(0) 人氣()

點我執行範例程式

製做 java look and feel 選單,首先要先取得系統支援的 look and feel 項目,可由 UIManager.getInstalledLookAndFeels() 來取得目前可用的 look and feel。接著製做 JRadioButtonMenuItem,並內嵌一個 actionPerformed 來變更外觀。變更新的 look and feel 後,一定要執行 SwingUtilities.updateComponentTreeUI 來變更當前視窗內的所有元件。

台南小新 發表在 痞客邦 PIXNET 留言(0) 人氣()

2010-12-15_132914.png

Java 的 close event 有二種,一為 windowClosed,一為 windowClosing。在一般狀況下,按下右上角的 close 按鈕只會呼叫 windowClosing。因為 sdk source 找不到 Frame.java,所以不知道按了右上角 close 按鈕會做什麼動作。但是經由 JFrame.java 中的 processWindowEvent 函數得知,收到 WindowEvent.WINDOW_CLOSING 時,會做出下列動作。

  1. HIDE_ON_CLOSE: 隱藏視窗。

台南小新 發表在 痞客邦 PIXNET 留言(0) 人氣()

在 Java 中,要取得字串在某一種字型所呈現的寬度,需透過 FontMetrics 類別來計算。FontMetrics 為 abstract class,但是我卻找不到 implements 的子類別。不過,可以透過下列方式來取得 FontMetrics 實例。

  1. Graphics.getFontMetrics() 獲取當前字體的字體規格。
  2. Graphics.getFontMetrics(Font f) 獲取指定字體的字體規格。
  3. Component.getFontMetrics(Font font) 獲取指定字體的字體規格。
  4. Component.AccessibleAWTComponent.getFontMetrics(Font f) 獲取此物件的 FontMetrics。
  5. List.AccessibleAWTList.AccessibleAWTListChild.getFontMetrics(Font f) 獲取此物件的 FontMetrics。
  6. MenuComponent.AccessibleAWTMenuComponent.getFontMetrics(Font f) 獲取此物件的 FontMetrics。

因為在取得 FontMetrics 實例時,已經指定字型的字體,所以接下來的函數,用來直接取得字串的顯示寬度。

台南小新 發表在 痞客邦 PIXNET 留言(0) 人氣()

在 trace JTableHeader source code 時,怎麼都找不到有關處理 mouse event 的地方,看了半天也是不明所以。最後在 TableHeaderUI 裏才找到相關的 code,但是在 JTableHeader 找不到載入 TableHeaderUI 的地方。還好,看到了 netbaixc 所寫的"JAVA Painting-Swing实现纪要一",才明暸如何載入 TableHeaderUI。

  1. javax\swing\table\JTableHeader.java

台南小新 發表在 痞客邦 PIXNET 留言(0) 人氣()

  1. 一開始顯示時要讓分割成為一半一半時,可以使用 setResizeWeight(0.5) 來達成,不可以使用 setDividerLocation(0.5) 根本無效。
  2. to be continue...

台南小新 發表在 痞客邦 PIXNET 留言(0) 人氣()

  1. infonode 的組成,主要是有一個 RootWindow,再加上很多的 Views,而 TabWindow, SplitWindow, FloatWindow 都是用來做為合成的視窗。
  2. RootWindow 最好藉由 DockingUtil.createRootWindow() 來取得,以確保唯一性。
  3. RootWindow 包含很多 Views,而 Views 最好放進 ViewMap 做管理。
import javax.swing.SwingUtilities;
import java.awt.BorderLayout;

import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JFrame;

import net.infonode.docking.RootWindow;
import net.infonode.docking.View;
import net.infonode.docking.util.DockingUtil;
import net.infonode.docking.util.ViewMap;

public class test2 extends JFrame {
    private static final long serialVersionUID = 1L;
    private JPanel jContentPane = null;
    private ViewMap viewMap;
    private RootWindow rootWindow;

    private RootWindow getRootWindow() {
        if (rootWindow == null) {
            View[] views = new View[5];
            viewMap = new ViewMap();
            for (int i = 0; i < views.length; i++) {
                views[i] = new View("View " + i, null, new JButton("This is view " + i + "!"));
                viewMap.addView(i, views[i]);
            }
            rootWindow = DockingUtil.createRootWindow(viewMap, true);
        }
        return rootWindow;
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                test2 thisClass = new test2();
                thisClass.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                thisClass.setVisible(true);
            }
        });
    }

    public test2() {
        super();
        initialize();
    }

    private void initialize() {
        this.setSize(300, 200);
        this.setContentPane(getJContentPane());
        this.setTitle("JFrame");
    }

    private JPanel getJContentPane() {
        if (jContentPane == null) {
            jContentPane = new JPanel();
            jContentPane.setLayout(new BorderLayout());
            jContentPane.add(getRootWindow(), BorderLayout.CENTER);
        }
        return jContentPane;
    }
}

台南小新 發表在 痞客邦 PIXNET 留言(0) 人氣()

1 2