Tải bản đầy đủ (.doc) (32 trang)

kinh nghiem java

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (827.93 KB, 32 trang )

Sử dụng JTDS thay cho jdbc
jTDS là một JDBC 3.0 driver thuần Java (type 4) mã nguồn mở dành cho Microsoft SQL Server và Sybase. Hiện nó
được xem là JDBC Driver nhanh nhất cho SQL Server và Sybase.

Như các bạn đã biết, để có thể kết nối phần mềm Java với database, chúng ta cần sử dụng những cầu nối trung gian
được biết đến như các driver. Nó cũng giống như việc bạn cần cài đặt driver để có thể giao tiếp với các thiết bị ngoại
vi vậy. Có 4 loại driver nhưng thường chúng ta sử dụng type 4 (100% pure Java). Với MS SQL, driver phổ biến là
SqlJDBC do Microsoft cung cấp. Nó cài đặt dễ dàng và sự khác biệt về tốc độ cũng không quá quan trọng.
Chúng ta chỉ thấy một thứ có vấn đề khi chính chúng ta gặp phải vấn đề với nó. Sẽ chẳng có bài viết này nếu như
mình không gặp phải một bug rất ngớ ngẩn với SqlJDBC và phải tìm đến jTDS. Số là một ngày đẹp trời mình quyết
định nâng cấp MS SQL Server lên bản mới nhất (SQL Server 2008 R2). Sau khi nâng cấp, mình chạy thử một ứng
dụng Java mình đã làm trước đó thì nó báo không thể kết nối được với database. Mình vô Netbeans run project thì
lại được. Clean rồi build đi build lại vẫn thế. Cứ chạy trong Netbeans thì ok, mà ra ngoài click file jar thì không kết
nối được. Mất rất nhiều thời gian mình mới tìm hiểu ra đó là một bug của SqlJDBC với SQL Server 2008 R2. Tất cả
được giải quyết khi mình dùng jTDS. Ngoài việc là mã nguồn mở có thể được chỉnh sửa dễ dàng và khắp phục
những lỗi phát sinh so với SqlJDBC, jTDS còn được tối ưu hóa để tăng tốc độ kết nối cũng như hiệu năng làm việc.
Trước hết, bạn tải về jTDS tại đây. Là một driver type 4, việc cài đặt jTDS rất đơn giản, chỉ cần thêm nó vào

thư viện project của bạn, sau đó viết chuỗi kết nối dựa vào tên server, cổng kết nối, tên database. Mình
demo phương thức getConnection trả về một đối tượng Connection khi kết nối thành công đến SQL
Server:

1
2
3
4
5
6
7
8
9


10
11
12
13
14
15

public static Connection getConnection(Server server) {
String url = "jdbc:jtds:sqlserver://" + server.getServerName() + ":" + server.getPort()
+ "/" + server.getDatabaseName();
try {
Class.forName("net.sourceforge.jtds.jdbc.Driver");
Connection cn = DriverManager.getConnection(url, server.getUserName(), server.getPassword());
return cn;
} catch (ClassNotFoundException cnfe) {
JOptionPane.showMessageDialog(null, "Did you forget to import the jdbc library?");
} catch (SQLException se) {
System.out.println("Connection failed!");
}
return null;
}


Cập nhật: Microsoft đã nâng cấp JDBC Driver của mình lên 4.0 để hỗ trợ thêm SQL Server 2012. Mình cũng
không rõ nó khắp phục được bug trên chưa. Bạn có thể tải về tại đây.

DELETE MỘT ROW TRONG JTABLE
Xóa là một trong những chức năng cơ bản nhất mà chúng ta thực hiện trên JTable khi
thao tác với cơ sở dữ liệu. Xóa một row trên JTable thường đi kèm với việc chúng ta xóa
một bản ghi trong database. Cách làm khá đơn giản. Tuy nhiên để thực hiện một cách

đúng đắn và hiệu quả, chúng ta cần nắm rõ về cấu trúc dữ liệu của JTable, hay nói cách
khác là về data model của JTable.
Có nhiều bạn có thể sẽ làm theo cách như sau: khi thực hiện xóa 1 row, các bạn xóa bản
ghi tương ứng trong database, sau đó setModel lại cho JTable. Cách này cũng cho ra kết
quả nhưng là một cách không thực tế và nên tránh. Trong trường hợp dữ liệu của bạn
nhiều, việc setModel sẽ tốn khá nhiều thời gian cho việc truy xuất để lấy dữ liệu ra từ
database.
Có một cách khác hay hơn nhiều. Trong data model, bạn hãy tạo ra một phương thức như sau:

1 public void removeRow(int row) {
2
data.remove(row);
3
fireTableDataChanged();
4 }
Phương thức này có nhiệm vụ remove 1 đối tượng (tương ứng với 1 row trên JTable)
trong data của model xác định bởi tham số là chỉ số hàng được chọn để xóa của bảng
(trong code ví dụ, data của mình là một ArrayList nên để xóa 1 đối tượng, mình dùng
phương thức remove của nó). Khi phương thức này được gọi, JTable sẽ tự update giao
diện của nó để phù hợp với data model.
Chúng ta tạo một JButton để thực hiện xóa row được chọn trên JTable. Code xử lý sự kiện của nút như
sau:

1
2
3
4
5
6
7

8
9
10
11

private void btnDeleteActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
if (myTable.getSelectedRowCount() > 0) {
int realIndex =
myTable.convertRowIndexToModel(myTable.getSelectedRow());
// delete record in database
//..........................
// update table UI
((CustomTableModel) myTable.getModel()).removeRow(realIndex);
myTable.repaint();
}
}

listselectionListener trong jtable
Trong JTable chúng ta thường sử dụng 2 bộ lắng nghe sự kiện chính
làListSelectionListener và TableModelListener để bắt các sự kiện tương ứng khi có sự


thay đổi về ô đang được chọn và về dữ liệu của bảng. Nói về cách sử dụng
TableModelListener, mình đã viết khá chi tiết trong bài viết Chỉnh sửa dữ liệu trực tiếp
trên JTable, các bạn có thể tham khảo. Trong bài viết này, mình sẽ đề cập đến
ListSelectionListener.
Về cơ bản, bạn sử dụng bộ lắng nghe ListSelectionListener để bắt sự kiện khi JTable có
sự thay đổi về lựa chọn trên bảng. Ví dụ như khi bạn click chuột vào một row khác row
đang được chọn thì sự kiện sẽ được phát sinh. Nhiều bạn khi mới nhập môn có thể sẽ

dùng sự kiện mouseClicked để xử lý sự lựu chọn trên JTable. Tuy nhiên, bạn nên nhớ sự
lựu chọn trên bảng không chỉ được tạo ra bởi click chuột, mà còn bởi các phím lên
xuống. Khi đó, MouseClick event không xử lý được.
JTable mặc định cho phép chúng ta có thể chọn nhiều row một lúc ở các vị trí khác nhau
(MULTIPLE_INTERVAL_SELECTION). Ngoài ra nó còn cung cấp 2 chế độ khác
làSINGLE_INTERVAL_SELECTION cho phép chọn nhiều row nhưng phải liên tiếp
nhau vàSINGLE_SELECTION chỉ cho phép chọn một row trong một thời điểm mà thôi.
Bạn có thể thay đổi giữa các chế độ này bằng phương thức setSelectionMode. Do tính
chất của bài viết, mình sử dụng chế độ chọn SINGLE_SELECTION. Mình sẽ tạo ra một
demo mà sau khi hoàn thành sẽ như sau:

Một bảng dữ liệu mà khi click vào từng row, dữ liệu của row đó sẽ được viết vào
cácJTextBox, JCheckBox phía dưới.
Như thường lệ, bạn tạo một data model kế thừa từ AbstractTableModel làm dữ liệu cho bảng
(chi tiết bạn có thể xem source code trong file attach). Code xử lý chính như sau:

1
2
3
4
5
6
7

myTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
final DateFormat df = new SimpleDateFormat("E MMM dd hh:mm:ss Z yyyy");
myTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
if(!e.getValueIsAdjusting()){



8
int selectedIndex = myTable.getSelectedRow();
9
int realIndex = myTable.convertRowIndexToModel(selectedIndex);
10
TableModel model = myTable.getModel();
11
txtFirst.setText(model.getValueAt(realIndex, 0).toString());
12
txtLast.setText(model.getValueAt(realIndex, 1).toString());
13
txtBalance.setText(model.getValueAt(realIndex, 3).toString());
14
String dateString = model.getValueAt(realIndex, 2).toString();
15
try {
16
Date birth = df.parse(dateString);
17
txtBirth.setText(DateFormat.getInstance().format(birth));
18
} catch (ParseException ex) {
19
Logger.getLogger(MyTable.class.getName()).log(Level.SEVERE, null, ex);
20
}
21
cbMale.setSelected(Boolean.parseBoolean(model.getValueAt(realIndex, 4).toString()));

22
}
23
}
24 });
Trong đoạn code trên, bạn hãy lưu ý các điểm sau:
1. Luôn luôn quy về table model
Nguyên nhân bởi vì người dùng có thể dễ dàng thay đổi vị trí của các cột bằng cách kéo
nó sang vị trí khác, hay thay đổi vị trí các hàng khi thực hiện sắp xếp. Nhưng trong model
thì vị trí luôn được giữ nguyên. Vì vậy việc quy tất cả về table model giúp chúng ta xử lý
dễ dàng hơn. JTable hỗ trợ sẵn các phương thức nhưconvertRowIndexToModel hay
ngược lại convertRowIndexToView để chuyển đổi qua lại giữa vị trí trên View và trong
Model.
2. Sử dụng phương thức getValueIsAdjusting
Bạn hãy đặt một câu lệnh System out để test trước khối lệnh if trong đoạn code trên, sau
đó chạy ứng dụng. Bạn có thể để ý thây điều này. Khi bạn click chọn một row trong bảng,
bạn sẽ thấy ở cửa sổ output, lệnh System out được chạy 2 lần. Điều đó có nghĩa là khối
code valueChanged đã được xử lý 2 lần mặc dù bạn chỉ click chọn 1 lần. Tại sao lại như
vậy? Nguyên nhân bởi vì khi bạn click chuột chọn 1 row, có 2 event đã được phát sinh
là mousePressed và mouseReleased, và cả 2 event này đều được gửi tới bộ lắng nghe sự
kiện ListSelectionListener. Vì vậy mà đoạn code đã được xử lý đến 2 lần. Điều này sẽ
không xảy ra nếu bạn chọn row trên JTable bằng các phím lên xuống. Bởi vì khi đó chỉ
có 1 event được gửi đến bộ lắng nghe sự kiện làfocusGained.
Phương thức getValueIsAdjusting trả về true nếu như sự kiện đang được tiếp nhận chỉ là
một trong một chuỗi của nhiều sự kiện mà những sự kiện tiếp sau nó có thể vẫn tạo ra
được sự thay đổi về lựa chọn. Nó chỉ trả về false nếu như sự kiện đó là cái cuối cùng
trong chuỗi sự kiện (trong trường hợp trên mouseReleased là cái cuối cùng). Thường thì


việc phải xử lý tất cả các event trong chuỗi là điều không mong muốn, nên chúng ta luôn

kiểm tra nếu getValueIsAdjusting trả về false trước khi cho phép code được thực thi tiếp.
CĂN GIỮA HEADER TRONG JTABLE
Header của JTable chính là những thanh tiêu đề trên mỗi cột của nó. Trên hầu hết
các Look and Feel, header của bảng luôn được căn lề bên trái. Điều này dẫn đến việc
những người kỹ tính (như mình chẳng hạn) sẽ thấy nó không đẹp mắt chút nào, đặc biệt
nếu một cột có độ rộng lớn. Đó là lý do tại mình muốn căn giữa header của JTable.
Cũng không có gì nhiều để chia sẻ với các bạn. Nó chỉ là một vài dòng code mà thôi,
giúp bạn tiết kiệm thời gian tìm kiếm trên Google.
Để căn giữ header của bảng, bạn có thể code như sau:

1
2
3
4
5
6
7
8
9
10
11
12
13

// center the table header
TableColumnModel columnModel = myTable.getColumnModel();
myTable.setTableHeader(new JXTableHeader(columnModel) {
@Override
public void updateUI() {
super.updateUI();

// need to do in updateUI to survive toggling of LAF
if (getDefaultRenderer() instanceof JLabel) {
((JLabel) getDefaultRenderer()).setHorizontalAlignment(JLabel.CENTER);
}
}
});

Hoặc một cách tương tự khác:

// center title column
TableCellRenderer myRenderer =
myTable.getTableHeader().getDefaultRenderer();
JLabel label = (JLabel) myRenderer;
label.setHorizontalAlignment(JLabel.CENTER);
Hai cách code trên về cơ bản là chung một nguyên tắc. Nhưng thực tế có một vài trường
hợp đặc biệt 1 trong 2 cách có thể không hiệu quả. Mình nêu cả 2 cách để thêm một
phương án dự phòng cho các bạn.
Các bạn cũng để ý rằng, không chỉ có các ô của bảng, mà cả header của nó cũng được vẽ
nên bởi các renderer. Nếu bạn sử dụng JXTable trong SwingX, cách làm hoàn toàn giống
nhau. Bạn có thể thay JTableHeader bằng JXTableHeader ở đoạn code đầu tiên trong
trường hợp này.
SỬ DỤNG JXTABLE TRONG SWING
1
2
3
4

Như các bạn đã biết, SwingX là thư viện được kế thừa từ Swing. Các component của SwingX
được kế thừa từ các component tương ứng trong Swing, mà cụ thể ở đây,JXTable được kế
thừa từ JTable. Do vậy, cách sử dụng của JXTable hoàn toàn tương tự như JTable. Cũng chính



vì lẽ đó, sau bài giới thiệu về SwingX, mình đã có một loạt bài cơ bản về cách sử dụng của
JTable để làm nền cho bài viết này.
Sẽ chẳng có gì để viết thêm nếu như JXTable không có thêm một số tính năng hay ho mà
mình sắp giới thiệu ở đây. Bạn hãy dành 2 phút xem clip này:
Rất hay phải không các bạn

Giờ chúng ta sẽ cùng tìm hiểu cụ thể những tính năng

này nhé.
JXTable hỗ trợ sẵn

sorting. Khi bạn click chuột vào header, các row của bảng sẽ được sắp

xếp theo giá trị của cột có header được click. Mỗi lần click, sự sắp xếp sẽ được tự động đảo
chiều tăng dần hay giảm dần. Bạn sẽ hỏi JXTable căn cứ vào đâu để thực hiện việc sắp xếp
các giá trị? Khác với JTable, JXTable mặc định enableautoCreateRowSorter. Do đó, nó tự
động tạo ra và sử dụng một row sorter mặc định được cung cấp bởi phương
thức createDefaultRowSorter.
Dĩ nhiên, bạn cũng có thể can thiệp vào quá trình này để tự mình chỉ ra comparatorso sánh
giá trị để sắp xếp, hay bạn có thể disable sorting trên một cột trong bảng.
Như bạn đã thấy trên clip, JXTable của mình có một nút nhỏ ở góc trên bên phải. Đó chính là
Column Control. Column Control các tác dụng giúp cho người dùng ẩn đi các cột không cần
thiết, để có tầm nhìn tốt hơn các cột muốn tập trung.
Mặc định, Column Control được ẩn đi. Bạn làm nó xuất hiện bằng
cáchsetColumnControlVisible(true).
Theo con mắt thẩm mỹ của mình,

Highlighter giúp cho giao diện của bảng đẹp hơn rất


nhiều. Có lẽ mình sẽ có một bài viết riêng nói về cách sử dụng Highlighter sau.

Trong clip demo, mình mới chỉ sử dụng một highlighter đơn giản làm background cho bảng được tạo ra
bằng cách:

1
2

Highlighter simpleStripHL =
HighlighterFactory.createSimpleStriping();
jxTable.setHighlighters(simpleStripHL);

Rollover là hiệu ứng khi mà bạn di chuột đến row nào trong bảng thì row đó đổi màu. Đây cũng là một
cách rất hay để làm đẹp giao diện. Bản chất của nó chính là một highlighter. Cách sử dụng như sau:

1
2

jxTable.addHighlighter(new
ColorHighlighter(HighlightPredicate.ROLLOVER_ROW,
null, new Color(255, 102, 0)));

Phương thức mình dùng sử dụng một constructor của lớp ColorHighlighter. Tham số đầu tiên
là kiểu rollover. Bạn có thể thay
thế ROLLOVER_ROW bằng ROLLOVER_CELL hayROLLOVER_COLUMN để có hiệu ứng rollover trên
từng ô, từng cột của bảng. Tham số thứ 2 là màu background. Mình không muốn để màu nền
nên set nó bằng null. Và tham số thứ 3 là màu text, được set mà màu da cam như bạn thấy.
Cái search popup hiện ra trên JXTable trong clip demo là hoàn toàn được support sẵn, mình
không hề tốn dòng code nào để tạo ra nó. Quá tuyệt phải không các bạn

kiếm với đầy đủ tùy chọn như case sensitive hay backward.

Một box tìm


MỘT SỐ RENDERER HAY DUNG TRONG JTABLE
Trong bài viết này, mình sẽ tổng hợp cho các bạn một vài mẫu renderer hay được dùng
trong JTable. Mặc dù nhu cầu biểu diễn dữ liệu trên JTable rất đa dạng, bài tổng hợp này cũng
không thể bao quát hết được tất cả các trường hợp, nhưng cũng đừng lo, khi các bạn làm
nhiều, các bạn sẽ nhanh chóng rút được ra quy luật của nó thôi.
Với các cách biểu diễn đơn giản như dùng checkbox cho dữ liệu kiểu boolean, căn lề phải
cho kiểu Number, biểu diễn ngày tháng, hoặc nếu bạn còn lạ lẫm với khái niệm về renderer,
bạn hãy xem lại trong bài viết giới thiệu về cell renderer trước nhé.
DefaultTableCellRenderer mặc định sử dụng JLabel để render ô của bảng. Để tạo một
renderer có tác dụng đổi màu text trong JTable rất đơn giản. Bạn chỉ cần lợi dụng phương
thức setForeground của JLabel.

1
2
3
4
5
6
7
8
9
10

public static class ColorRenderer extends DefaultTableCellRenderer {


@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean
int column) {
setForeground(new Color(153, 0, 0));
super.getTableCellRendererComponent(table, value, isSelected,
hasFocus, row, column);
return this;
}
}

Bạn có thể căn lề trái, giữa, phải cho các cột trong JTable tùy ý.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

public static class AlignRenderer extends DefaultTableCellRenderer {


@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean
int column) {
// align center
setHorizontalAlignment(SwingConstants.CENTER);
// align left
//setHorizontalAlignment(SwingConstants.LEFT);
// align right
//setHorizontalAlignment(SwingConstants.RIGHT);
super.getTableCellRendererComponent(table, value, isSelected,
hasFocus, row, column);
return this;
}
}

Lợi dụng khả năng biểu diễn hình ảnh của JLabel thông qua phương thức setIcon. Trong
demo, mình sẽ chèn 2 ảnh khác nhau vào cell của JTable dựa vào giá trị được đưa vào
là true hay false.
public static class ImageRenderer extends DefaultTableCellRenderer {
1

2
3

@Override


4
5
6

7
8
9
10
11
12
13
14
15
16
17
18
19

public Component getTableCellRendererComponent(JTable table, Object value, boolean
int column) {
boolean bool = Boolean.parseBoolean(value.toString());
Image img = null;
if(bool) {
img = getToolkit().getImage(getClass().getResource("/images/male.png"));
} else {
img = getToolkit().getImage(getClass().getResource("/images/female.png"));
}
setSize(16, 16);
setHorizontalAlignment(SwingConstants.CENTER);
setIcon(new ImageIcon(img));
super.getTableCellRendererComponent(table, "", isSelected,
hasFocus, row, column);
return this;
}

}

Tương tự từ demo này, bạn cũng có thể tạo ra renderer biểu diễn cả text và hình ảnh trên JTable.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

public static class CurrencyRenderer extends DefaultTableCellRenderer {

@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean
int column) {
if ((value != null) && (value instanceof Number)) {
Number numberValue = (Number) value;
NumberFormat formater = NumberFormat.getCurrencyInstance();
value = formater.format(numberValue.doubleValue());
}

setHorizontalAlignment(javax.

public void setValue(Object value) {
if ((value != null) && (value instanceof Number)) {
Number numberValue = (Number) value;
NumberFormat formater =
NumberFormat.getCurrencyInstance();
value = formater.format(numberValue.doubleValue());
}


14
15
16
17

super.setValue(value);
}

}

Hai cách viết này tương đương nhau. Bản chất của phương thức setValue của
DefaultTableCellRenderer là gọi đến phương thức setText được kế thừa từ JLabel.
Các bạn tránh nhầm lẫn nhé. Giá trị mà renderer vẽ ra hoàn toàn không có ảnh hưởng gì đến
giá trị đầu vào. Nó chỉ có hiệu quả thị giác mà thôi. Bạn có thể hình dung rằng renderer chỉ
vẽ ra một tấm mặt nạ để che đậy khuôn mặt bên dưới vậy. Một cell có giá trị đầu vào là
“CodeBlue” nhưng được render ra hiển thị là “Mr.CodeBlue”. Khi bạn lấy giá trị của cell này
(thông qua getValueAt của JTable chẳng hạn), thì giá trị bạn lấy được vẫn là “CodeBlue ”.

1
2
3

4
5
6
7
8
9
10

public static class OutputRenderer extends DefaultTableCellRenderer {

@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean
int column) {
value = "Mr." + value;
super.getTableCellRendererComponent(table, value, isSelected,
hasFocus, row, column);
return this;
}
}

Đến đây, bạn có thể thấy rằng tất cả các renderer mà chúng ta vừa tạo đều được kế thừa từ
DefaultTableCellRenderer. Và trên thực tế, đây cũng là cách phổ biến nhất để tạo ra renderer
cho JTable. Như các bạn biết, DefaultTableCellRenderer được kế thừa từ JLabel. Chúng ta có
thể rút ra một kinh nghiệm từ đây:
Khi bạn muốn JTable được hiển thị theo một cách nào đó, bạn hãy nghĩ xem cách đó có thể
được biểu diễn bởi JLabel hay không? Nếu có thể, bạn chỉ cần viết các phương thức tương
ứng của JLabel trong khi override lại getTableCellRendererComponent của
DefaultTableCellRenderer. Nó sẽ trả về tham chiếu đến JLabel mà bạn đã sửa đổi để đạt
được sự hiển thị như mong muốn.
Vậy trường hợp nếu bạn muốn một cột trong JTable được hiển thị theo cách mà không thể

được biểu diễn bằng JLabel thì sao? Chúng ta cũng biết JLabel có những giới hạn. Chẳng hạn
nó không thể tạo ra màu background được. Trong trường hợp này, bạn có thể trả về cho
phương thức getTableCellRendererComponent một component khác mà có khả năng biểu
diễn được yêu cầu của bạn, hơn là trả về một JLabel như bình thường.

Ví dụ mình tạo ra một renderer để render ra các ô có màu nền màu đỏ, text màu xanh, căn lề giữa:

1
2
3
4
5
6

public static class BgRenderer extends DefaultTableCellRenderer {

@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean
int column) {
JPanel pn = new JPanel(new BorderLayout());
JLabel lb = new JLabel();


7
8
9
10
11
12
13

14
15
16
17

pn.add(lb, BorderLayout.CENTER);
lb.setHorizontalAlignment(SwingConstants.CENTER);
lb.setText(value.toString());
lb.setForeground(Color.BLUE);
pn.setBackground(Color.RED);

}

super.getTableCellRendererComponent(table, value, isSelected,
hasFocus, row, column);
return pn;

}

Chúng ta đã có renderer, vậy làm thế nào để sử dụng chúng? Trong JTable, bạn có 2 cách
thường dùng sau:
Sử dụng renderer cho từng cột

Bạn xác định renderer cho từng cột bằng cách lấy về cột theo chỉ số của nó:

1
2

myTable.getColumnModel().getColumn(0).setCellRenderer(new
MyTableRenderer.OutputRenderer());

myTable.getColumnModel().getColumn(1).setCellRenderer(new
MyTableRenderer.BgRenderer());

Sử dụng renderer cho từng kiểu dữ liệu

Áp dụng renderer cho tất cả các cột có cùng kiểu dữ liệu:

1
2

myTable.setDefaultRenderer(Number.class, new
MyTableRenderer.CurrencyRenderer());
myTable.setDefaultRenderer(Boolean.class, new
MyTableRenderer.ImageRenderer());
Kết quả khi áp dụng các renderer đã được tạo:

Bài tổng hợp của mình không biết có bỏ sót renderer nào cơ bản không

Nếu có thiếu

sót, hãy để lại comment mình sẽ bổ sung nhé. Cảm ơn các bạn.

SỬ DỤNG CELL RENDERER TRING JTABLE
Khi mới làm việc với JTable, chắc sẽ có lúc bạn tự hỏi làm thế nào để hiển thị hình ảnh,
checkbox trong JTable. Bạn sẽ có câu trả lời trong bài viết này, khi chúng ta cùng nhau tìm
hiểu về cell renderer.


Trong các bài viết trước về JTable, chúng ta đã tìm hiểu về data model – thành phần đảm
nhận nhiệm vụ cung cấp dữ liệu cho JTable. Bạn đã biết cách cơ bản để hiển thị dữ liệu, biết

cách để chỉnh sửa dữ liệu trực tiếp trên bảng. Tuy nhiên, để có thể tùy biến sự hiển thị của
dữ liệu trên các ô của bảng theo ý mình, bạn cần phải biết về cell renderer.
Có thể nói, kiến trúc của Swing mang đến cho các component của nó một sự uyển chuyển
tuyệt vời trong việc hiển thị. UI được tách biệt với phần data. Bạn hoàn toàn có thể làm cho
một ô trong JTable hiển thị một thứ chẳng liên quan gì đến dữ liệu được cung cấp cho ô đó.
Để dễ hình dung hơn, bạn hãy tưởng tượng cell renderer giống như một họa sĩ, dữ liệu ví như
một cô gái. Đối với bạn, cô gái đó có xấu hay đẹp đều hoàn toàn phụ thuộc vào người họa sĩ
vẽ cô gái ấy như thế nào. Tương tự với cell renderer, dữ liệu được hiển thị ra sao đều do nó
quyết định.
Mỗi một ô trong JTable được vẽ ra bởi một đối tượng cell renderer. Đối tượng này được tạo ra
từ các lớp implements interface TableCellRenderer. Trong interface này định nghĩa một
phương thức là getTableCellRendererComponent. Phương thức này trả về tham chiếu đến
một component mà component này sẽ đóng vai trò như một cây bút vẽ để cell renderer vẽ
nên hình hài ô của bảng. Phương thức getTableCellRendererComponent gồm 6 tham số đầu
vào như sau:


Một tham chiếu đến cái JTable có các ô dữ liệu đang được vẽ



Một tham chiếu đến giá trị của ô dữ liệu



Một biến cờ để xác định xem ô này có đang được chọn (kích chuột vào) hay không



Một biến cờ để xác định xem ô này có là một ô để nhập dữ liệu đầu vào và đang được

trỏ đến hay không



Một chỉ số hàng của ô đang được vẽ



Một chỉ số cột của ô đang được vẽ

Ngoài việc trả về tham chiếu đến một component có khả năng hiển thị, phương thức
getTableCellRendererComponent còn có nhiệm vụ khởi tạo trạng thái ban đầu cho
component đó. Chú ý rằng một trong những tham số đầu vào của phương thức là một tham
chiếu đến giá trị của một ô. Và giá trị này sẽ được lưu trong component trước khi phương
thức trả về một tham chiếu đến nó.
JTable đã cung cấp sẵn cho chúng ta một tập các renderer đã được định nghĩa trước cho một
số kiểu dữ liệu thông dụng. Mặc định, nếu chúng ta không chỉ ra renderer cho cột của bảng,
JTable sẽ căn cứ theo kiểu dữ liệu của các cột đó để xác định xem renderer nào sẽ được áp
dụng. Vậy làm thế nào JTable xác định được cột nào thuộc kiểu dữ liệu nào? Đó chính là nhờ
vào phương thức getColumnClass của data model. Đây là lý do chúng ta nên override lại phương

thức này khi chúng ta tạo data model cho bảng. Nếu không override lại phương thức này, JTable sẽ hiểu
tất cả các cột của JTable mang kiểu Object.

1
2
3

public class CustomTableModel extends AbstractTableModel {
...................................



4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

/*
* JTable uses this method to determine the default renderer/
* editor for each cell. If we didn't implement this method,
* then the last column would contain text ("true"/"false"),
* rather than a check box.
*/
@Override
public Class getColumnClass(int c) {
if (getValueAt(0, c) == null) {
return String.class;

}
return getValueAt(0, c).getClass();
}

}

...................................

Các kiểu dữ liệu mà được JTable hỗ trợ sẵn renderer gồm có:
java.lang.Number
Đây là kiểu cha cho các kiểu số như Integer, Float, Long … Renderer dành cho kiểu này dùng
một JLabel để biểu diễn dữ liệu theo dạng chuỗi, nhưng được căn lề bên phải.
java.lang.Boolean
Renderer áp dụng cho kiểu này sẽ thay thế các giá trị true, false bằng một JCheckBox với
true tương đương với checked, false tương đương với unchecked.
java.util.Date
Renderer cho kiểu này cũng đơn giản là sử dụng một JLabel để biểu diễn giá trị thời gian đưa
vào theo Locale mà Java xác định được.

javax.swing.ImageIcon
Renderer kết hợp với lớp này cho phép chúng ta hiển thị một icon trên bảng. Nó lợi dụng khả
năng hiển thị cả ảnh và chữ của JLabel (phương thức setIcon cho phép chèn ảnh vào JLabel).
java.lang.Object
Khi JTable xác định kiểu dữ liệu cho cột của nó, nếu nó không tìm thấy renderer nào phù hợp
với kiểu dữ liệu được xác định, nó sẽ tiến hành tìm kiếm renderer của kiểu dữ liệu cha của
kiểu trước. Và cứ thế, nếu vẫn không tìm được, nó sẽ sử dụngDefaultTableCellRenderer –


renderer tương ứng với kiểu Object. Renderer này dùng một JLabel để biểu diễn dữ liệu dưới
dạng chuỗi, được căn lề bên trái.

Có nhiều cách để bạn tạo một custom renderer. Bạn có thể kế thừa lớp
DefaultTableCellRenderer, implements interface TableCellRenderer, hoặc vừa kế thừa một
component có khả năng biểu diễn dữ liệu vừa implements TableCellRenderer.
Trong demo dưới đây, mình sẽ biến chữ trong cột First Name thành màu xanh, trong cột Last
Name thành màu đỏ.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

public class ColorRenderer extends DefaultTableCellRenderer {

@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean
int column) {
if (column == 0) {
setForeground(Color.BLUE);
}

if (column == 1) {
setForeground(Color.red);
}
super.getTableCellRendererComponent(table, value, isSelected,
hasFocus, row, column);
return this;
}
}

Bạn có thể đặt câu hỏi tại sao lại return this? this ở đây chính là một JLabel. Điều này là
bởi vì DefaultTableCellRenderer được kế thừa từ JLabel. Đây là kết quả:

Trong bài tiếp theo, mình sẽ đưa cho các bạn một số demo về các cell renderer phổ biến mà
các bạn có thể hay dùng.

SỬ DỤNG HYPERLINKS TRONG PANEL
Hôm trước có bạn đã hỏi mình về sử dụng hyperlink trong Java. Qua tìm kiếm trên Google,
mình thấy có nhiều hướng dẫn hơi phức tạp và chưa có bài viết nào mang tính tổng quát. Do
vậy, mình viết bài viết này nhằm tổng hợp cũng như giới thiệu cho các bạn những cách dùng
của hyperlink trong Java Swing.


Có khá nhiều cách dùng của hyperlink, tùy theo từng mục đích cụ thể. Đầu tiên, ta sẽ xét
trường hợp bạn chỉ muốn chèn 1 link dạng label trên panel ứng dụng. Cách đầu tiên và đơn
giản nhất để sử dụng đó là sử dụng JXHyperlink trong SwingX.
Thư viện mở rộng SwingX cung cấp một compoment có tên gọi JXHyperlink để giúp chúng ta
dễ dàng chèn siêu liên kết ở bất kì đâu trong panel.
Cách tạo rất đơn giản. Bạn kéo thả JXHyperlink từ Palette của Netbeans vào panel. Sau đó bạn

thiết lập title và URI cho nó.


1
2
3
4
5
6

try {
jxLink.setURI(new URI("http://codeimba.com/"));
} catch (URISyntaxException ex) {
ex.printStackTrace();
}
jxLink.setText("Visit my site");

Thế là xong. Bạn đã tạo ra được một hyperlink như trên web vậy. Khi được click, nó sẽ mở ra
trang web có URI mà bạn đã thiết lập bằng trình duyệt mặc định trên máy tính của bạn.

Một cách khác bạn có thể sử dụng là làm giả giao diện (“look”) của các compenent như label hay button
để nó trông giống như một siêu liên kết. Sau đó bạn sẽ viết sự kiện để mở một URI khi người dùng click
vào. Đây là ví dụ mình làm với JButton:

1
2
3
4
5
6
7
8

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

btnLink.setForeground(new java.awt.Color(0, 0, 255));
btnLink.setText("Go codeimba.com");
btnLink.setBorder(null);
btnLink.setBorderPainted(false);
btnLink.setContentAreaFilled(false);
btnLink.setCursor(new java.awt.Cursor(java.awt.Cursor.HAND_CURSOR));
btnLink.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
btnLinkActionPerformed(evt);
}
});
..............
// handling event on click button

private void btnLinkActionPerformed(java.awt.event.ActionEvent evt)
{
// TODO add your handling code here:
try {
Desktop.getDesktop().browse(new URI("http://codeimba.com"));
} catch (URISyntaxException ex) {
ex.printStackTrace();
} catch (IOException io) {
io.printStackTrace();
}
}

Mình copy ra cho các bạn nhìn thấy thôi. Các bạn hãy sử dụng cửa sổ Properties trong
Netbeans để tiết kiệm thời gian nhé.


Trường hợp sử dụng thường thấy nữa là bạn có một đoạn văn bản có bao gồm cả các liên
kết. Bạn muốn hiển thị đoạn văn bản này lên giao diện phần mềm Java sao cho các liên kết
vẫn được giữ nguyên. Khi người dùng click vào chúng, mỗi liên kết sẽ duyệt đến trang web
được chỉ ra trong thuộc tính href của liên kết đó. Trong trường hợp này, các bạn có thể sử
dụng JEditorPane để làm điều đó.
Điều đầu tiên, bạn phải set thuộc tính editable là false cho JEditorPane. Tiếp theo, bạn set
content type của văn bản trong JEditorPane là text/html. Bạn cung cấp nội dung html cho
JEditorPane hiển thị thông qua phương thức setText() của nó. Làm đến đây, chúng ta đã được
một đoạn văn bản với các liên kết như mong muốn. Cuối cùng, để người dùng click vào liên
kết thì mở lên kết chứa trong nó, bạn cần phải bắt sự kiện HyperlinkUpdate của JEditorPane.

Cụ thể, đoạn code hoàn chỉnh cho phần này sẽ như sau nếu bạn gõ tay (nhớ sử dụng tool nhé bạn):

1

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

jep.setEditable(false);
jep.setContentType("text/html");
jep.setText("rn rnrn
rnrn");
.............

rn

rn

rn


// event
private void jepHyperlinkUpdate(javax.swing.event.HyperlinkEvent evt) {
// TODO add your handling code here:
if (evt.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
try {
Desktop.getDesktop().browse(evt.getURL().toURI());
} catch (URISyntaxException ex) {
ex.printStackTrace();
} catch (IOException io) {
io.printStackTrace();
}
}
}

Đó là cách trường hợp và cách thực hiện phổ biến mà mình tổng hợp và giới thiệu đến các
bạn. Hi vọng bài viết sẽ giúp bạn nhiều trong việc sử dụng hyperlink trong Java.

CHỈNH SỬA DỮ LIỆU TRỰC TIẾP TRÊN JTABLE
JTable trong Java có một tính năng rất hay ho. Đó là ngoài việc hiển thị dữ liệu, nó
còn hỗ trợ bạn sửa trực tiếp dữ liệu trên nó.
Tiếp nối bài viết trước, trong bài viết này, mình sẽ hướng dẫn các bạn implements một số
phương thức trong data model để có thể sửa dữ liệu ngay trên JTable. Và quan trọng hơn là
cách xử lý dữ liệu mà người dùng vừa nhập đó để update dữ liệu gốc trong database hay
file…
Trước hết, muốn JTable có thể chỉnh sửa được, bạn cần phải override phương
thứcisCellEditable() của AbstractTableModel trong data model mà bạn tạo (ở đây mình mặc
định là data model được tạo ra bằng cách kế thừa AbstractTableModel). Mặc định phương


thức isCellEditable() đã được implements trong AbstractTableModel để luôn trả về giá

trị false. Bạn có thể đơn giản làm cho cả bảng editable bằng 1 dòng lệnh return true. Tuy

nhiên, không phải trong trường hợp nào bạn cũng muốn tất cả các cột trong JTable editable. Ví dụ như
bạn lấy ra một bảng mà có cột Id có thuộc tính Identity trong database và show nó lên JTable. Bạn chắc
sẽ không muốn cột Id của mình editable làm gì.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

public class CustomTableModel extends AbstractTableModel {
...................
/*
* Don't need to implement this method unless your table's
* editable.
*/
@Override
public boolean isCellEditable(int row, int col) {

//Note that the data/cell address is constant,
//no matter where the cell appears onscreen.
return col == 1;
}
}

Đoạn code trên chỉ ra rằng bạn chỉ muốn cột thứ 2 (chỉ mục 1) editable mà thôi, các cột còn
lại thì không.
Tiếp theo, bạn cần phải override lại phương thức setValueAt(). Phương thức này có tham số đầu

tiên là giá trị mà người dùng vừa sửa, các tham số tiếp theo lần lượt là chỉ số hàng và cột của ô được
sửa. Giả sử data của chúng ta là một mảng 2 chiều, bạn có thể implements như sau:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

18
19
20

public class CustomTableModel extends AbstractTableModel {

public final static boolean GENDER_MALE = true;
public final static boolean GENDER_FEMALE = false;
public final static String[] columnNames = {
"First Name", "Last Name", "Date of Birth", "Account Balance",
"Gender"
};
public Object[][] values = {
{
"Clay", "Ashworth",
new GregorianCalendar(1962, Calendar.FEBRUARY,
20).getTime(),
new Float(12345.67), GENDER_MALE}, {
"Jacob", "Ashworth",
new GregorianCalendar(1987, Calendar.JANUARY, 6).getTime(),
new Float(23456.78), GENDER_MALE}, {
"Jordan", "Ashworth",
new GregorianCalendar(1989, Calendar.AUGUST, 31).getTime(),
new Float(34567.89), GENDER_FEMALE}, {
"Evelyn", "Kirk",
new GregorianCalendar(1945, Calendar.JANUARY, 16).getTime(),
new Float(-456.70), GENDER_FEMALE}, {
"Belle", "Spyres",



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

new GregorianCalendar(1907, Calendar.AUGUST, 2).getTime(),
new Float(567.00), GENDER_FEMALE}
};
...................

/*
* Don't need to implement this method unless your table's
* editable.
*/
@Override
public boolean isCellEditable(int row, int col) {
//Note that the data/cell address is constant,
//no matter where the cell appears onscreen.
return col == 1;
}
@Override
public void setValueAt(Object value, int row, int col) {
values[row][col] = value;
fireTableCellUpdated(row, col);

}

}

Công việc mà bạn cần làm trong phương thức này là khi người dùng đưa vào giá trị mới cho
một ô, bạn sẽ cập nhật lại dữ liệu của ô đó bằng giá trị mới này. Sau khi cập nhật, JTable sẽ
tự rebuild UI để hiển thị giá trị mới. Phương thứcfireTableCellUpdated được gọi ra có mục
đích thông báo đến tất cả các bộ lắng nghe sự kiện rằng data model của JTable vừa có sự
thay đổi ở hàng thứ row, cột thứ col. Bạn có thể tự hỏi liệu có cần thiết
gọi fireTableCellUpdated không? Hãy bình tĩnh đọc tiếp đã nhé.
Có một điều rất hay. Bạn dễ dàng nhận thấy JTable chỉ cập nhật giá trị mới trên giao diện của
nó khi nào câu lệnh cập nhật giá trị trong phương thức setValueAt() được thực thi thành công.

Bạn có thể lời dụng điều này để bắt lỗi khi người dùng sửa dữ liệu. Giả sử mình sẽ bắt lỗi cột “Last
Name” không được ít hơn 3 ký tự.


1
2
3
4
5
6
7
8
9
10

public class CustomTableModel extends AbstractTableModel {
...................

/*
* Don't need to implement this method unless your table's
* editable.
*/
@Override
public boolean isCellEditable(int row, int col) {
//Note that the data/cell address is constant,


11
12
13
14
15
16

17
18
19
20
21
22
23
24
25
26

}

//no matter where the cell appears onscreen.
return col == 1;

@Override
public void setValueAt(Object value, int row, int col) {
if (value.toString().length() < 3) {
JOptionPane.showMessageDialog(null, "Must be more than 3
letters");
return;
}
values[row][col] = value;
fireTableCellUpdated(row, col);
}
}
Kết quả khi thử nhập sai:

Cơ bản như vậy là đã xong. Tuy nhiên, trong bài toán thực tế, khi mà data của bạn thường

được lấy ra từ database, từ file, không phải là mảng 2 chiều được khởi tạo sẵn như trong
demo trên kia, thì những thay đổi mà chúng ta vừa làm không có ý nghĩa gì. Thật vậy, nếu
chúng ta dừng lại ở đây, những gì đã được chỉnh sửa chỉ có ảnh hưởng đến phần giao diện
của JTable, có nghĩa là lần sau bạn vào lại ứng dụng thì đâu lại vào đấy, vẫn nguyên như lúc
đầu.
Để cập nhật sự thay đổi lên nguồn dữ liệu gốc (database, file,…), bạn sử dụng bộ lắng nghe
sự kiện TableModelListener của data model để bắt những thông báo được phát sinh
bởi fireTableCellUpdated khi cập nhật JTable thành công (xem lại đoạn trên để hiểu rõ hơn).
Mỗi khi bắt được 1 thông báo, event tableChanged sẽ được kích hoạt. Đến đây là lời giải
thích cho câu hỏi ở phía trên. Bởi vì đơn giản bạn có thể hiểu nôm na rằng, nếu bạn không


dùng fireTableCellUpdated để ném ra thông báo thì bộ lắng sự kiện TableModelListener cũng chẳng

bao giờ kích hoạt được sự kiện tableChanged cả. Và như thế những gì bạn viết trong sự kiện đó cũng sẽ
chẳng bao giờ được thực thi. Ví dụ minh họa:

1
2
3
4
5
6
7
8
9
10

myTable.getModel().addTableModelListener(new TableModelListener() {
@Override

public void tableChanged(TableModelEvent e) {
// ví dụ
// lấy về giá trị Id
// lấy về giá trị mới được sửa
// thực hiện cập nhật tới database
}
});

Ngoài cách trên, bạn cũng có thể viết đoạn code xử lý trong phương thứcsetValueAt() khi
bạn override nó. Nhưng theo mình nên tách riêng model ra để phần code được rõ ràng,
mạch lạc.
Bạn cũng nên lưu ý rằng, không phải lúc nào cách trên cũng hiệu quả. Nó sẽ không thích hợp
nếu bảng của bạn có quá nhiều cột editable. Khi đó, thay vì chỉ cần sửa tất cả các cột rồi
update 1 lần, nó sẽ update liên tục mỗi khi bạn sửa 1 cột. Điều này gây tiêu tốn tài nguyên,
giảm hiệu năng hệ thống.

CÁCH SỬ DỤNG JTABLE CƠ BẢN TRONG JAVA
Trong Java, JTable là một trong những component chủ đạo trong việc hiển thị dữ liệu. Nó là
một Swing component cung cấp khả năng biểu diễn dữ liệu ở dạng bảng biểu. Ngoài ra,
JTable còn cho phép bạn có thể dễ dàng chỉnh sửa thông tin ngay trên các cell của nó như
khi bạn làm việc với Excel vậy. Trong bài viết này, chúng ta sẽ cùng tìm hiểu cách thao tác
với dữ liệu của bảng để hiển thị chúng lên JTable.
Như các bạn đã biết, kiến trúc của JTable nói riêng và Swing nói chung, đều được xây dựng
dựa theo mô hình

MVC nên phần data (Model) sẽ tách biệt với phần giao diện (UI). Điều

này tạo nên sự rõ ràng, mạch lạc cho code, nhưng có thể gây khó khăn với những ai mới bắt
đầu.


Để hiển thị dữ liệu với JTable, chúng ta có nhiều cách. Bạn có thể lợi dụng một contructor của JTable như
sau:

1
2
3
4
5
6
7
8
9

public class MyTable extends javax.swing.JFrame {
public final static boolean GENDER_MALE =
public final static boolean GENDER_FEMALE
public final static String[] columnNames
"First Name", "Last Name", "Date of
"Gender"
};
public Object[][] values = {
{
"Clay", "Ashworth",

true;
= false;
= {
Birth", "Account Balance",



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

new GregorianCalendar(1962, Calendar.FEBRUARY,
20).getTime(),
new Float(12345.67), GENDER_MALE}, {

"Jacob", "Ashworth",
new GregorianCalendar(1987, Calendar.JANUARY, 6).getTime(),
new Float(23456.78), GENDER_MALE}, {
"Jordan", "Ashworth",
new GregorianCalendar(1989, Calendar.AUGUST, 31).getTime(),
new Float(34567.89), GENDER_FEMALE}, {
"Evelyn", "Kirk",
new GregorianCalendar(1945, Calendar.JANUARY, 16).getTime(),
new Float(-456.70), GENDER_FEMALE}, {
"Belle", "Spyres",
new GregorianCalendar(1907, Calendar.AUGUST, 2).getTime(),
new Float(567.00), GENDER_FEMALE}
};
/** Creates new form MyTable */
public MyTable() {
initComponents();
setLayout(new BorderLayout());
JTable myTable = new JTable(values, columnNames);
JScrollPane jsp = new JScrollPane(myTable);
getContentPane().add(jsp, BorderLayout.CENTER);
}

Bạn cũng lưu ý rằng, để JTable có thể hiển thị tên cột, các bạn

cần bao nó lại bằng

một JScrollPane.
Cách trên giúp bạn nhanh chóng tạo ra một JTable với dữ liệu được hiển thị. Mảng 1 chiều
cung cấp tên cột, mảng 2 chiều cung cấp data cho bảng. Tuy nhiên, cách này không thực tế,
chúng ta thường chỉ sử dụng nó trong demo, khi bạn mới học về JavaSE. Một cách khác mà

mình sẽ giới thiệu cho các bạn, cách mà các bạn sẽ thường dùng trong bài tập lớn, project…,
đó là sử dụng data model.
Data model đảm nhận nhiệm vụ cung cấp dữ liệu hiển thị cho JTable. Sử dụng data model
giúp ứng dụng “MVC” hơn bằng việc tách riêng phần data và phần UI, tạo ra sự custom tốt
hơn cho những bài toán phức tạp.
Về cơ bản, một data model được cài đặt

9 phương thức do interface TableModel định

nghĩa. Các phương thức đó được liệt kê trong hình dưới đây:


Tác dụng của những phương thức trên bạn có thể dễ dàng suy ra thông qua tên của chúng,
mình không nói lại nữa nhé. Bạn cũng có thể tham khảo thêm trên Java docs. Bạn sẽ phải tự
mình cài đặt cả 9 phương thức trong trường hợp bạn tạo ra data model bằng cách
implements interface TableModel. Tuy nhiên Java cũng tạo ra một số các lớp khác được cài
đặt sẵn một số phương thức, giúp cho công việc của chúng ta đơn giản hơn. Cụ thể như
class DefaultTableModel và abstract class AbstractTableModel, tất cả đều implements
interface TableModel. Thông thường, chúng ta sẽ tạo ra data model bằng cách kế thừa
AbstractTableModel. Khi đó, chúng ta chỉ cần thiết phải override lại 4 phương thức.
Dưới đây, mình sẽ tạo ra lớp CustomTableModel như sau:
public class CustomTableModel extends AbstractTableModel {
1

2
3
4
5
6
7

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

public final static boolean GENDER_MALE = true;
public final static boolean GENDER_FEMALE = false;
public final static String[] columnNames = {
"First Name", "Last Name", "Date of Birth", "Account Balance",
"Gender"
};
public Object[][] values = {
{
"Clay", "Ashworth",
new GregorianCalendar(1962, Calendar.FEBRUARY,
20).getTime(),

new Float(12345.67), GENDER_MALE}, {
"Jacob", "Ashworth",
new GregorianCalendar(1987, Calendar.JANUARY, 6).getTime(),
new Float(23456.78), GENDER_MALE}, {
"Jordan", "Ashworth",
new GregorianCalendar(1989, Calendar.AUGUST, 31).getTime(),
new Float(34567.89), GENDER_FEMALE}, {
"Evelyn", "Kirk",
new GregorianCalendar(1945, Calendar.JANUARY, 16).getTime(),
new Float(-456.70), GENDER_FEMALE}, {
"Belle", "Spyres",
new GregorianCalendar(1907, Calendar.AUGUST, 2).getTime(),
new Float(567.00), GENDER_FEMALE}
};
@Override
public int getRowCount() {


26
27
28
29
30
31
32
33
34
35
36
37

38
39
40
41
42
43
44
45
46
47
48

}

return values.length;

@Override
public int getColumnCount() {
return columnNames.length;
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
return values[rowIndex][columnIndex];
}
// Need for showing column name
@Override
public String getColumnName(int columnIndex) {
return columnNames[columnIndex];
}
}


Trong thực tế, dữ liệu của bảng thường được cung cấp dưới dạng Vector hoặc List thay vì
mảng 2 chiều như demo này. Khi đó, bạn cần sửa lại những phương thức đã override cho phù
hợp.
Để JTable của chúng ta sử dụng data model vừa được tạo ra, chúng ta có thể sử dụng
phương thức setModel() của JTable:
1 myTable.setModel(new CustomTableModel());
Kết quả khi chạy chương trình:

Nhìn từ bề nổi, bạn có thể nhận xét rằng cấu trúc của 2 lớp này chỉ khác nhau ở chỗ
DefaultTableModel được cài đặt nốt 3 phương thức mà AbstractTableModel còn bỏ ngỏ. Thế
nhưng còn có sự khác biệt khá lớn nữa, để AbstractTableModel được khuyến khích sử dụng,
chứ không phải DefaultTableModel.


Sở dĩ như vậy là bởi vì AbstractTableModel cho phép chúng ta toàn quyền điều khiển dữ liệu
input cũng như output. Để xuất dữ liệu ra bảng, chúng ta phải tự mình cài đặt phương
thức getValueAt(). Phương thức này nhận 2 tham số là chỉ số cột và chỉ số dòng. Nó quy
định dữ liệu gì sẽ được cung cấp cho mỗi ô trong bảng (được xác định bởi 2 tham số hình
thức). Không giống như thế, DefaultTableModel thiếu tính mềm dẻo và mở rộng hơn nhiều.
Mặc định nó được cài sẵn tất cả các phương thức mà đã implements từ TableModel. Điều này
dẫn đến việc bạn sẽ khó khăn hơn khi muốn can thiệp vào quá trình xử lý bảng. Hơn nữa,
DefaultTableModel sử dụng Vector (chính xác hơn là một Vector bao gồm những Vector khác)
làm dữ liệu input. Và kéo theo đó, các phương thức như getValueAt(), setValueAt()…
trong quá trình thực thi cũng thao tác với dữ liệu dạng Vector này. Vector là một Collection
đã “lỗi thời” xuất hiện từ thời Java 1.0. Bạn có thể thấy trong Netbeans, bạn sẽ nhận được
thông báo “Obsolete Collection” nếu cố tình dùng Vector. Vector có khá nhiều hạn chế như
less thread-safe và chậm chạp hơn so với các Collection khác. ArrayList được khuyến cáo
thay thế cho Vector. Cụ thể mình sẽ có một bài viết khác về vấn đề này.
Vậy nên, nếu làm việc với data model, bạn nên sử dụng AbstractTableModel và sử dụng

ArrayList cho dữ liệu đầu vào.
Trong bài viết này, mình đã hướng dẫn cho các bạn cơ bản về cách hiển thị dữ liệu với JTable.
Ở vài viết tiếp theo, chúng ta vẫn sẽ xoay quanh data model. Mình sẽ hướng dẫn các bạn
override một số phương thức của TableModel để có thể chỉnh sửa dữ liệu trực tiếp trên các ô
của bảng và làm thế nào để xử lý kết quả người dùng vừa mới cập nhật đó.

CÀI ĐẶT VÀ SỬ DỤNG SWINGX
Bản chất của việc cài đặt và sử dụng SwingX trong Netbeans là việc bạn đưa các component
của SwingX vào trong Palette của Netbeans để phục vụ cho việc kéo thả.
Trong bài viết này, mình sẽ hướng dẫn chi tiết cách để đưa một thư viện Java vào trong
Palette của Netbeans. Sau bước này, bạn có thể kéo thả các component của thư viện đó
tương tự như cách bạn vẫn làm với các component của Swing. Mình demo với SwingX, các
thư viện khác như JCalendar.., các bạn hoàn toàn làm tương tự.
Bước 1: Trong Netbeans, các bạn chọn Tools -> Palette -> Swing/AWT Components


Bước 2: Click chọn New Category…, đặt tên cho category là SwingX, sau đó nhấn OK


Bước 3: Click vào category SwingX mà bạn vừa tạo, nhấn nút Add from JAR…


Tài liệu bạn tìm kiếm đã sẵn sàng tải về

Tải bản đầy đủ ngay
×