Thứ Hai, 25 tháng 3, 2013

Tạo nhiều cửa sổ con trong một ứng dụng với JDesktopPane containertrong java

Bạn đã từng sử dụng các trình soạn thảo vd Word, Excel. Bạn thấy các chương trình này đều cho phép chúng ta mở nhiều tài liệu cùng một lúc và cho phép chúng ta chuyển đổi qua lại một cách dễ dàng. Với bài viết này tôi sẽ trình bày một cách đơn giản nhất để tạo ra một ứng dụng có thể có nhiều các cửa sổ con. Cụ thể ở đây tôi dùng JInternalFrame.



Mã đơn giản
Ứng dụng của chúng ta sẽ gồm 2 lớp : InternalFrameDemo.java và MyInternalFrame.


  • InternalFrameDemo sẽ là khung Frame bên ngoài bao trùm ứng dụng: bao gồm menu và khung vẽ.

  • MyInternalFrame là nội dung bên trong của các cửa sổ nhỏ của chúng ta.

Lớp InternalFrameDemo 

PHP Code:

import javax.swing.JInternalFrame;
import javax.swing.JDesktopPane;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JMenuBar;
import javax.swing.JFrame;
import javax.swing.KeyStroke;
import java.awt.event.*;
import java.awt.*;/*
* InternalFrameDemo.java is a 1.4 application that requires:
* MyInternalFrame.java
*/
public class InternalFrameDemo extends JFrame
implements ActionListener {
JDesktopPane desktop;public InternalFrameDemo() {
super(”InternalFrameDemo”);

//Make the big window be indented 50 pixels from each edge
//of the screen.
int inset = 50;
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
setBounds(inset, inset,
screenSize.width – inset*2,
screenSize.height – inset*2);

//Set up the GUI.
desktop = new JDesktopPane(); //a specialized layered pane
createFrame(); //create first “window”
setContentPane(desktop);
setJMenuBar(createMenuBar());

//Make dragging a little faster but perhaps uglier.
desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
}

protected JMenuBar createMenuBar() {
JMenuBar menuBar = new JMenuBar();

//Set up the lone menu.
JMenu menu = new JMenu(”Document”);
menu.setMnemonic(KeyEvent.VK_D);
menuBar.add(menu);

//Set up the first menu item.
JMenuItem menuItem = new JMenuItem(”New”);
menuItem.setMnemonic(KeyEvent.VK_N);
menuItem.setAccelerator(KeyStroke.getKeyStroke(
KeyEvent.VK_N, ActionEvent.ALT_MASK));
menuItem.setActionCommand(”new”);
menuItem.addActionListener(this);
menu.add(menuItem);

//Set up the second menu item.
menuItem = new JMenuItem(”Quit”);
menuItem.setMnemonic(KeyEvent.VK_Q);
menuItem.setAccelerator(KeyStroke.getKeyStroke(
KeyEvent.VK_Q, ActionEvent.ALT_MASK));
menuItem.setActionCommand(”quit”);
menuItem.addActionListener(this);
menu.add(menuItem);

return menuBar;
}

//React to menu selections.
public void actionPerformed(ActionEvent e) {
if (”new”.equals(e.getActionCommand())) { //new
createFrame();
} else { //quit
quit();
}
}

//Create a new internal frame.
protected void createFrame() {
MyInternalFrame frame = new MyInternalFrame();
frame.setVisible(true); //necessary as of 1.3
desktop.add(frame);
try {
frame.setSelected(true);
} catch (java.beans.PropertyVetoException e) {}
}

//Quit the application.
protected void quit() {
System.exit(0);
}

/**
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event-dispatching thread.
*/
private static void createAndShowGUI() {
//Make sure we have nice window decorations.
JFrame.setDefaultLookAndFeelDecorated(true);

//Create and set up the window.
InternalFrameDemo frame = new InternalFrameDemo();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

//Display the window.
frame.setVisible(true);
}

public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application’s GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}



- Như bạn thấy đoạn mã trên không có gì đặc biệt . Chỉ có một số điểm bạn cần lưu ý :
+ JDesktopPane sẽ là bộ chứa để chúng ta chứa các của sổ con. JDesktopPane là một lớp mở rộng của lớp JLayeredPane để cung cấp thêm khả năng quản lý các đối tượng lớp JInternalFrame.
+ Tookit là một lớp abstract, có chức năng ràng buộc các components với các phần hiện thực native của chúng (lớp này được xem là sự hiện thực ở mức thấp nhất của AWT).
+ Bản thân lớp InternalFrameDemo cũng hiện thực interface ActionListener . Và vì thế khi có một sự kiện trên menu (người dùng lựa chọn) thì phương thức actionPerformed sẽ được gọi và thông qua các chuỗi được gán cho các menu (bởi phương thức setActionCommand) mà ứng dụng sẽ có hành động phù hợp.
+ Phương thức createFrame sẽ tạo ra một của sổ con cho ứng dụng. Lưu ý là đối tượng của JInternalFrame cần được setVisible(true) trước khi đưa vào JDestopPane để đảm bảo sẽ hiển thị đúng (sun khuyến cáo) .
+ Phương thức setSelected(true) của JInternalFrame sẽ có tác dụng của sổ con được tạo ra sẽ được Active và đương nhiên các cửa sổ khác sẽ là Deactive.
+ Cuối cùng, phương thức main sẽ không trực tiếp vẽ khung Frame mà sẽ dùng SwingUtilities.invokeLater để giao việc này cho Event-dispatching Thread. Bạn có thể tham khảo luồng Thread vs Swing để hiểu rõ hơn.

Lớp MyInternalFrame

PHP Code:



import javax.swing.JInternalFrame;

import java.awt.event.*;
import java.awt.*;

/* Used by InternalFrameDemo.java. */
public class MyInternalFrame extends JInternalFrame {
static int openFrameCount = 0;
static final int xOffset = 30, yOffset = 30;

public MyInternalFrame() {
super(”Document #” + (++openFrameCount),
true, //resizable
true, //closable
true, //maximizable
true);//iconifiable

//…Create the GUI and put it in the window…

//…Then set the window size or call pack…
setSize(300,300);

//Set the window’s location.
setLocation(xOffset*openFrameCount, yOffset*openFrameCount);
}
}



- Lớp này sẽ chịu trách nhiệm quản lý những gì bên trong các cửa sổ con của chúng ta.
- Về mặt giao diện thì thực sự JInternalFrame không khác Jframe mấy. Bạn có thể thao tác trên JInternalFrame như trên Jframe khi cần thêm các components.
Nâng cao hơn
- Bạn đã tạo được một ứng dụng gồm nhiều cửa sổ con. Nhưng bạn vẫn chưa quản lý được các cửa sổ con.
- Tôi sẽ trình bày một ví dụ nhỏ : thêm một menu Window cho phép bạn lựa chọn của sổ con nào sẽ được active và ngoài ra giới hạn số của sổ con tối đa là 4.
- Phần các biến toàn cục bạn thêm vào các khai báo sau :

PHP Code:

JMenu win_menu;
ButtonGroup group;
JRadioButtonMenuItem [] item;
MyInternalFrame [] child_win_list;
int curr;


+ win_menu là menu Window của chúng ta.
+ item là danh sách lưu các MenuItem là các mục chọn của sổ active. Tôi dùng RadioButton bởi vì bạn chỉ có thể chọn 1 cửa sổ con active tại một thời điểm.
+ child_win_list là danh sách các của sổ con được tạo ra .
+ curr lưu giữ index của của sổ được active.
- Phần Constructor bạn thêm vào các đoạn mã sau :

PHP Code:

group = new ButtonGroup();
child_win_list = new MyInternalFrame [4];
item = new JRadioButtonMenuItem [4];


+ Số 4 là số cửa sổ con tối đa. Bạn có thể thay đổi.
- Phần createMenuBar() bạn thêm vào :

PHP Code:

win_menu = new JMenu(”Window”);
win_menu.setMnemonic(KeyEvent.VK_W);
menuBar.add(win_menu);


- Phương thức createFrame() được sửa lại :

PHP Code:

protected void createFrame() {
curr = getFreeList();
if (curr >= 0) {
child_win_list[curr] = new MyInternalFrame(”Child Window ” +
String.valueOf(curr+1));
child_win_list[curr].addInternalFrameListener(new InternalFrameAdapter() {
public void internalFrameClosed(InternalFrameEvent arg0) {
// TODO Auto-generated method stub
String tmp = arg0.getInternalFrame().getTitle();
curr = Integer.parseInt(tmp.substring(tmp.length()-1)) – 1;
removeWindowMenu();
}
public void internalFrameActivated(InternalFrameEvent e) {
String tmp = e.getInternalFrame().getTitle();
curr = Integer.parseInt(tmp.substring(tmp.length()-1)) – 1;
item[curr].setSelected(true);
}
});
addWindowMenu();
child_win_list[curr].setVisible(true); //necessary as of 1.3
desktop.add(child_win_list[curr]);
try {
child_win_list[curr].setSelected(true);
} catch (java.beans.PropertyVetoException e) {}
} else {
JOptionPane.showMessageDialog(null, “You only can open 4 windows simultaneously”,
“Alert”, JOptionPane.ERROR_MESSAGE);
}
}


- Mỗi một lần người dùng new một child window chúng ta cần phải lấy về giá trị còn trống trong danh sách các cửa sổ. Hàm getFreeList() sẽ trả về phần tử đầu tiên của child_win_list chưa được khởi tạo.
- Child window được tạo ra sẽ được cài một bộ lắng nghe. Nếu người dùng đóng một của sổ thì sẽ loại tên của nó ra khỏi menu Window. Nếu người dùng active một cửa sổ thì MenuItem tương ứng của nó phải được chọn.
- Ở đây curr được thay đổi cho phù hợp khi người dùng active một của sổ. Curr được parse từ tên của child window. Cách này không phù hợp nếu tên của child window sẽ không lưu chứa số index của nó. Bạn có thể thêm hai phương thức setIndex() và getIndex() trong MyInternalFrame để có thể quản lý index một cách dễ dàng.
- Các phương thức thêm vào :

PHP Code:

protected void removeWindowMenu() {
JMenuItem tmp = null;
for (int i = 0; i < win_menu.getItemCount(); i++) {
tmp = win_menu.getItem(i);
if (tmp.getActionCommand().equals(String.valueOf(curr))) {
group.remove(tmp);
win_menu.remove(tmp);
}
}
child_win_list[curr] = null;
}
protected void addWindowMenu() {
item[curr] = new JRadioButtonMenuItem(”Child Window ” + String.valueOf(curr+1));
item[curr].setActionCommand(String.valueOf(curr));
item[curr].setAccelerator(KeyStroke.getKeyStroke(
49 + curr, ActionEvent.ALT_MASK));
item[curr].addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
curr = Integer.parseInt(e.getActionCommand());
child_win_list[curr].setSelected(true);
} catch (Exception ex) {}
}
});
group.add(item[curr]);
win_menu.add(item[curr], curr);
}
protected int getFreeList() {
for (int j = 0; j < 4; j++)
if (child_win_list[j] == null) return j;
return -1;
}


- Phương thức addWindowMenu() sẽ thêm vào một MenuItem khi có một cửa sổ được tạo ra.
- Phương thức removeWindowMenu() sẽ loại ra MenuItem tương ứng khi có một của sổ con bị đóng.

0 nhận xét:

Đăng nhận xét