Tin tức mới

[Swing] Trang trí bảng với hiệu ứng highlight

Bảng là một trong những thành phần được dùng nhiều nhất khi xây dựng các ứng dụng dựa trên Swing. Bất cứ khi nào ta muốn hiển thị danh sách các đối tượng cùng với các thuộc tính của nó dưới dạng các hàng và các cột thì ta đều có thể sử dụng bảng. Đối với người dùng cuối thì việc sử dụng bảng thông thường là rất tiện lợi và chẳng có chút khó khăn nào. Thế nhưng trong một số trường hợp khi kích thước của bảng lớn thì người dùng có thể gặp khó khăn khi muốn xác định giá trị trong một ô nhất định nào đó của bảng, hoặc là khi người dùng muốn xem và so sánh các giá trị cùng nằm trên một dòng, phân biệt dòng này với dòng khác; Đối với những trường hợp như vậy thì cách tốt nhất để xử lý đó là tạo highlight cho bảng. Bài viết này sẽ hướng dẫn các bạn một cách đơn giản để tạo hiệu ứng highlight cho bảng bằng cách sử dụng CellRenderer.

Trong bài viết này tôi có đề cập đến các nội dung liên quan đến JTable, TableModel, CellRenderer…, nếu các bạn có thắc mắc gì thì xin cứ để lại comment, tôi hứa sẽ giải đáp nhanh nhất. Ngoài ra tôi cũng có dự định viết một số bài khác với các nội dung liên quan đến các thành phần này, sẽ lên mây nhanh thôi, xin thông báo để các bạn tiện theo dõi. 🙂

Trước tiên chúng ta nói về hiệu ứng highlight, đây là một dạng hiệu ứng mà có thể bạn đã gặp ở rất nhiều nơi ở đâu đó trên web hoặc ở các ứng dụng desktop; hiệu ứng highlight mà chúng ta đang nói đến chính là việc làm nổi bật một thành phần nào đó lên trên các phần khác bằng cách thay đổi màu chữ hoặc là màu nều của nó. Trong trường hợp của bảng thì thông thường các dòng chẵn sẽ có một màu nền riêng, các dòng lẻ sẽ có một màu nều riêng, những dòng mà đang được trỏ chuột đến thì sẽ có một màu nền riêng, và đây cũng chính là ví dụ mà chúng ta sẽ thực hiện trong bài viết này.

Trong ví dụ dưới đây chúng ta sẽ sử dụng một bảng để hiển thị các thông tin về thủ đô, diện tích, dân số và tiền tệ của một số nước trên thế giới. Để làm được điều đó thì trước tiên chúng ta sẽ tạo một lớp có tên là Country với các thuộc tính được nêu ở trên:

[sourcecode lang=”java”]package demo;

public class Country {
private String name;
private String capital;
private int area;
private int population;
private String currency;

public Country(String name, String capital, int area, int population, String currency) {
this.name = name;
this.capital = capital;
this.area = area;
this.population = population;
this.currency = currency;
}

public int getArea() {
return area;
}

public String getCapital() {
return capital;
}

public String getCurrency() {
return currency;
}

public String getName() {
return name;
}

public int getPopulation() {
return population;
}
}
[/sourcecode]

Để tạo một ít dữ liệu mẫu cho bảng thì chúng ta sẽ tạo một lớp tiện ích có tên là CountryData với một phương thức duy nhất là getCountries(), phương thức này sẽ trả về một danh sách các quốc gia đã được định sẵn, trong một ứng dụng thật thì phương thức này sẽ được viết lại để cung cấp dữ liệu thật.

[sourcecode lang=”java”]
package demo;

import java.util.LinkedList;
import java.util.List;

public class CountryData {
public static List getCountries(){
List countries = new LinkedList();
countries.add(new Country("Brazil", "Brasília", 8514877, 192376496, "Real (BRL)"));
countries.add(new Country("India", "New Delhi", 3287263, 1210193422, "Indian rupee (INR)"));
countries.add(new Country("United States", "Washington, D.C.", 9826675, 313943000, "United States dollar (USD)"));
countries.add(new Country("Vietnam", "Hanoi", 331210, 91519289, "đồng (VND)"));
countries.add(new Country("China", "Beijing", 9640821, 1339724852, "Renminbi (CNY)"));
countries.add(new Country("Russian", "Moscow", 17075400, 143056383, "Ruble (RUB)"));
return countries;
}
}
[/sourcecode]

Công việc tiếp theo mà chúng ta sẽ thực hiện đó là tạo lớp CountryTableModel, lớp này sẽ là model cho bảng các quốc gia của chúng ta sau này. Để hiểu thêm về model của bảng thì bạn nên đọc ở đây, hoặc là đón đọc một bài viết khác của tôi về đối tượng này nhé, chắc là sắp có rồi đó 😉

[sourcecode lang=”java”]
package demo;

import java.util.List;
import javax.swing.table.AbstractTableModel;

public class CountryTableModel extends AbstractTableModel{
private List countries;

public CountryTableModel(List countries) {
this.countries = countries;
}

@Override
public int getRowCount() {
return countries.size();
}

@Override
public int getColumnCount() {
return 5;
}

@Override
public Object getValueAt(int rowIndex, int columnIndex) {
Country c = countries.get(rowIndex);
switch(columnIndex){
case 0: return c.getName();
case 1: return c.getCapital();
case 2: return c.getArea();
case 3: return c.getPopulation();
case 4: return c.getCurrency();
default: return null;
}
}

@Override
public String getColumnName(int column) {
switch(column){
case 0: return "Name";
case 1: return "Capital";
case 2: return "Area";
case 3: return "Population";
case 4: return "Currency";
default: return "";
}
}
}
[/sourcecode]

Sau khi đã tạo xong lớp CountryTableModel thì chúng ta sẽ bắt tay vào việc tạo giao diện cho ứng dụng. Bắt đầu bằng việc tạo một JFrame, sau đó thêm một JTable có model là một đối tượng của CountryTableModel.

[sourcecode lang=”java”]
package demo;

import java.awt.BorderLayout;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JTable;

public class TableHighlight {
public static void main(String[] args) {
JFrame frame = new JFrame("Table with highlight");
List countries = CountryData.getCountries();
CountryTableModel countryModel = new CountryTableModel(countries);
JTable tblCountries = new JTable(countryModel);
frame.getContentPane().add(tblCountries);
frame.getContentPane().add(tblCountries.getTableHeader(), BorderLayout.NORTH);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
}

[/sourcecode]

Bây giờ thì ta đã có thể chạy thử ứng dụng và quan sát. Chúng ta thấy rằng danh sách các quốc gia cùng với các thuộc tính của nó đã được hiển thị trong bảng như chúng ta mong muốn,  một khi chắc chắn bạn đã làm được các bước trên thì ta mới bắt đầu thực hiện công việc chính của mình đó là tạo highlight cho bảng. Có một cách đơn giản để thực hiện điều này đó là sử dụng  một CellRenderer tùy chỉnh cho bảng, về cách sử dụng CellRenderer tôi sẽ đề cập kỹ hơn ở một bài viết khác, lớp này đơn giản như sau.

[sourcecode lang=”java”]
package demo;

import java.awt.Color;
import java.awt.Component;
import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.table.TableCellRenderer;

public class CountryCellRenderer extends JLabel implements TableCellRenderer{

@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
if(row%2 != 0){
this.setBackground(Color.CYAN);
}else{
this.setBackground(Color.lightGray);
}
this.setOpaque(true);
this.setText(value.toString());
return this;
}
}
[/sourcecode]

Ở đoạn code trên thì chúng ta đã thay đổi màu nền của JLabel dựa vào tham số row, đối với các dòng chẵn thì ta quy định màu nền riêng, và một màu nền khác cho các dòng lẻ. Phương thức setOpaque() trong trường hợp này sẽ giúp cho JLabel hiển thị đầy đủ màu nền của mình. Ứng dụng của ta sẽ có giao diện như sau:

Giao diện này đã được trang trí đẹp và rõ ràng hơn trước,nhưng chúng ta không dừng ở đây mà tiếp tục chỉnh sửa bảng các quốc gia để nó càng tiện lợi hơn nữa. Lần này chúng ta sẽ tạo hiệu ứng highlight cho bảng mỗi khi ta trỏ chuột đến một dòng nào đó. Để làm hiệu ứng này thì chúng ta cần chỉnh sửa lại lớp CountryCellRenderer lại một chút, đồng thời thêm một MouseMotionListener cho đối tượng tblCountries của mình. Trước hết, cần thêm thuộc tính currentRow cho class này, và bổ sung thêm một số dòng lệnh cho phương thức getTableCellRendererComponent() :

[sourcecode lang=”java”]
package demo;
import java.awt.Color;
import java.awt.Component;
import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.table.TableCellRenderer;

public class CountryCellRenderer extends JLabel implements TableCellRenderer{

public static int currentRow = -1;
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
if(row%2 != 0){
this.setBackground(new Color(190, 220, 190));
}else{
this.setBackground(new Color(230, 255, 250));
}
if(row == currentRow)
this.setBackground(new Color(250, 250, 150));
this.setOpaque(true);
this.setText(value.toString());
return this;
}
}
[/sourcecode]

Và ta sẽ thêm  một MouseMotionListener cho đối tượng tblCountries như đoạn code sau:

[sourcecode lang=”java”]
tblCountries.addMouseMotionListener(new MouseAdapter() {
@Override
public void mouseMoved(MouseEvent e) {
JTable table = (JTable)e.getSource();
CountryCellRenderer.currentRow = table.rowAtPoint(e.getPoint());
table.repaint();
}
});
[/sourcecode]

Phương thức getPoint() của lớp MouseEvent được dùng để lấy về vị trí hiện tại của chuột, khi đã có vị trí của chuột thì ta dùng phương thức rowAtPoint() của JTable để lấy về dòng hiện tại có con trỏ chuột, nhiệm vụ cuối cùng đó là cập nhật lại vị trí này cho lớp CountryCellRenderer. Hãy cùng ngắm thành quả lao động của chúng ta nào.

Thật là ấn tượng đúng không, đơn giản nhưng đẹp, chỉ bằng cách sử dụng một TableCellRenderer tùy chỉnh thì chúng ta đã tạo được một hiệu ứng đẹp và tiện ích cho bảng của mình. Nếu bạn thích bài viết này, hãy đóng góp bằng cách gửi các phản hồi, theo dõi  và tham gia Tạp chí Lập trình bằng cách viết các thủ thuật khác để chia sẽ cùng mọi người.

Tải mã nguồn

Tham khảo:

How to use table  (http://docs.oracle.com/javase/tutorial/uiswing/components/table.html)


Hãy tham gia nhóm Học lập trình để thảo luận thêm về các vấn đề cùng quan tâm.

6 thoughts on “[Swing] Trang trí bảng với hiệu ứng highlight

  1. Em xin chào thầy(cô),bài viết rất hay nhưng không ghi tên tác giả^^!em thấy dữ liệu của thầy(cô) là dữ liệu cục bộ,bây giờ em muốn lấy dữ liệu từ sql server hoặc từ đâu đó như mô hình ứng dụng tương tác trên mạng để ghi vào cái mảng Linkerlist kia thì sẽ phải làm sao?mong thầy(cô) ra phần này sớm hơn phần trang trí bảng:D

  2. Đơn giản thôi, bạn chỉ cần chỉnh sửa lại phương thức getCountries() của lớp CountryData. Thay thế các dòng hack cứng dữ liệu bằng các dòng code lấy dữ liệu từ nguồn nào đó: File, DB, v.v.

  3. Sau một đêm nghâm cứu em đã solve đc vấn đề!
    Code:
    ArrayList obj = new ArrayList();
    …..v.v…
    while(rs.next())
    {
    Properties per = new Properties(abc,xyz….);
    obj.add(per);
    }
    …v.v…
    //Done

    1. Code của bạn khá là tốt rồi đó, nếu cải tiến thêm chút nữa thì sẽ không có gì phàn nàn.
      Thứ nhất, bạn nên chuyển ArrayList sang dạng generic, ví như là ArrayList chẳng hạn.
      Thứ 2, trong vòng lặp while mà bạn dùng để duyệt qua các dòng trong Resultset thì có thể không sử dụng lớp Properties, mà thay vào đó là lớp của đối tượng mà bạn đang muốn lấy dữ liệu ra, ví như là Country c = new Country(abc, xyz…);

Leave a Reply

Your email address will not be published. Required fields are marked *