Google Tag Manager

2008/03/26

JTable pagination using RowFilter

Code

public class MainPanel extends JPanel {
  private static final Color evenColor = new Color(240, 255, 250);
  private static final LinkViewRadioButtonUI ui = new LinkViewRadioButtonUI();
  private static int LR_PAGE_SIZE = 5;

  private final TestModel model = new TestModel();
  private final TableRowSorter< TestModel > sorter = new TableRowSorter< TestModel >(model);
  private final Box box = Box.createHorizontalBox();
  public MainPanel() {
    super(new BorderLayout());
    JTable table = new JTable(model) {
      @Override
      public Component prepareRenderer(TableCellRenderer tcr, int row, int column) {
        Component c = super.prepareRenderer(tcr, row, column);
        if(isRowSelected(row)) {
          c.setForeground(getSelectionForeground());
          c.setBackground(getSelectionBackground());
        }else{
          c.setForeground(getForeground());
          c.setBackground((row%2==0)?evenColor:getBackground());
        }
        return c;
      }
    };
    table.setFillsViewportHeight(true);
    table.setIntercellSpacing(new Dimension());
    table.setShowHorizontalLines(false);
    table.setShowVerticalLines(false);
    table.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
    table.setRowSorter(sorter);

    for(int i=0;i <= 2008;i++) {
      model.addTest(new Test(" Test: "+i, (i%2==0)?"":"comment..."));
    }
    initLinkBox(100, 1);

    add(box, BorderLayout.NORTH);
    add(new JScrollPane(table));
    setPreferredSize(new Dimension(320, 240));
  }
  private void initLinkBox(final int itemsPerPage, final int currentPageIndex) {
    //assert currentPageIndex > 0;
    sorter.setRowFilter(makeRowFilter(itemsPerPage, currentPageIndex-1));

    ArrayList< JRadioButton > l = new ArrayList< JRadioButton >();

    int startPageIndex = currentPageIndex-LR_PAGE_SIZE;
    if(startPageIndex <= 0) startPageIndex = 1;

//#if 0
    //int maxPageIndex = (model.getRowCount()/itemsPerPage)+1;
//#else
    /* "maxPageIndex" gives one blank page if the module of the division is not zero.
     *   pointed out by erServi
     * e.g. rowCount=100, maxPageIndex=100
     */
    int rowCount = model.getRowCount();
    int maxPageIndex = (rowCount/itemsPerPage) + (rowCount%itemsPerPage==0?0:1);
//#endif
    int endPageIndex = currentPageIndex+LR_PAGE_SIZE-1;
    if(endPageIndex>maxPageIndex) endPageIndex = maxPageIndex;

    if(currentPageIndex > 1)
        l.add(makePNRadioButton(itemsPerPage, currentPageIndex-1, "Prev"));
    //for(int i=startPageIndex;i <= endPageIndex;i++) 
    //    l.add(makeRadioButton(itemsPerPage, currentPageIndex, i-1));
    //if I only have one page, Y don't want to see pagination buttons
    //suggested by erServi
    if(startPageIndex < endPageIndex) {
      for(int i=startPageIndex; i<= endPageIndex;i++) {
          l.add(makeRadioButton(itemsPerPage, currentPageIndex, i-1));
      }
    }
    if(currentPageIndex < maxPageIndex)
        l.add(makePNRadioButton(itemsPerPage, currentPageIndex+1, "Next"));

    box.removeAll();
    ButtonGroup bg = new ButtonGroup();
    box.add(Box.createHorizontalGlue());
    for(JRadioButton r:l) {
      box.add(r); bg.add(r);
    }
    box.add(Box.createHorizontalGlue());
    box.revalidate();
    box.repaint();
    l.clear();
  }

  private JRadioButton makeRadioButton(final int itemsPerPage,
                                       final int current, final int target) {
    JRadioButton radio = new JRadioButton(""+(target+1));
    radio.setForeground(Color.BLUE);
    radio.setUI(ui);
    if(target+1==current) {
      radio.setSelected(true);
      radio.setForeground(Color.BLACK);
    }
    radio.addActionListener(new ActionListener() {
      @Override public void actionPerformed(ActionEvent e) {
        initLinkBox(itemsPerPage, target+1);
      }
    });
    return radio;
  }
  private JRadioButton makePNRadioButton(final int itemsPerPage, final int target, String title) {
    JRadioButton radio = new JRadioButton(title);
    radio.setForeground(Color.BLUE);
    radio.setUI(ui);
    radio.addActionListener(new ActionListener() {
      @Override public void actionPerformed(ActionEvent e) {
        initLinkBox(itemsPerPage, target);
      }
    });
    return radio;
  }

  private RowFilter< TestModel, Integer > makeRowFilter(final int itemsPerPage, final int target) {
    return new RowFilter< TestModel, Integer >() {
      @Override
      public boolean include(Entry< ? extends TestModel, ? extends Integer > entry) {
        int ei = entry.getIdentifier();
        return (target*itemsPerPage <= ei && ei < target*itemsPerPage+itemsPerPage);
      }
    };
  }

  public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {
      @Override public void run() {
        createAndShowGUI();
      }
    });
  }

  public static void createAndShowGUI() {
    try{
      UIManager.getInstalledLookAndFeels();
      UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
    }catch(Exception e) {
      e.printStackTrace();
    }
    final JFrame frame = new JFrame("@title@");
    frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    frame.getContentPane().add(new MainPanel());
    frame.pack();
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }
}

References

20 comments:

  1. Hi Atsuhiro,
    Very nice Work, thanks a lot.
    But i recognised an extreme CPU-usage when using your class on my system.

    There seems to be a circuit, which can be prevented by changing 3 lines of code in your paint-Method:
    - b.setForeground(Color.BLUE);
    - b.setForeground(Color.BLACK);
    + if (b.getForeground()!=Color.BLACK) b.setForeground(Color.BLACK);
    and the same for the gray color.

    Maybe not elegant but it helped

    Thanks again,
    Peter

    ReplyDelete
  2. Thanks for your help!
    This is clearly an infinite repaint loop bug.

    89c89,92
    < if(target+1==current) radio.setSelected(true);
    ---
    > if(target+1==current) {
    > radio.setSelected(true);
    > radio.setForeground(Color.BLACK);
    > }
    195c198
    < b.setForeground(Color.BLUE);
    ---
    > //b.setForeground(Color.BLUE);
    197c200
    < b.setForeground(Color.GRAY);
    ---
    > //b.setForeground(Color.GRAY);
    199c202
    < b.setForeground(Color.BLACK);
    ---
    > //b.setForeground(Color.BLACK);

    Thanks again for the correction.

    ReplyDelete
  3. This comment has been removed by the author.

    ReplyDelete
  4. LinkViewRadioButtonUI is giving compilation error pls help
    paramupk@gmail.com

    ReplyDelete
  5. Hi, ranjeeth.
    Hmm, what OS and Java version are you using?
    (Works fine for me on Windows XP using Java 1.6.0_22)

    ReplyDelete
  6. Hi,
    Nice One.... and thanks for Ur work great
    Can anyone explain the code. like Role of table model, Underline in radio-button and working flow

    In my case i will be getting an array-list of bean(i.e setters)
    So i need to iterate and copy to object[][].So if huge number of record is coming it will be a performance issue ???
    And i need to apply pagination so were i have to change ??

    Plz do help

    ReplyDelete
  7. I'm not sure of the problem, but how about to use a DefaultTableModel#addRow(Object[]) instead of "copy to object[][]".
    BTW, what has become of LinkViewRadioButtonUI compilation error?

    ReplyDelete
  8. I solved Everything.....
    But have a problem
    When user click on the header whole model should get sorted not the view??
    What to do ??
    For Example u have data from A - Z your current view contains a-c so sorting will become c -a but i need Z to come first....

    ReplyDelete
  9. One way is to do something like this: TablePaginationTest.java
    (But this looks like ugly hack!)

    ReplyDelete
  10. Very good code, thanks!
    BTW, there is one or two little improvements:

    1. "maxPageIndex" gives one blank page if the module of the division is not zero.

    So:

    int maxPageIndex = (getTableModel(false).getRowCount() % itemsPerPage) == 0 ? (getTableModel(false).getRowCount() / itemsPerPage) : ((getTableModel(false).getRowCount() / itemsPerPage) + 1);


    2. Also, if I only have one page, Y don't want to see pagination buttons, so:

    if(startPageIndex < endPageIndex)
    for(int i = startPageIndex; i <= endPageIndex; i ++) paginationButtons.add(makeRadioButton(itemsPerPage, currentPageIndex, i - 1));




    Thank you again!

    ReplyDelete
  11. Perfect! Thanks for the corrections, erServi.

    ReplyDelete
  12. Hi,
    I am newbie in Swing, I like this pagination in JTable, my question please how to use it with from a resultset from a jdbc connection from a database or using hibernate ?
    thanks, your help is appreciated.

    ReplyDelete
  13. Hi, Majid.
    Loading all data at once, or step by step, at any rate, you might be able to use a SwingWorker: TablePaginationLoadingTest.java
    private int currentPageIndex = 1;
    static class MyBean{
    private String name;
    private int age;
    public MyBean() {
    name = "empty";
    age = -1;
    }
    public MyBean(String name, int age) {
    this.name = name;
    this.age = age;
    }
    public String getName() {
    return name;
    }
    public int getAge() {
    return age;
    }
    }
    private final JButton button = new JButton(new AbstractAction("get") {
    @Override public void actionPerformed(ActionEvent e) {
    table.setEnabled(false);
    button.setEnabled(false);
    SwingWorker> worker = new SwingWorker>() {
    private int itemsPerPage = 100;
    private int max = 3000;
    @Override public String doInBackground() {
    int current = 0;
    // Hibernate: not tested
    // SessionFactory factory = new Configuration().configure().buildSessionFactory();
    // Session session = factory.openSession();
    // Criteria criteria = session.createCriteria(MyBean.class);
    // criteria.setMaxResults(itemsPerPage);
    while(current result = criteria.list();
    // make dummy list
    Thread.sleep(2000);
    java.util.List result = new java.util.ArrayList(itemsPerPage);
    for(int i=0;i> chunks) {
    for(java.util.List list : chunks) {
    for(MyBean mb: list) {
    model.addRow(new Object[] {mb.getName(), mb.getAge()});
    }
    }
    initLinkBox(itemsPerPage, currentPageIndex);
    }
    @Override public void done() {
    String text = null;
    if(isCancelled()) {
    text = "Cancelled";
    }else{
    try {
    text = get();
    }catch(Exception ex) {
    ex.printStackTrace();
    text = "Exception";
    }
    }
    table.setEnabled(true);
    button.setEnabled(true);
    }
    };
    worker.execute();
    }
    });
    //......

    ReplyDelete
  14. Hi..
    I am just learning swing and I like the pagination you created in table.Instead of using too much number of toggle buttons ,I need only 4 toggle buttons i.e. First Prev Next Last.Can you tell me how to do that in your code?.My emailid - vishalkadu87@gmail.com

    ReplyDelete
  15. Hi, Vishal.
    It's kind of hard to explain in English for me, so check out this example: https://gist.github.com/aterai/7261520

    ReplyDelete
  16. Really Great..
    This is what I wanted.. Thanks.
    Just only thing need to add is Page No ,Total pages and search according to page no(Example :: Page 1 out of 10 and when we type page no in Text Field it will redirect on that page).

    ReplyDelete
  17. Hi, Vishal.
    I’ve updated the gist code :)

    ReplyDelete
  18. Nice work.
    Thank you again.It helps me a lot

    ReplyDelete
  19. Hi, Atsuhiro
    Thanks for guiding me.Now I am facing critical problem in JTable and need your help.
    Problem::If there are 50,000 or more rows in the table then it takes too much time to load.So how can I solve this problem and how can I show instant data in table with the same code on github.

    ReplyDelete
  20. Hi, Vishal
    How about using the SwingWorker http://java-swing-tips.googlecode.com/svn/trunk/PageInputForPagination/src/java/example/MainPanel.java

    ReplyDelete