Sử dụng JTable của Swing trong java (phần 7)

https://codersontrang.com/2012/09/11/su-dung-jtable-cua-swing-trong-java-phan-7/

Trong phần này chúng ta sẽ học cách để tạo tiêu đề cho các hàng trong một bảng và tạo cột đóng băng.

Tạo tiêu đề cho hàng
Trong rất nhiều trường hợp, một bảng chỉ chứa các tiêu đề cho các cột là chưa đủ bởi vì sẽ có lúc chúng ta còn muốn tạo tiêu đề cho các hàng trong bảng nữa. Để làm điều này thì cũng khá là dễ dàng bởi vì JScrollPane không chỉ có viewport cho tiêu đề của cột mà còn cả cho tiêu đề của hàng nữa. Không giống như viewport cho tiêu đề của cột, theo mặc định, viewport cho tiêu đề của hàng là trống. Tuy nhiên, như đã nói ở trên, việc tạo ra một tiêu đề cho hàng và hiển thị nó là khá dễ dàng.

Dưới đây chúng ta tạo một lớp RowNumberHeader trong file RowNumberHeader.java để làm một tiêu đề cho hàng trong bảng ở ví dụ của chúng ta. Bản chất của tiêu đề dưới đây chính là một JTable, và model cho JTable này chính là lớp RowNumberHeaderModel. Số cột của bảng này là 1, số hàng bằng với số hàng của bảng mà ta đang muốn thêm tiêu đề cho hàng. Giá trị trong từng ô của bảng bằng với giá trị của chỉ số hàng tương ứng cộng thêm một đơn vị.


public class RowNumberHeader extends JTable{
    protected JTable mainTable;

    public RowNumberHeader (JTable table){
        super();
        mainTable = table;
        setModel(new RowNumberTableModel());
        setPreferredScrollableViewportSize(getMinimumSize());
        setRowSelectionAllowed(false);
        JComponent renderer = (JComponent)getDefaultRenderer(Object.class);
        LookAndFeel.installColorsAndFont(renderer, "TableHeader.background", 
                                         "TableHeader.foreground", "TableHeader.font");
        LookAndFeel.installBorder(this, "TableHeader.cellBorder");
    }

    @Override
    public int getRowHeight(int row){
        return mainTable.getRowHeight();
    }

    class RowNumberTableModel extends AbstractTableModel{

        public int getRowCount() {
            return mainTable.getModel().getRowCount();
        }

        public int getColumnCount() {
            return 1;
        }

        public Object getValueAt(int rowIndex, int columnIndex) {
            return new Integer(rowIndex+1);
        }
    }
}

Giờ chúng ta thay đổi lớp SimpleTableTest như sau:


public class SimpleTableTest extends JFrame{
    protected JTable table;

    public SimpleTableTest(){
        [...]
        JScrollPane jsp = new JScrollPane(table);
        

JViewport jvp = new JViewport(); jvp.setView(new RowNumberHeader(table)); jsp.setRowHeader(jvp);

pane.add(jsp, BorderLayout.CENTER); addHeaderListener(); [...] } public void addHeaderListener(){ [...] } public static void main(String [] args){ [...] } }

Bây giờ chạy chương trình, ta sẽ tiêu đề của hàng hiện lên là số thứ tự của các hàng như hình dưới đây:

Tạo cột đóng băng
Ngoài việc hiển thị tiêu đề cho hàng, sẽ có lúc chúng ta còn muốn “đóng băng” một hay nhiều cột trong một bảng lại sao cho chúng vẫn ở nguyên vị trí kể cả khi chúng ta cuộn thanh cuộn ngang trong bảng. Trong ví dụ của chúng ta, chúng ta có thể đóng băng cột đầu tiên (cột First Name) sao cho nó luôn luôn có thể nhìn thấy khi mà chúng ta cuộn thanh trượt ngang của bảng. Các bước để làm cột đóng băng sẽ như sau:

  1. Tạo một JTable và chúng ta gọi nó là bảng chính. Dùng một JScrollPane để bọc JTable này lại. Bảng chính này có nhiệm vụ hiển thị những dữ liệu không bị đóng băng.
  2. Tạo một JTable thứ hai, chúng ta gọi nó là bảng tiêu đề và cũng thêm nó vào JScollPane. Bảng này sẽ sử dụng TableModel giống của bảng chính nhưng sẽ để hiển thị các cột bị đóng băng
  3. Tạo một TableColumnModel để sau này sẽ gán nó vào bảng tiêu đề
  4. Xóa các đối tượng của TableColum tương ứng với các cột bị đóng băng trong TableColumModel của bảng chính, và sau đó thêm chúng vào TableColumModel của bảng tiêu đề mà chúng ta đã tạo ở bước 3
  5. Gán TableColumModel chúng ta tạo ở bước 3 vào bảng tiêu đề sử dụng phương thức setColumnModel()
  6. JScrollPane chứa bảng tiêu đề bây giờ cũng chứa một JTableHeader của bảng tiêu đề đó ở trong column header viewport của nó. Lấy một tham chiếu đến JTableHeader này, và chuyển nó đến góc trên bên trái của JScrollPane cái có chứa bảng chính. Chúng ta có thể làm điều này sử dụng phương thức setCorner() của JScrollPane có chứa bảng chính.
  7. Đặt độ rộng ưa thích cho viewport của bảng tiêu đề sao cho nó chỉ đủ rộng để hiển thị các cột bị đóng băng. Độ rộng mặc định của nó là 450, và độ rộng này luôn lớn hơn độ rộng cần thiết.

Tóm lại, để đóng băng các cột trong một bảng, chúng ta chia bảng ra thành hai bảng con. Một bảng chính để hiển thị các cột không đóng băng. Bảng tiêu đề còn lại để hiển thị các cột đóng băng và đóng vai trò như một tiêu đề cho hàng trong cửa sổ cuộn có chứa bảng chính. Tiêu đề cột của bảng tiêu đề này được chuyển đến góc trên bên trái của cửa sổ cuộn có chứa bảng chính. Chúng ta tạo một lớp là FrozenColumnHeader trong file FrozenColumnHeader.java như sau:


public class FrozenColumnHeader extends JScrollPane{
    protected JTable mainTable;
    protected JTable headerTable;
    protected int columnCount;

    public FrozenColumnHeader(JTable table, int columns){
        super();
        mainTable = table;
        headerTable = new JTable(mainTable.getModel());
        getViewport().setView(headerTable);
        columnCount = columns;
    }

    @Override
    public void addNotify(){
        TableColumn column;
        super.addNotify();
        TableColumnModel mainModel = mainTable.getColumnModel();
        TableColumnModel headerModel = new DefaultTableColumnModel();
        int frozenWidth = 0;
        for(int i = 0; i<columnCount; i++){
            column = mainModel.getColumn(0);
            mainModel.removeColumn(column);
            headerModel.addColumn(column);
            frozenWidth += column.getPreferredWidth() + headerModel.getColumnMargin();
        }

        headerTable.setColumnModel(headerModel);
        Component columnHeader = getColumnHeader().getView();
        getColumnHeader().setView(null);
        JScrollPane mainScrollPane = 
            (JScrollPane)SwingUtilities.getAncestorOfClass(JScrollPane.class, mainTable);
        mainScrollPane.setCorner(JScrollPane.UPPER_LEFT_CORNER, columnHeader);
        headerTable.setPreferredScrollableViewportSize(new Dimension(frozenWidth, 0));
    }
}

Chúng ta có thể sử dụng lớp vừa tạo ở trên bằng cách tạo một đối tượng của nó và truyền vào contructor của nó một tham chiếu đến cái JTable là bảng chính của chúng ta, đồng thời một tham số là số lượng cột sẽ bị đóng băng (tính từ cột đầu tiên trong bảng chính). Trong ví dụ, ta có thể sửa lớp SimpleTableTest thành như sau:


public class SimpleTableTest extends JFrame{
    protected JTable table;

    public SimpleTableTest(){
        [...]
        JScrollPane jsp = new JScrollPane(table);
        JViewport jvp = new JViewport();
        

jvp.setView(new FrozenColumnHeader(table, 1)); table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);

jsp.setRowHeader(jvp); pane.add(jsp, BorderLayout.CENTER); addHeaderListener(); [...] } public void addHeaderListener(){ [...] } public static void main(String [] args){ [...] } }

Bây giờ chạy chương trình chúng ta sẽ thấy rằng khi chúng ta cuộn thanh trượt ngang thì cột First Name bị đóng băng sẽ vẫn giữ nguyên vị trí của nó như hình dưới đây:

Mặc dù ví dụ trên chỉ minh họa làm thế nào để đóng băng một cột, nhưng chúng ta hoàn toàn có thể sử dụng kĩ thuật trên để đóng băng nhiều cột. Với các tiếp cận đó, chúng ta cũng có thể đóng băng các dòng dữ liệu trong một bảng bằng cách tạo ra một bảng có chứa các dòng cần đóng băng rồi thêm nó vào column header viewport của JScrollPane.

Trong phần 8, chúng ta sẽ tìm hiểu cách để sắp xếp các dòng trong một bảng theo chiều tăng dần hoặc giảm dần của các giá trị trong một cột nào đó của bảng.

Nguồn: Brett Spell – Pro Java Programming, Second Edition

Chia sẻ bài viết

Thích bài này:

Thích

Đang tải…