Xây dựng giao diện java so sánh

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é.

Đổi màu chữ trong JTable

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.

public static class ColorRenderer extends DefaultTableCellRenderer {

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

Căn lề cho cột

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

public static class AlignRenderer extends DefaultTableCellRenderer {

@Override  
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, 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;  
}  
}

Chèn ảnh vào ô của bảng

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

public static class AlignRenderer extends DefaultTableCellRenderer {

@Override  
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, 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;  
}  
} 0.

public static class ImageRenderer extends DefaultTableCellRenderer {

@Override  
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, 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.

Định dạng tiền tệ – Currency

public static class CurrencyRenderer extends DefaultTableCellRenderer {

@Override  
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {  
    if ((value != null) && (value instanceof Number)) {  
        Number numberValue = (Number) value;  
        NumberFormat formater = NumberFormat.getCurrencyInstance();  
        value = formater.format(numberValue.doubleValue());  
    }  
    setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);  
    super.getTableCellRendererComponent(table, value, isSelected,  
            hasFocus, row, column);  
    return this;  
}  
} Hoặc bạn làm như sau:

public static class CurrencyRenderer extends DefaultTableCellRenderer {

public CurrencyRenderer() {  
    super();  
    setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);  
}
@Override  
public void setValue(Object value) {  
    if ((value != null) && (value instanceof Number)) {  
        Number numberValue = (Number) value;  
        NumberFormat formater = NumberFormat.getCurrencyInstance();  
        value = formater.format(numberValue.doubleValue());  
    }  
    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.

Output một giá trị khác với giá trị đầu vào

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”.

public static class OutputRenderer extends DefaultTableCellRenderer {

@Override  
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, 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.

Tạo màu nền cho các ô trong bả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:

public static class BgRenderer extends DefaultTableCellRenderer {

@Override  
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {  
    JPanel pn = new JPanel(new BorderLayout());  
    JLabel lb = new JLabel();  
    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;  
}  
}

Sử dụng các renderer đã được tạo

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: