{Kurung Kurawal}

Swing JTable, Lazy Loading

Bekerja dengan Swing, apalagi jika berurusan dengan tampilan data, pasti pernah menggunakan Jtable bukan? Ada relasi yang kemarin mengalami situasi, di mana data yang harus ditampilkan ternyata sangat besar, mencapai 17 ribu buah, dan katanya, itu akan terus bertambah.

Sebenarnya sih, data itu sih tidak terlalu banyak untuk kekuatan komputer modern saat ini. Tidak akan membuat aplikasinya hang kok. Tapi, dengan data sebanyak itu, akan menimbulkan delay yang lumayan mengganggu ketika tabel data tersebut ditampilkan.

Solusinya? Upgrade ke Super Computer aja.
Hehehe, gak segitunya juga kali ya.

Setelah diperhatikan, sebenarnya tampilan data itu sama sekali tidak ada gunanya. Artinya, jarang sekali user benar benar memperhatikan dengan detail 17 ribu baris data tersebut, bahkan, kebanyakan cuma ditampilkan sebentar, puter puter mouse, pindah ke halaman lain. Atau melakukan pencarian item tertentu. Jadi, pertanyaannya, untuk apa data sebanyak itu ditampilkan sekaligus? Tapi beliau ngotot, atasan di kantor maunya begitu, mereka maunya bisa dilihat semua, meskipun pake scroll.

Okelah, kalo udah bawa bos, udah susahlah.

Ada satu teknik pengambilan data, yang dilakukan ketika dibutuhkan. Jadi data itu hanya ditampilkan ketika memang perlu ditampilkan. Dalam kasus ini, diperhatikan bahwa baris yang muncul di layar komputer sangat terbatas, anggaplah misalnya 100 baris sekali tampil, maksimum. Tentu saja, beda resolusi komputer akan beda jumlahnya. Dan ada scrollbar bukan ?

Bagaimana jika menampilkan hanya 100 baris, namun ketika user scroll mendekati akhir dari jumlah baris itu, kita ambil lagi 100 baris berikut. User scroll lagi, mendekati akhir lagi, kita ambil lagi. Dan seterusnya, hingga seluruh data terambil. ini namanya Lazy Loading. Akan jauh lebih cepat memproses 100 data dibandingkan 17 ribu, bukan ?

Berikut saya berikan contohnya, tentu saja, data di contoh ini adalah auto-generated, jika Anda memahami kode sumber di bawah, Anda akan mudah mengubah sumber datanya, misalnya diambil dari database.

File Jar yang dapat dijalankan bisa didownload disini. Mungkin bisa digunakan untuk menguji roda mouse ?

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
package whatever;
 
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.table.DefaultTableModel;
import java.awt.*;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
 
/**
 * Created by konglie on 3/14/16.
 */
public class LazyLoad extends JFrame {
    public static void main(String[] args){
        new LazyLoad();
    }
 
    int currentPage = 0, loadPerPage = 50;
    boolean isLoading = false;
 
    JPanel body, headerPanel;
    JTable theTable;
    DefaultTableModel dtm;
    JScrollPane scrollPane;
    JTextField inputLimit;
    public LazyLoad(){
        setTitle("JTable Lazy Loading");
        setResizable(false);
        setPreferredSize(new Dimension(640, 480));
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 
        body = new JPanel(new BorderLayout());
        inputLimit = new JTextField("1000000");
        inputLimit.addFocusListener(new FocusAdapter() {
            @Override
            public void focusLost(FocusEvent e) {
                dtm.setRowCount(0);
                currentPage = 0;
                loadData();
            }
        });
 
        headerPanel = new JPanel(new BorderLayout());
        headerPanel.setBorder(BorderFactory.createEmptyBorder(5, 15, 5, 15));
        headerPanel.add(new JLabel("Limit Data"), BorderLayout.WEST);
        headerPanel.add(inputLimit, BorderLayout.CENTER);
        body.add(headerPanel, BorderLayout.NORTH);
 
        dtm = new DefaultTableModel(new Object[][]{}, new String[]{"Kolom 1", "Kolom 2", "Kolom 3"}){
            @Override
            public boolean isCellEditable(int r, int c){
                return false;
            }
        };
        theTable = new JTable(dtm);
 
        scrollPane = new JScrollPane(theTable);
        body.add(scrollPane, BorderLayout.CENTER);
 
        getContentPane().add(body, BorderLayout.CENTER);
        pack();
        setLocationRelativeTo(null);
        setVisible(true);
 
        loadData();
 
        setupLazyLoad();
    }
 
    void loadData(){
 
        if(isLoading){
            return;
        }
 
 
        int limit = 0;
        try{
            limit = Integer.parseInt(inputLimit.getText());
            if(theTable.getRowCount() >= limit){
                return;
            }
        } catch (Exception e){
 
        }
 
        isLoading = true;
 
        int p = (int) Math.ceil(Math.log10(limit));
        if(p < 1){
            p = 1;
        }
 
        for(int i = (currentPage++ * loadPerPage); i < (loadPerPage * currentPage); i++){
            dtm.addRow(new Object[]{
                    String.format("Baris %0"+p+"d, kolom 1", i),
                    String.format("Baris %0"+p+"d, kolom 2", i),
                    String.format("Baris %0"+p+"d, kolom 3", i)
            });
        }
        isLoading = false;
    }
 
    void setupLazyLoad(){
        final JScrollBar scrollBar = scrollPane.getVerticalScrollBar();
 
        // tinggi setiap baris
        final int rh = theTable.getRowHeight();
 
        scrollBar.getModel().addChangeListener(new ChangeListener() {
 
            @Override
            public void stateChanged(ChangeEvent event) {
                BoundedRangeModel model = (BoundedRangeModel) event.getSource();
                int extent = model.getExtent();
                int maximum = model.getMaximum();
                int value = model.getValue() + extent;
                int diff = maximum - value;
 
                // jumlah baris tersisa sebelum kita mengambil data baru
                int rowBeforeLoad = 15;
 
                if(Math.floor(diff - value) <= (rowBeforeLoad * rh)){
                    SwingUtilities.invokeLater(new Runnable() {
                        @Override
                        public void run() {
                            loadData();
                        }
                    });
                }
            }
        });
    }
 
 
}

Kuncinya adalah, seperti pada teknik paging, mengambil data pada offset tertentu. Misalnya, kita tentukan sekali pengambilan data adalah 10 buah, maka pengambilan pertama adalah 1 – 10, pengambilan kedua adalah 11 – 20, dan seterusnya.

Demikian, semoga berguna untuk kita semua.