Swing還提供我們許多特殊容器方便我們編程,JSPlitPane(分割面板),JTabbedPane(多選項卡),JLayeredPane(層容器,允許組件互相重疊),最後講兩個復雜的容器JDesktopPane和JInternalFrame這些多是為了實現MDI(多文檔界面),這些容器不是三言兩語能說清楚的,所以我將以舉例的方式(其中多是書中的例子,舉的都不錯,自己一個一個寫可吃不消),如還有不懂的,請多查閱API文檔。
eg:JSPlitPane(分割面板)
- public class TestSplitPane
- {
- Book[] books = new Book[]{
- new Book("Struts2權威指南" , new ImageIcon("ico/struts2.jpg") ,
- "全面介紹Struts2的各方/n面知識"),
- new Book("輕量級J2EE企業應用實戰" , new ImageIcon("ico/J2EE.jpg") ,
- "介紹Struts、Spring和/nHibernate整合開發的知識"),
- new Book("基於J2EE的AJax寶典" , new ImageIcon("ico/AJax.jpg") ,
- "全面介紹J2EE平台上AJax/n開發的各方面知識")
- };
- JFrame jf = new JFrame("測試JSPlitPane");
- JList bookList = new JList(books);
- JLabel bookCover = new JLabel();
- JTextArea bookDesc = new JTextArea();
- public void init()
- {
- //為三個組件設置最佳大小
- bookList.setPreferredSize(new Dimension(150, 300));
- bookCover.setPreferredSize(new Dimension(300, 150));
- bookDesc.setPreferredSize(new Dimension(300, 150));
- //為下拉列表添加事件監聽器
- bookList.addListSelectionListener(new ListSelectionListener()
- {
- public void valueChanged(ListSelectionEvent event)
- {
- Book book = (Book)bookList.getSelectedValue();
- bookCover.setIcon(book.getIco());
- bookDesc.setText(book.getDesc());
- }
- });
- //創建一個垂直的分割面板,
- //將bookCover放在上面,將bookDesc放在下面 , 支持連續布局
- JSPlitPane left = new JSplitPane(JSPlitPane.VERTICAL_SPLIT, true ,
- bookCover, new JScrollPane(bookDesc));
- //打開“一觸即展”的特性
- left.setOneTouchExpandable(true);
- //下面代碼設置分割條的大小。
- //left.setDividerSize(50);
- //設置該分割面板根據所包含組件的最佳大小來調整布局
- left.resetToPreferredSizes();
- //創建一個水平的分割面板
- //將left組件放在左邊,將bookList組件放在右邊
- JSPlitPane content = new JSplitPane(JSPlitPane.HORIZONTAL_SPLIT,
- left, bookList);
- jf.add(content);
- jf.setDefaultCloSEOperation(JFrame.EXIT_ON_CLOSE);
- jf.pack();
- jf.setVisible(true);
- }
- public static void main(String[] args)
- {
- new TestSplitPane().init();
- }
- }
- class Book
- {
- private String name;
- private Icon ico;
- private String desc;
- public Book(){}
- public Book(String name , Icon ico , String desc)
- {
- this.name = name;
- this.ico = ico;
- this.desc = desc;
- }
- public void setName(String name)
- {
- this.name = name;
- }
- public String getName()
- {
- return this.name;
- }
- public void setIco(Icon ico)
- {
- this.ico = ico;
- }
- public Icon getIco()
- {
- return this.ico;
- }
- public void setDesc(String desc)
- {
- this.desc = desc;
- }
- public String getDesc()
- {
- return this.desc;
- }
- public String toString()
- {
- return name;
- }
- }
eg:JTabbedPane(多選項卡)
- public class TestJTabbedPane
- {
- JFrame jf = new JFrame("測試Tab頁面");
- //創建一個Tab頁面的標簽放在左邊,采用換行布局策略的JTabbedPane
- JTabbedPane tabbedPane = new JTabbedPane(JTabbedPane.LEFT , JTabbedPane.WRAP_TAB_LAYOUT);
- ImageIcon icon = new ImageIcon("ico/close.gif");
- String[] layouts = {"換行布局" , "滾動條布局"};
- String[] positions = {"左邊" , "頂部" , "右邊" , "底部"};
- Map<String , String> books = new LinkedHashMap<String , String>();
- public void init()
- {
- books.put("ROR敏捷開發最佳實踐" , "ror.jpg");
- books.put("Struts2權威指南" , "struts2.jpg");
- books.put("基於J2EE的AJax寶典" , "AJax.jpg");
- books.put("輕量級J2EE企業應用實戰" , "J2EE.jpg");
- books.put("Spring2.0寶典" , "spring.jpg");
- String tip = "可看到本書的封面照片";
- //向JTabbedPane中添加5個Tab頁面,指定了標題、圖標和提示,但該Tab頁面的組件為null
- for (String bookName : books.keySet())
- {
- tabbedPane.addTab(bookName, icon, null , tip);
- }
- jf.add(tabbedPane, BorderLayout.CENTER);
- //為JTabbedPane添加事件監聽器
- tabbedPane.addChangeListener(new ChangeListener()
- {
- public void stateChanged(ChangeEvent event)
- {
- //如果被選擇的組件依然是空
- if (tabbedPane.getSelectedComponent() == null)
- {
- //獲取所選Tab頁
- int n = tabbedPane.getSelectedIndex();
- //為指定標前頁加載內容
- loadTab(n);
- }
- }
- });
- //系統默認選擇第一頁,加載第一頁內容
- loadTab(0);
- tabbedPane.setPreferredSize(new Dimension(500 , 300));
- //增加控制標簽布局、標簽位置的單選按鈕
- JPanel buttonPanel = new JPanel();
- ChangeAction action = new ChangeAction();
- buttonPanel.add(new ButtonPanel(action , "選擇標簽布局策略" ,layouts));
- buttonPanel.add (new ButtonPanel(action , "選擇標簽位置" ,positions));
- jf.add(buttonPanel, BorderLayout.SOUTH);
- jf.setDefaultCloSEOperation(JFrame.EXIT_ON_CLOSE);
- jf.pack();
- jf.setVisible(true);
- }
- //為指定標簽頁加載內容
- private void loadTab(int n)
- {
- String title = tabbedPane.getTitleAt(n);
- //根據標簽頁的標題獲取對應圖書封面
- ImageIcon bookImage = new ImageIcon("ico/" + books.get(title));
- tabbedPane.setComponentAt(n, new JLabel(bookImage));
- //改變標簽頁的圖標
- tabbedPane.setIconAt(n, new ImageIcon("ico/open.gif"));
- }
- //定義改變標簽頁的布局策略,放置位置的監聽器
- class ChangeAction implements ActionListener
- {
- public void actionPerformed(ActionEvent event)
- {
- JRadioButton source = (JRadioButton)event.getSource();
- String selection = source.getActionCommand();
- if (selection.equals(layouts[0]))
- {
- tabbedPane.setTabLayoutPolicy(JTabbedPane.WRAP_TAB_LAYOUT);
- }
- else if (selection.equals(layouts[1]))
- {
- tabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
- }
- else if (selection.equals(positions[0]))
- {
- tabbedPane.setTabPlacement(JTabbedPane.LEFT);
- }
- else if (selection.equals(positions[1]))
- {
- tabbedPane.setTabPlacement(JTabbedPane.TOP);
- }
- else if (selection.equals(positions[2]))
- {
- tabbedPane.setTabPlacement(JTabbedPane.RIGHT);
- }
- else if (selection.equals(positions[3]))
- {
- tabbedPane.setTabPlacement(JTabbedPane.BOTTOM);
- }
- }
- }
- public static void main(String[] args)
- {
- new TestJTabbedPane().init();
- }
- }
- //定義一個JPanel類擴展類,該類的對象包含多個縱向排列的JRadioButton控件
- //且Panel擴展類可以指定一個字符串作為TitledBorder
- class ButtonPanel extends JPanel
- {
- private ButtonGroup group;
- public ButtonPanel(TestJTabbedPane.ChangeAction action , String title, String[] labels)
- {
- setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), title));
- setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
- group = new ButtonGroup();
- for (int i = 0; labels!= null && i < labels.length; i++)
- {
- JRadioButton b = new JRadioButton(labels[i]);
- b.setActionCommand(labels[i]);
- add(b);
- //添加事件監聽器
- b.addActionListener(action);
- group.add(b);
- b.setSelected(i == 0);
- }
- }
- }
eg:JLayeredPane(層容器,允許組件互相重疊)
- public class TestJLayeredPane
- {
- JFrame jf = new JFrame("測試JLayeredPane");
- JLayeredPane layeredPane = new JLayeredPane();
- public void init()
- {
- //向layeredPane中添加3個組件
- layeredPane.add(new ContentPanel(10 , 20 , "Struts2權威指南" ,
- "ico/struts2.jpg"), JLayeredPane.MODAL_LAYER);
- layeredPane.add(new ContentPanel(100 , 60 , "RoR敏捷開發最佳實踐",
- "ico/ror.jpg"), JLayeredPane.DEFAULT_LAYER);
- layeredPane.add(new ContentPanel(190 , 100 , "輕量級J2EE企業應用實戰",
- "ico/J2EE.jpg"), 4);
- layeredPane.setPreferredSize(new Dimension(400, 300));
- layeredPane.setVisible(true);
- jf.add(layeredPane);
- jf.pack();
- jf.setDefaultCloSEOperation(JFrame.EXIT_ON_CLOSE);
- jf.setVisible(true);
- }
- public static void main(String[] args)
- {
- new TestJLayeredPane().init();
- }
- }
- //擴展了JPanel類,可以直接創建一個放在指定位置,
- //且有指定標題、放置指定圖標的JPanel對象
- class ContentPanel extends JPanel
- {
- public ContentPanel(int xPos , int yPos , String title , String ico)
- {
- setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), title));
- JLabel label = new JLabel(new ImageIcon(ico));
- add(label);
- setBounds(xPos , yPos , 160, 200);
- }
- }
以上3例子由於都是廣告,我就不貼給大家了,沒圖片不影響程序的效果。
最後是JDesktopPane和JInternalFrame來實現MDI
- public class TestInternalFrame
- {
- final int DESKTOP_WIDTH = 480;
- final int DESKTOP_HEIGHT = 360;
- final int FRAME_DISTANCE = 30;
- JFrame jf = new JFrame("MDI界面");
- //定義一個虛擬桌面
- private MyJDesktopPane desktop = new MyJDesktopPane();
- //保存下一個內部窗口的座標點
- private int nextFrameX;
- private int nextFrameY;
- //定義內部窗口為虛擬桌面的1/2大小
- private int width = DESKTOP_WIDTH / 2;
- private int height = DESKTOP_HEIGHT / 2;
- //為主窗口定義2個菜單
- JMenu fileMenu = new JMenu("文件");
- JMenu windowMenu = new JMenu("窗口");
- //定義newAction用於創建菜單和工具按鈕
- Action newAction = new AbstractAction("新建", new ImageIcon("ico/new.png"))
- {
- public void actionPerformed(ActionEvent event)
- {
- //創建內部窗口
- final JInternalFrame iframe = new JInternalFrame("新文檔",
- true, // 可改變大小
- true, // 可關閉
- true, // 可最大化
- true); // 可最小化
- iframe.add(new JScrollPane(new JTextArea(8, 40)));
- //將內部窗口添加到虛擬桌面中
- desktop.add(iframe);
- //設置內部窗口的原始位置(內部窗口默認大小是0X0,放在0,0位置)
- iframe.reshape(nextFrameX, nextFrameY, width, height);
- //使該窗口可見,並嘗試選中它
- iframe.show();
- //計算下一個內部窗口的位置
- nextFrameX += FRAME_DISTANCE;
- nextFrameY += FRAME_DISTANCE;
- if (nextFrameX + width > desktop.getWidth()) nextFrameX = 0;
- if (nextFrameY + height > desktop.getHeight()) nextFrameY = 0;
- }
- };
- //定義exitAction用於創建菜單和工具按鈕
- Action exitAction = new AbstractAction("退出", new ImageIcon("ico/exit.png"))
- {
- public void actionPerformed(ActionEvent event)
- {
- System.exit(0);
- }
- };
- public void init()
- {
- //為窗口安裝菜單條和工具條
- JMenuBar menuBar = new JMenuBar();
- JToolBar toolBar = new JToolBar();
- jf.setJMenuBar(menuBar);
- menuBar.add(fileMenu);
- fileMenu.add(newAction);
- fileMenu.add(exitAction);
- toolBar.add(newAction);
- toolBar.add(exitAction);
- menuBar.add(windowMenu);
- JMenuItem nextItem = new JMenuItem("下一個");
- nextItem.addActionListener(new ActionListener()
- {
- public void actionPerformed(ActionEvent event)
- {
- desktop.selectNextWindow();
- }
- });
- windowMenu.add(nextItem);
- JMenuItem cascadeItem = new JMenuItem("級聯");
- cascadeItem.addActionListener(new ActionListener()
- {
- public void actionPerformed(ActionEvent event)
- {
- //級聯顯示窗口,內部窗口的大小是外部窗口的0.75
- desktop.cascadeWindows(FRAME_DISTANCE , 0.75);
- }
- });
- windowMenu.add(cascadeItem);
- JMenuItem tileItem = new JMenuItem("平鋪");
- tileItem.addActionListener(new ActionListener()
- {
- public void actionPerformed(ActionEvent event)
- {
- //平鋪顯示所有內部窗口
- desktop.tileWindows();
- }
- });
- windowMenu.add(tileItem);
- final JCheckBoxMenuItem dragOutlineItem = new JCheckBoxMenuItem("僅顯示拖動窗口的輪廓");
- dragOutlineItem.addActionListener(new ActionListener()
- {
- public void actionPerformed(ActionEvent event)
- {
- //根據該菜單項是否選擇來決定采用哪種拖動模式
- desktop.setDragMode(dragOutlineItem.isSelected()
- ? JDesktopPane.OUTLINE_DRAG_MODE
- : JDesktopPane.LIVE_DRAG_MODE);
- }
- });
- windowMenu.add(dragOutlineItem);
- desktop.setPreferredSize(new Dimension(480, 360));
- //將虛擬桌面添加到頂級JFrame容器中
- jf.add(desktop);
- jf.add(toolBar , BorderLayout.NORTH);
- jf.setDefaultCloSEOperation(JFrame.EXIT_ON_CLOSE);
- jf.pack();
- jf.setVisible(true);
- }
- public static void main(String[] args)
- {
- new TestInternalFrame().init();
- }
- }
- class MyJDesktopPane extends JDesktopPane
- {
- //將所有窗口以級聯方式顯示,
- //其中offset是兩個窗口的位移距離,scale是內部窗口與JDesktopPane的大小比例
- public void cascadeWindows(int offset , double scale)
- {
- //定義級聯顯示窗口時內部窗口的大小
- int width = (int)(getWidth() * scale);
- int height = (int)(getHeight() * scale);
- //用於保存級聯窗口時每個窗口的位置
- int x = 0;
- int y = 0;
- for (JInternalFrame frame : getAllFrames())
- {
- try
- {
- //取消內部窗口的最大化,最小化
- frame.setMaximum(false);
- frame.setIcon(false);
- //把窗口重新放置在指定位置
- frame.reshape(x, y, width, height);
- x += offset;
- y += offset;
- //如果到了虛擬桌面邊界
- if (x + width > getWidth()) x = 0;
- if (y + height > getHeight()) y = 0;
- }
- catch (PropertyVetoException e)
- {}
- }
- }
- //將所有窗口以平鋪方式顯示
- public void tileWindows()
- {
- //統計所有窗口
- int frameCount = 0;
- for (JInternalFrame frame : getAllFrames())
- {
- frameCount++;
- }
- //計算需要多少行、多少列才可以平鋪所有窗口
- int rows = (int) Math.sqrt(frameCount);
- int cols = frameCount / rows;
- //需要額外增加到其他列中的窗口
- int extra = frameCount % rows;
- //計算平鋪時內部窗口的大小
- int width = getWidth() / cols;
- int height = getHeight() / rows;
- //用於保存平鋪窗口時每個窗口在橫向、縱向上的索引
- int x = 0;
- int y = 0;
- for (JInternalFrame frame : getAllFrames())
- {
- try
- {
- //取消內部窗口的最大化,最小化
- frame.setMaximum(false);
- frame.setIcon(false);
- //將窗口放在指定位置
- frame.reshape(x * width, y * height, width, height);
- y++;
- //每排完一列窗口
- if (y == rows)
- {
- //開始排放下一列窗口
- y = 0;
- x++;
- //如果額外多出的窗口與剩下的列數相等,則後面所有列都需要多排列一個窗口
- if (extra == cols - x)
- {
- rows++;
- height = getHeight() / rows;
- }
- }
- }
- catch (PropertyVetoException e)
- {}
- }
- }
- //選中下一個非圖標窗口
- public void selectNextWindow()
- {
- JInternalFrame[] frames = getAllFrames();
- for (int i = 0; i < frames.length; i++)
- {
- if (frames[i].isSelected())
- {
- // 找出下一個非最小化的窗口,嘗試選中它,
- //如果選中失敗,則繼續嘗試選中下一個窗口
- int next = (i + 1) % frames.length;
- while (next != i)
- {
- //如果該窗口不是處於最小化狀態
- if (!frames[next].isIcon())
- {
- try
- {
- frames[next].setSelected(true);
- frames[next].toFront();
- frames[i].toBack();
- return;
- }
- catch (PropertyVetoException e)
- {}
- }
- next = (next + 1) % frames.length;
- }
- }
- }
- }
- }
大家注意看繼承JDesktopPane的類MyJDesktopPane
其中的3個方法非常有用,這是對窗口控制的基本方法(級聯,平鋪,選下個窗口),大家可以保留下來,以備後用。