Bộ sưu tập

Sử dụng JTable của Swing trong java (phần 9 – phần cuối)


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

Trong phần này chúng ta sẽ tìm hiểu về cách thêm và xóa dữ liệu ở trong một bảng. Để thay đổi dữ liệu trong bảng, chúng ta cần thay đổi TableModel và sau đó thông báo cho listener của nó (hay nói cách khác chính là JTable) rằng dữ liệu đã bị thay đổi.

Đoạn mã dưới đây là ví dụ minh họa cho một bảng có 1 cột. Có một ô chữ để cho phép chúng ta thêm những dòng mới vào trong bảng.


public class RowAdder extends JFrame{
    protected SimpleModel tableData;
    protected JTable table;
    protected JTextField textField;

    public static void main(String[] args) {
        RowAdder ra = new RowAdder();
        ra.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        ra.setSize(400, 300);
        ra.setVisible(true);
    }

    public RowAdder(){
        Container pane = getContentPane();
        pane.setLayout(new BorderLayout());
        tableData = new SimpleModel();
        table = new JTable(tableData);
        table.getColumnModel().getColumn(0).setPreferredWidth(300);
        JScrollPane jsp = new JScrollPane(table);
        pane.add(jsp, BorderLayout.CENTER);
        textField = new JTextField();
        textField.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent event) {
                addLineToTable();
            }
        });
        pane.add(textField, BorderLayout.SOUTH);
    }

    protected void addLineToTable(){
        tableData.addText(textField.getText());
        textField.setText("");
    }

    class SimpleModel extends AbstractTableModel{
        protected Vector textData = new Vector();

        public void addText(String text){
            textData.addElement(text);
            fireTableDataChanged();
        }

        public int getRowCount(){
            return textData.size();
        }

        public int getColumnCount(){
            return 1;
        }

        public Object getValueAt(int row, int column){
            return textData.elementAt(row);
        }
    }
}

Khi chạy chương trình, chúng ta nhập chữ vào trong ô chữ sau đó nhấn Enter. Một dòng mới trong sẽ được thêm vào JTable như hình dưới đây:

Khi nhấn phím Enter vào ô chữ, một sự kiện sẽ được sinh ra và xử lý sự kiện đó sẽ gọi phương thức fireTableDataChanged() để cập nhật lại dữ liệu trong bảng. Tuy nhiên phương thức này chỉ được cài đặt trước trong lớp AbstractTableModel, còn nếu cái model mà không phải kế thừa từ lớp AbstractTableModel thì chúng ta sẽ không có phương thức này để dùng. Lúc đó sẽ phải tự tạo ra một phương thức trong model giống như là phương thức fireTableDataChanged() của lớp AbstractTableModel. Ví dụ dưới đây minh họa trong trường hợp model của chúng ta cài đặt interface TableModel thì sẽ làm như sau:


public class RowAdder extends JFrame{
    protected SimpleModel tableData;
    protected JTable table;
    protected JTextField textField;

    public static void main(String[] args) {
        RowAdder ra = new RowAdder();
        ra.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        ra.setSize(400, 300);
        ra.setVisible(true);
    }

    public RowAdder(){
        Container pane = getContentPane();
        pane.setLayout(new BorderLayout());
        tableData = new SimpleModel();
        table = new JTable(tableData);
        table.getColumnModel().getColumn(0).setPreferredWidth(300);
        JScrollPane jsp = new JScrollPane(table);
        pane.add(jsp, BorderLayout.CENTER);
        textField = new JTextField();
        textField.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent event) {
                addLineToTable();
            }
        });
        pane.add(textField, BorderLayout.SOUTH);
    }

    protected void addLineToTable(){
        tableData.addText(textField.getText());
        textField.setText("");
    }

    class SimpleModel implements TableModel{
        protected Vector textData = new Vector();
        protected EventListenerList listenerList = new EventListenerList();
       
        public void addText(String text){
            textData.addElement(text);
            notifyListenersOfDataChange();

        }

        public void notifyListenersOfDataChange(){
            TableModelEvent event= new TableModelEvent(this);
            Object[] listeners = listenerList.getListenerList();
            for(int i = 0; i< listeners.length; i++){
                if(listeners[i] == TableModelListener.class){
                    TableModelListener listener = (TableModelListener) (listeners[i+1]);
                    listener.tableChanged(event);
                }
            }
        }

        public int getRowCount(){
            return textData.size();
        }

        public int getColumnCount(){
            return 1;
        }

        public Object getValueAt(int row, int column){
            return textData.elementAt(row);
        }

        public String getColumnName(int columnIndex) {
            return "A";
        }

        public Class getColumnClass(int columnIndex) {
            return String.class;
        }

        public boolean isCellEditable(int rowIndex, int columnIndex) {
            return false;
        }

        public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
            textData.setElementAt(aValue, rowIndex);
        }

        public void addTableModelListener(TableModelListener l) {
            listenerList.add(TableModelListener.class, l);
        }

        public void removeTableModelListener(TableModelListener l) {
            listenerList.remove(TableModelListener.class, l);
        }
    }
}

Trong đoạn mã trên, chúng ta tạo ra phương thức notifyListenersOfDataChange() và phương thức này tương đương với phương thức fireTableDataChanged() khi dùng AbstractTableModel. Bản chất vẫn là gọi phương thức tableChanged() của các listener và truyền vào đó một đối tượng của TableModelEvent. Các listener sẽ được đăng kí với một model qua phương thức addTableModelListener() của model đó. Listener trong trường hợp này chính là cái JTable đang tham chiếu đến cái model SimpleModel.

Hiển thị một dòng trong bảng
Trong ví dụ bên trên, khi mà chúng ta nhập chữ vào ô chữ rồi ấn phím Enter, một dòng mới sẽ được thêm vào bảng. Khi một vài dòng đầu được thêm vào, chúng ta sẽ nhìn thấy chúng ngay lập tức. Tuy nhiên, khi số dòng nhiều vượt quá kích thước thì sẽ dẫn đến sự xuất hiện của thanh cuộn dọc. Và đến đây, vấn đề chính là không thể nhìn thấy được dòng vừa được thêm vào bảng trừ khi chúng ta phải cuộn thanh cuộn xuống dưới. Hình dưới đây minh họa cho việc khi mà nhập quá 14 dòng vào bảng, nếu không cuộn thanh cuộn xuống dưới, thì từ dòng 15 trở xuống, chúng ta sẽ không nhìn thấy được.

Như vậy, chúng ta phải tìm cách nào đó để cho thanh cuộn tự cuộn xuống khi mà một dòng mới được thêm vào bảng. Có làm điều này bằng cách lấy ra đối tượng JViewport của cửa sổ cuộn và sau đó đặt lại vị trí cho nó sao cho dòng cuối cùng của bảng luôn được hiện lên trong cửa sổ cuộn. Chúng ta sửa lớp RowAdder như sau:


public class RowAdder extends JFrame{
    protected SimpleModel tableData;
    protected JTable table;
    protected JTextField textField;

    public static void main(String[] args) {
        RowAdder ra = new RowAdder();
        ra.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        ra.setSize(400, 300);
        ra.setVisible(true);
    }

    public RowAdder(){
        Container pane = getContentPane();
        pane.setLayout(new BorderLayout());
        tableData = new SimpleModel();
        table = new JTable(tableData);
        table.getColumnModel().getColumn(0).setPreferredWidth(300);
        table.addComponentListener(new TableScroller());
        JScrollPane jsp = new JScrollPane(table);
        pane.add(jsp, BorderLayout.CENTER);
        textField = new JTextField();
        textField.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent event) {
                addLineToTable();
            }
        });
        pane.add(textField, BorderLayout.SOUTH);
    }

    protected void addLineToTable(){
        tableData.addText(textField.getText());
        textField.setText("");
    }

    class SimpleModel implements TableModel{
        [...]
    }

    class TableScroller extends ComponentAdapter{
        public void componentResized(ComponentEvent event){
            int lastRow = tableData.getRowCount() -1;
            int cellTop = table.getCellRect(lastRow, 0, true).y;
            JScrollPane jsp  = (JScrollPane) SwingUtilities.getAncestorOfClass(JScrollPane.class, table);
            JViewport jvp = jsp.getViewport();
            int portHeight = jvp.getSize().height;
            int position = cellTop - (portHeight - table.getRowHeight() - table.getRowMargin());
            if(position >= 0){
                jvp.setViewPosition(new Point(0, position));
            }
        }
    }
}

Phương thức componentResized() lấy về kích thước và tọa độ của một dòng trong bảng bằng việc gọi phương thức getCellRect(). Sau đó nó sử dụng vị trí thẳng đứng của dòng, kích thước của viewport, và chiều cao của dòng để điều chỉnh vị trí nhìn của viewport sao cho chúng ta có thể luôn nhìn thấy được dòng cuối cùng của bảng. Sau khi chạy chương trình, bây giờ chúng ta thêm dòng đến đâu, thanh cuộn sẽ tự động cuộn xuống tới đó như hình dưới đây:

Như vậy, trong phần này chúng ta đã biết cách để thêm một dòng mới vào bảng như thế nào. Thêm vào đó, chúng ta cũng biết cách để hiển thị một dòng tại đúng vị trí mà chúng ta mong muốn. Loạt bài “Cách sử dụng JTable của Swing trong java” sẽ dừng ở đây với hi vọng cung cấp cho bạn đọc những kiển thức cơ bản về cách sử dụng JTable – một thành phần rất hay được sử dụng trong các ứng dụng viết bằng java.

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

Advertisements

3 comments on “Sử dụng JTable của Swing trong java (phần 9 – phần cuối)

  1. Chao Ban , cam on ban da viet mot bai rat chi tiet ve phan jtable , sau khi doc xong nhung bai cua ban , minh co mot so thac mac muon hoi truc tiep ban thong qua email . Neu ban nhan dc tin nhan nay cua minh mong ban co the gui email qua cho minh qua dia chi hoangvm1988@gmail.com . Rat mong nhan mail cua ban . Minh co mot project dang phai thuc hien va can mot so tu van huu ich cua ban . Cam on ban nhieu

    Số lượt thích

Trả lời

Mời bạn điền thông tin vào ô dưới đây hoặc kích vào một biểu tượng để đăng nhập:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Đăng xuất /  Thay đổi )

Google photo

Bạn đang bình luận bằng tài khoản Google Đăng xuất /  Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Đăng xuất /  Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Đăng xuất /  Thay đổi )

Connecting to %s