Swing : JScrollPane doesn't refresh after changes in the structure of a JTable

匆匆过客 提交于 2019-12-01 00:04:05

Here is a test program that shows that when a JTable is held by a JScrollPane and the DefaultTableModel's DataVector is changed via setDataVector(...) it is not necessary to call fireTableDataChanged() on the model for the JTable's data to change correctly and be viewed correctly.

The program GUI looks like the images below.
Before Data Changed:

After Data Changed:

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;

import javax.swing.*;
import javax.swing.table.DefaultTableModel;

@SuppressWarnings("serial")
public class ScrollPaneRefresh extends JPanel {
   private static final int PREF_W = 600;
   private static final int PREF_H = 200;
   private Integer[][] initialData = {
         {1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 0},
         {1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 0},
         {1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 0}
         };
   private Integer[][] newData = {
         {1, 2}, {3, 4}
         };
   private String[] columnNames = {"Col1", "Col2"};
   private TablePanel gregsPanel = new TablePanel("With fireTableDataChanged", initialData, columnNames);
   private TablePanel myPanel = new TablePanel("Without fireTableDataChanged", initialData, columnNames);

   public ScrollPaneRefresh() {
      gregsPanel.setButtonAction(new AbstractAction("Change Table Data") {
         private boolean changeToNewData = true;

         @Override
         public void actionPerformed(ActionEvent evt) {
            if (changeToNewData) {
               gregsPanel.setTableModelDataVector(newData, columnNames);
            } else {
               gregsPanel.setTableModelDataVector(initialData, columnNames);
            }
            gregsPanel.fireTableDataChanged();
            changeToNewData = !changeToNewData;
         }
      });
      myPanel.setButtonAction(new AbstractAction("Change Table Data") {
         private boolean changeToNewData = true;

         @Override
         public void actionPerformed(ActionEvent evt) {
            if (changeToNewData) {
               myPanel.setTableModelDataVector(newData, columnNames);
            } else {
               myPanel.setTableModelDataVector(initialData, columnNames);
            }
            // myPanel.getScrollPane().getViewport().revalidate();
            changeToNewData = !changeToNewData;
         }
      });

      setLayout(new GridLayout(1, 0));
      add(gregsPanel.getMainPanel());
      add(myPanel.getMainPanel());
   }

   @Override // so scrollbars will show
   public Dimension getPreferredSize() {
      return new Dimension(PREF_W, PREF_H);
   }

   private static void createAndShowGui() {
      ScrollPaneRefresh mainPanel = new ScrollPaneRefresh();

      JFrame frame = new JFrame("ScrollPaneRefresh");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

class TablePanel {
   private JPanel mainPanel = new JPanel();
   private DefaultTableModel dm;
   private JTable table = new JTable();
   private JButton changeTableBtn = new JButton();
   private JScrollPane scrollpane = new JScrollPane(table);

   public TablePanel(String title, Object[][] data, Object[] columnNames) {
      dm = new DefaultTableModel(data, columnNames);
      table.setModel(dm);
      JPanel btnPanel = new JPanel();
      btnPanel.add(changeTableBtn);

      mainPanel.setBorder(BorderFactory.createTitledBorder(title));
      mainPanel.setLayout(new BorderLayout(5, 5));
      mainPanel.add(scrollpane, BorderLayout.CENTER);
      mainPanel.add(btnPanel, BorderLayout.PAGE_END);
   }

   public void setButtonAction(Action action) {
      changeTableBtn.setAction(action);
   }

   public void setTableModelDataVector(Object[][] data, Object[] columnNames) {
      dm.setDataVector(data, columnNames);
   }

   public void fireTableDataChanged() {
      dm.fireTableDataChanged();
   }

   public JScrollPane getScrollPane() {
      return scrollpane;
   }

   public JComponent getMainPanel() {
      return mainPanel;
   }
}

The deficiency of my example above is that it does not reproduce the original poster's problem, and I think that is due to us use of SpringLayout, which he does not fully describe or show code:

I have a JTable, associated with a DefaultTableModel, in a JPanel with a SpringLayout which is in a JScrollPane.

For more and better help, the OP is going to have to create his own sscce similar (or simpler) than what I've posted above. Either that, or solve his problem by not using SpringLayout to hold the JTable but rather either add it directly to the JScrollPane's viewport view or add it to a BorderLayout using JPanel (taking care to also add the JTable's header) and add that to the JScrollPane's viewport view.

I've run into this problem before.

Try invalidating the JTable and repainting the JScrollPane.

table.invalidate();
scrollPane.repaint();

Try invoking revalidate() method on table. According to specification of JScrollPanel http://docs.oracle.com/javase/tutorial/uiswing/components/scrollpane.html#update - "Dynamically changing client size" this method should notify Scroll Panel.

table.revalidate();
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!