Showing Old Value in JTable in swing - java

In this code block I'm getting old values after clicking inside scroll pane, however new values also disappear at first on fetching but they slowly disappear and old values appear in that place.
I tried a few combinations but was't able to achieve the results as expected. Also during variable declaration I made all the variables as non-static except the frame which I suppose is not relevant and recommended and I'm also embedding the components on frame itself.
//THE TABLE
table = new JTable();
//THE COLUMN
String[] columns = new String[] {
"Serial No", "BundleId", "Bundle Count", "Record Count","Status","Date"
};
//THE ROW
Object[][] data = dc.Success(date,afterDate);
final Class[] columnClass = new Class[] {
Boolean.class,String.class, String.class, String.class,String.class,String.class };
//create table model with data
DefaultTableModel model = new DefaultTableModel(data, columns) {
#Override
public boolean isCellEditable(int row, int column)
{
return false;
}
#Override
public Class<?> getColumnClass(int columnIndex)
{
return columnClass[columnIndex];
}
public Object getCellEditorValue() {
return "";
}
};
// JTable table = new JTable(model);
table.setModel(model);
//add the table to the frame
//ADD SCROLLPANE
scroll.setBounds(70, 80, 600, 400);
scroll.setViewportView(table);
frame.getContentPane().add(scroll);
// this.add(new JScrollPane(table));
// table.setAutoCreateRowSorter(true);
table.setPreferredScrollableViewportSize(new Dimension(320, 160));
TableColumn tc = table.getColumnModel().getColumn(BOOLEAN_COL);
tc.setHeaderRenderer(new SelectAllHeader(table, BOOLEAN_COL));
this.setTitle("Table Example");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.pack();
this.setVisible(true);
table.repaint();

If you want to notify your JTable about changes of your data, use something similar to tableModel.fireTableDataChanged().
There are other methods that can be used for updating specific ranges of data. tableModel.fireTableRowsUpdated​(firstRow, lastRow) might be of particular use to you. This is because you suggest you only update a single row when that row is clicked on. If that is not the case, stick to tableModel.fireTableDataChanged().
From your comments, I notice you are trying to fire a data change event and then changing your whole model anyway. This is not required and could actually be the source of your issues if your model is holding the old values.
Instead you should change the DataVector of the current model. To change the data on a single row you could use something like the following extract of code. There is a full example below the extract.
//Get your row's values from your database
//I'll use random values for this purpose
int col1Val = getRand(0, 9);
int col2Val = getRand(0, 9);
//Put the values in an object vector
Vector<Object> rowVals = new Vector<>();
rowVals.addElement(col1Val);
rowVals.addElement(col2Val);
//Set the object vector
model.getDataVector().setElementAt(rowVals, table.getSelectedRow());
//Notify the table the row has changed
model.fireTableRowsUpdated(table.getSelectedRow(), table.getSelectedRow());
Full example
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Vector;
import java.util.concurrent.ThreadLocalRandom;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
public class JTableExample extends JFrame {
private static final String windowName = "JTable Example";
private static final long serialVersionUID = 362702020844358278L;
private JPanel tablePanel;
private JScrollPane scrollPanel;
private JTable table;
private DefaultTableModel model;
private JTableExample() {
super(windowName);
SetUp();
}
private void SetUp() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
SetUpJTable();
SetUpJPanel();
add(tablePanel);
pack();
setVisible(true);
}
private void SetUpJPanel() {
tablePanel = new JPanel(new GridLayout());
tablePanel.add(scrollPanel);
}
private void SetUpJTable() {
String[] columns = new String[] {
"Something", "Something Else"
};
String[][] data = new String[][] {
{"1", "2"},
{"3", "3"}
};
model = new DefaultTableModel(data, columns) {
private static final long serialVersionUID = -3895234084030399437L;
#Override
public boolean isCellEditable(int row, int column)
{
return false;
}
};
table = new JTable(model);
table.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
//Get your row's values from your database
//I'll use random values for this purpose
int col1Val = getRand(0, 9);
int col2Val = getRand(0, 9);
//Put the values in an object vector
Vector<Object> rowVals = new Vector<>();
rowVals.addElement(col1Val);
rowVals.addElement(col2Val);
//Set the object vector
model.getDataVector().setElementAt(rowVals, table.getSelectedRow());
//Notify the table the row has changed
model.fireTableRowsUpdated(table.getSelectedRow(), table.getSelectedRow());
}
});
scrollPanel = new JScrollPane(table);
scrollPanel.setPreferredSize(new Dimension(500, 500));
}
private static int getRand(int min, int max) {
return ThreadLocalRandom.current().nextInt(min, max + 1);
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> new JTableExample());
}
}

Related

Adding JComboBox to JTable

I'm creating a JTable in Java & I'm asked to add to the table a JCheckBox, JButton and a JComboBox. The table that I created is displaying all the information, the button is working fine and the JCheckBox is also working, the problem I'm facing is that the JComboBox is not working. I really can't figure out why. I've tried to look the problem up but I can't figure it out. Can someone help me please?
The code:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;
public class sinX extends JFrame {
private JTable table;
private DefaultTableModel model;
private Object[][] data;
private String[] columnNames;
private JButton button;
JComboBox comboBox;
public sinX() {
comboBox = new JComboBox();
setTitle("Programming Languages");
data = new Object[][]{{"C","Dennis Ritchie",1972,false},{"C++","Bjarne Stroustrup",1983,true},
{"Python","Guido van Rossum",1991,false},{"Java","James Gosling",1995,true},{
"JavaScript","Brendan Eich",1995,true},{"C#","Anders Hejlsberg",2001,false},
{"Scala","Martin Odersky",2003,true}};
columnNames = new String[] {"Language","Author","Year","Check Box"};
//model = new DefaultTableModel(data, columnNames);
//table = new JTable(model);
//table.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
final Class[] columnClass = new Class[] {
String.class, String.class, Integer.class, Boolean.class
};
//create table model with data
DefaultTableModel model = new DefaultTableModel(data, columnNames) {
#Override
public boolean isCellEditable(int row, int column)
{
return false;
}
#Override
public Class<?> getColumnClass(int columnIndex)
{
return columnClass[columnIndex];
}
};
table = new JTable(model);
table.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
button = new JButton("Remove");
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
// check for selected row first
if(table.getSelectedRow() != -1) {
// remove selected row from the model
model.removeRow(table.getSelectedRow());
JOptionPane.showMessageDialog(null, "Selected row deleted successfully");
}
}
});
TableColumn year = table.getColumnModel().getColumn(2);
comboBox.addItem("A");
comboBox.addItem("B");
comboBox.addItem("C");
comboBox.addItem("D");
year.setCellEditor(new DefaultCellEditor(comboBox));
add(new JScrollPane(table), BorderLayout.CENTER);
add(button, BorderLayout.SOUTH);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(600, 500);
setLocationRelativeTo(null);
setVisible(true);
}
public static void main(String args[]) {
new sinX();
}
}
the problem I'm facing is that the JComboBox is not working.
year.setCellEditor(new DefaultCellEditor(comboBox));
The above code indicates you are trying to use a combo box as an editor for the given column.
public boolean isCellEditable(int row, int column)
{
return false;
}
However, you have stated in your model that none of columns are editable.
You need to return true for any column where you want to edit the data.
the JCheckBox is also working
Not really. Yes you see the boolean value is rendered as a check box. However, you can't change its value by clicking on it, unless of course you return true for that column as well.

Why does setPreferredSize() sometimes take precedent over setMinimumSize()?

I had an error yesterday where I added a JTable and a JPanel (with a JButton in it) to a JScrollPane. The JButton was fixed to the bottom of the table, and it added a row to the JTable when clicked.
The problem was if the table ever got bigger than the JScrollPane, it would only allow you to scroll to the bottom of the JTable; you couldn't get to the JButton anymore. Today, I made an MCVE to try and get help, but first I monkeyed with it a bit more and ended up fixing my problem, but in a way that left me with more questions than answers... Here's the MCVE I had prepared:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
public class MCVE extends JFrame{
private JButton addRow;
private MCVEModel tableModel;
private JTable table;
private JScrollPane pane;
private JPanel scrollPanel, panel;
public static void main (String[] args) {
new MCVE();
}
public MCVE() {
initialize();
}
public void initialize () {
this.setTitle("Halp");
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setBounds(50, 50, 500, 300);
this.setResizable(false);
JPanel mainPanel = new JPanel(new GridBagLayout());
/** The JPanel everything is put into **/
scrollPanel = new JPanel();
scrollPanel.setLayout(new BoxLayout(scrollPanel, BoxLayout.Y_AXIS));
/** The JScrollPane we're using **/
pane = new JScrollPane(scrollPanel);
pane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
pane.getVerticalScrollBar().setUnitIncrement(10);
/** The button which keeps getting cut off.... **/
addRow = new JButton("...");
addRow.setBackground(Color.WHITE);
addRow.setMnemonic('R');
addRow.setFocusable(false);
addRow.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
addNewRow();
}
});
/** I wrap the button into this panel so I can affix it to the left **/
panel = new JPanel();
panel.setMinimumSize(new Dimension(0, 20));
panel.setLayout(null);
addRow.setBounds(0, 0, 35, 15);
panel.add(addRow);
/** Faking some data to get the table to populate **/
ArrayList<List<String>> allData = new ArrayList<List<String>>();
ArrayList<String> fakeData = new ArrayList<String>();
fakeData.addAll(Arrays.asList(
new String[]{"this", "is", "just", "sample", "data"}));
for(int i = 0; i < 5; i++)
allData.add(fakeData);
List<String> columnNames = Arrays.asList(new String[] {"", "", "", "", ""});
tableModel = new MCVEModel(columnNames, allData);
table = new JTable();
table.setModel(tableModel);
/** Adding it all together **/
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.BOTH;
c.weightx = 1;
c.weighty = 1;
scrollPanel.add(table);
scrollPanel.add(panel);
mainPanel.add(pane, c);
this.add(mainPanel);
this.setVisible(true);
}
public void addNewRow () {
tableModel.addRow(tableModel.getRowCount(),
new String[]{"true", "", "", "false", "false"});
tableModel.fireTableRowsInserted(
tableModel.getRowCount(), tableModel.getRowCount());
}
}
/**
* Just here to keep things compilable. Seriously cut back for the MCVE,
* but still replicates the problem without any errors.
* Nothing below here should be relevant to the issue.
*/
class MCVEModel extends DefaultTableModel {
private static final long serialVersionUID = -6598574844380686148L;
private List<String> columnNames;
private List<List<String>> values;
public MCVEModel (List<String> columnNames, List<List<String>> strings) {
this.columnNames = columnNames;
this.values = strings;
}
public int getColumnCount() {
return columnNames.size();
}
public int getRowCount() {
return values == null || values.size() == 0 ? 0 : values.get(0).size();
}
public String getColumnName(int col) {
return columnNames.get(col);
}
public Object getValueAt(int row, int col) {
return values.get(col).get(row);
}
#SuppressWarnings({ "unchecked", "rawtypes" })
public Class getColumnClass(int c) {
return String.class;
}
public boolean isCellEditable(int row, int col) {
return true;
}
public void setValueAt(Object value, int row, int col) {
values.get(col).set(row, (String) value);
fireTableCellUpdated(row, col);
}
public void removeRow(int row) {
for(int i = 0; i < values.size(); i++)
values.get(i).remove(row);
this.fireTableRowsDeleted(row, row);
}
public void addRow(int row, String[] strings) {
for(int i = 0; i < values.size(); i++)
values.get(i).add(row, strings[i]);
fireTableRowsInserted(row, row);
}
}
The problem is with this line:
panel.setMinimumSize(new Dimension(0, 20));
More precisely, it's with the word "Minimum". By changing this to:
panel.setPreferredSize(new Dimension(0, 20));
I got exactly the functionality I needed. Now when the table gets too large for the JScrollPanel, we're still able to scroll down and see the JButton; it's no longer cut off.
I'm assuming this means the JPanel's parent didn't honor its minimum dimensions, but that it did honor its preferred dimensions. Why is this? I had thought setPreferredSize(), setMinimumSize(), and setMaximumSize() interacted like, "I'd prefer to be this big, but no matter what I can't be smaller than my minimum or larger than my maximum," but it seems this isn't the case. I know none of these methods should be used too frequently, but when should I use setMinimumSize() over setPreferredSize() or vice versa?
The magic is this line:
scrollPanel.add(panel);
So, scrollPanel will contain this panel. Then, JScrollPane honors the preferredSize-s. Which makes sense, since its purpose is, by using the scroll bars, to make enough room for the contained components. In other words, JScrollPane -s impementation ignores the minimumSize-s.
Update:
From an other angle, JScrollPane -s source code checks the preferredSize of its children, but not the minimumSize. There's no deep philosophy here, JScrollPane is implemented this way.

IntelliJ JTable in JScrollPane

I'm using IntelliJ GUI Builder to design a GUI for my application. In it, there is a JTable inside a JScrollPane that doesn't seem to be working. Firstly, I can't get the column headers to display. Second, table clicking is not working. It acts as if I'm clicking 3 rows down from where I actually am, both in default row selection and in any MouseListeners I implement. Lastly, if the table exceeds the size of the JScrollPane, it just ignores the last X rows and doesn't provide a scroll bar to view them.
I've reworked the project a couple times now, trying extensions of AbstractTableModel, then DefaultTableModel, and lately I have tried ditching a custom TableModel altogether and just using a DefaultTableModel constructor to no avail. Here is all relevant code (some of it is auto-generated by the GUI Builder and I can't modify it directly).
BaldGUI.java (the main gui)
package client;
import client.DataTypes.Record;
import client.DataTypes.RecordSet;
import client.GuiElements.FileTree;
import client.GuiElements.RecordsTable;
import client.GuiElements.TextConsole;
import javax.swing.*;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.tree.TreePath;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.ArrayList;
public class BaldGUI extends JFrame {
//Menu
private JMenuBar menuBar = new JMenuBar();
private JMenu fileMenu = new JMenu("File");
private JMenuItem newBatchItem = new JMenuItem("New Batch");
private JMenuItem saveBatchItem = new JMenuItem("Save Batch");
private JMenuItem loadBatchItem = new JMenuItem("Load Batch");
private static String rootDir = "C:/Users/wf1946/IdeaProjects/DocumentumLoaderTest01/data";
private JPanel mainPanel;
private JPanel LeftSideBarPanel;
private JTree fileTree;
private JButton AddFileButton;
private JButton ChangeDirectoryButton;
private JButton AddDirectoryButton;
private JCheckBox IncludeSubDirectoriesCheckBox;
private JScrollPane DataTableWrapper;
private JTable DataTable;
private JEditorPane Console;
private JScrollPane ConsoleScroller;
public BaldGUI() {
$$$setupUI$$$();
this.loadComponents();
this.AddFileButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
((RecordsTable) DataTable).addItem(new Record());
}
});
this.add(this.mainPanel);
}
private void loadComponents() {
//Menu
this.setJMenuBar(this.menuBar);
this.menuBar.add(this.fileMenu);
this.fileMenu.add(this.newBatchItem);
this.fileMenu.add(this.saveBatchItem);
this.fileMenu.add(this.loadBatchItem);
//Selection handler for the file tree
this.fileTree.addTreeSelectionListener(new TreeSelectionListener() {
#Override
public void valueChanged(TreeSelectionEvent e) {
TreePath path = e.getPath();
if (!fileTree.getModel().isLeaf(path.getLastPathComponent())) { //Directory
AddDirectoryButton.setEnabled(true);
IncludeSubDirectoriesCheckBox.setEnabled(true);
AddFileButton.setEnabled(false);
} else { //File
AddFileButton.setEnabled(true);
AddDirectoryButton.setEnabled(false);
IncludeSubDirectoriesCheckBox.setEnabled(false);
}
}
});
}
//Getters
public JEditorPane getConsole() {
return Console;
}
public JPanel getMainPanel() {
return mainPanel;
}
public JTree getFileTree() {
return fileTree;
}
public JTable getDataTable() {
return this.DataTable;
}
public JCheckBox getIncludeSubDirectoriesCheckBox() {
return IncludeSubDirectoriesCheckBox;
}
public JScrollPane getDataTableWrapper() {
return DataTableWrapper;
}
private void createUIComponents() {
this.Console = new TextConsole();
this.fileTree = new FileTree(this, new File(this.rootDir));
RecordSet rs = new RecordSet();
for (int i = 0; i < 10; i++) rs.add(new Record());
this.DataTable = new RecordsTable(new DefaultTableModel(rs.getData(), RecordsTable.colNames), this);
this.DataTableWrapper = new JScrollPane(this.DataTable);
}
/**
* Method generated by IntelliJ IDEA GUI Designer
* >>> IMPORTANT!! <<<
* DO NOT edit this method OR call it in your code!
*
* #noinspection ALL
*/
private void $$$setupUI$$$() {
createUIComponents();
mainPanel = new JPanel();
mainPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 5, 5));
mainPanel.setMinimumSize(new Dimension(1080, 810));
mainPanel.setPreferredSize(new Dimension(1080, 810));
LeftSideBarPanel = new JPanel();
LeftSideBarPanel.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));
LeftSideBarPanel.setMinimumSize(new Dimension(220, 35));
LeftSideBarPanel.setPreferredSize(new Dimension(220, 600));
mainPanel.add(LeftSideBarPanel);
fileTree.setPreferredSize(new Dimension(200, 530));
fileTree.setShowsRootHandles(true);
LeftSideBarPanel.add(fileTree);
AddFileButton = new JButton();
AddFileButton.setPreferredSize(new Dimension(100, 25));
AddFileButton.setText("Add File");
LeftSideBarPanel.add(AddFileButton);
ChangeDirectoryButton = new JButton();
ChangeDirectoryButton.setPreferredSize(new Dimension(100, 25));
ChangeDirectoryButton.setText("Change Root");
LeftSideBarPanel.add(ChangeDirectoryButton);
AddDirectoryButton = new JButton();
AddDirectoryButton.setPreferredSize(new Dimension(100, 25));
AddDirectoryButton.setText("Add Directory");
LeftSideBarPanel.add(AddDirectoryButton);
IncludeSubDirectoriesCheckBox = new JCheckBox();
IncludeSubDirectoriesCheckBox.setPreferredSize(new Dimension(100, 22));
IncludeSubDirectoriesCheckBox.setText("Subdirectories");
LeftSideBarPanel.add(IncludeSubDirectoriesCheckBox);
DataTableWrapper.setPreferredSize(new Dimension(845, 600));
mainPanel.add(DataTableWrapper);
DataTable.setFillsViewportHeight(true);
DataTableWrapper.setViewportView(DataTable);
ConsoleScroller = new JScrollPane();
mainPanel.add(ConsoleScroller);
Console.setEnabled(false);
Console.setPreferredSize(new Dimension(1070, 195));
ConsoleScroller.setViewportView(Console);
}
/**
* #noinspection ALL
*/
public JComponent $$$getRootComponent$$$() {
return mainPanel;
}
}
RecordsTable.java
package client.GuiElements;
import client.ActionListeners.RightClickMenuItemClick;
import client.ActionListeners.TableRightClickHandler;
import client.BaldGUI;
import client.DataTypes.Record;
import client.DataTypes.RecordSet;
import javax.swing.*;
import javax.swing.table.DefaultTableColumnModel;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
import java.util.ArrayList;
//Table to store the records
public class RecordsTable extends JTable {
//Status codes returned to calling functions to indicate the success or failure of the new record
public static final int APPEND_SUCCESS_CODE = 1;
public static final int APPEND_FAIL_DUPLICATE_CODE = 2;
public static final String[] colNames = {"Status", "File", "Full Path", "Title", "Form Date",
"Form No.", "Language Code", "Filed", "Approval Date", "Filed Form No."};
private RecordSet data = new RecordSet();
//Parent form
BaldGUI parent;
//Right-click menu for table item
JPopupMenu itemRightClickMenu = new JPopupMenu();
JMenuItem itemEdit = new JMenuItem("Edit Record");
JMenuItem itemDelete = new JMenuItem("Remove Record");
public RecordsTable(DefaultTableModel model, BaldGUI form) {
super(model);
this.parent = form;
this.itemRightClickMenu.add(itemEdit);
this.itemRightClickMenu.add(itemDelete);
this.itemEdit.addMouseListener(new RightClickMenuItemClick(this, itemEdit));
this.itemDelete.addMouseListener(new RightClickMenuItemClick(this, itemDelete));
this.addMouseListener(new TableRightClickHandler(this));
this.updateTable();
}
//Attempts to add a new row to the table
//Returns APPEND_FAIL_DUPLICATE_CODE if the selected file is already in the table
//Returns APPEND_SUCCESS_CODE if the record is successfully added
public int addItem(Record newRecord) {
TextConsole tc = ((TextConsole)this.parent.getConsole());
if(this.itemInData(newRecord)) {
tc.addText(
"File " + newRecord.getFileName() + " already included.\n", TextConsole.redStyle
);
return this.APPEND_FAIL_DUPLICATE_CODE;
}
this.data.add(newRecord);
tc.addText("File " + newRecord.getFileName() + " added successfully.\n", TextConsole.greenStyle);
this.updateTable();
return this.APPEND_SUCCESS_CODE;
}
//Updates the table to display any new data
public void updateTable() {
}
//Returns true if the record is already in the table
//Record equality is defined based on the full path to the file
public boolean itemInData(Record item) {
for( Record r : data) {
if(r.equals(item)) return true;
}
return false;
}
public JPopupMenu getItemRightClickMenu() {
return itemRightClickMenu;
}
public JMenuItem getItemEdit() {
return itemEdit;
}
public BaldGUI getParent() {
return parent;
}
}
The Record type is just a basic data container, and RecordSet is just an extension of ArrayList{Record} with a method to turn the data therein into an Object[][] for the DefaultTableModel.
So, as I expected, it was a really simple, dumb mistake. In my RecordsTable class, I stored off the parent GUI (BaldGUI) as a variable called parent. I then had a method getParent() to fetch that parent, and I didn't realize that JTable comes with a method getParent() which gets the surrounding component. By overriding that method, the entire program more or less broke. I changed the method, and it works as it should.

JScrollPane scrollbar does not appear until after rowSorter.toggleSortOrder() is called

I have noticed that when I have a JTable with a TableRowSorter contained by a JScrollPane, the vertical scrollbar does not appear until after I have created SortKeys for the sorter (which is done by calling toggleSortOrder() for one of the columns).
My question is really why? What do SortKeys have to do with a vertical scrollbar?
Update: Added SSCCE that opens a JFrame with a JTable inside a JScrollPane, that sits in a Container along with a "Populate" button. When the table is initially painted, there is no data and hence no need for a scroll bar. After I populate it with 20 rows, there is a need for a scroll bar, but none appears.
There are two ways to make the scroll bar appear:
Click on either of the header cells to cause a sort to occur.
Remove the commented call toggleSortOrder() in the Container's refresh() method.
// table.getRowSorter().toggleSortOrder(0);
toggleSortOrder() calls setSortKeys() calls sort() calls fireRowSorterChanged() and eventually something catches the event and adds the scroll bar.
package test;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.WindowConstants;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableRowSorter;
#SuppressWarnings("serial")
public class TestFrame extends JFrame
{
class MyTableModel extends AbstractTableModel
{
public List<String> names = new ArrayList<String>();
public int getRowCount ()
{
return names.size();
}
public int getColumnCount ()
{
return 2;
}
public String getColumnName (int columnIndex)
{
return columnIndex > 0 ? "Name" : "Number";
}
public Class<?> getColumnClass (int columnIndex)
{
return String.class;
}
public Object getValueAt (int row, int col)
{
return row < names.size() ? col == 0 ? Integer.toString(row) : names.get(row) : "";
}
public void refresh (List<String> names)
{
this.names = names;
}
}
class MyContainer extends java.awt.Container implements ActionListener
{
JTable table;
MyTableModel model = new MyTableModel();
private TableRowSorter<MyTableModel> sorter;
public MyContainer()
{
}
public void init ()
{
sorter = new TableRowSorter<MyTableModel>(model);
table = new JTable(model);
table.setBorder(BorderFactory.createEmptyBorder());
table.setRowHeight(35);
table.getTableHeader().setPreferredSize(new Dimension(200, 35));
table.setRowSorter(sorter);
table.setPreferredScrollableViewportSize(new Dimension(200, 70));
table.setFillsViewportHeight(true);
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
//Create the scroll pane and add the table to it.
JScrollPane scrollPane = new JScrollPane(table);
scrollPane.setBounds(10, 10, 200, 210);
//Add the scroll pane to this panel.
add(scrollPane);
JButton btn = new JButton("Populate");
btn.setActionCommand("populate");
btn.addActionListener(this);
btn.setBounds(10, 220, 200, 35);
add(btn);
}
public void refresh (List<String> rows)
{
model.refresh(rows);
try
{
// Notify sorter that model data (possibly number of rows) has changed.
// Without this call, the sorter assumes the number of rows is the same.
table.getRowSorter().allRowsChanged();
// Do we really want to toggle the sort order every time we refresh?
// User can toggle the sort order himself by clicking on the
// appropriate header cell.
List<?> keys = table.getRowSorter().getSortKeys();
if (null == keys || keys.isEmpty())
{
// table.getRowSorter().toggleSortOrder(0);
}
} catch (Exception e)
{
e.printStackTrace();
}
table.repaint();
}
public void actionPerformed(ActionEvent e)
{
if ("populate".equals(e.getActionCommand()))
{
List<String> rows = new ArrayList<String>();
for (int ii = 0; ii < 20; ii++)
{
rows.add(String.format("%02d", new Integer(ii)));
}
refresh(rows);
}
}
MyTableModel getModel ()
{
return model;
}
}
public static void main (String args[])
{
new TestFrame();
}
MyContainer myContainer = new MyContainer();
TestFrame()
{
myContainer.init();
myContainer.table.getSelectionModel().clearSelection();
add(myContainer);
this.setSize(240, 310);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
//pack();
setVisible(true);
}
}
Well, that is not really a SSCCE because you are using a custom TableModel. If you would have created a proper SSCCE you would be using the DefaultTableModel so that you are testing your code with standard JDK classes. If you did this then you would have noticed that the code would work.
So then your next step would be to try the code with your custom TableModel and you would notice that the code did not work.
So then your question on the forum would be why doesn't the code work with my custom TableModel? The point of the SSCCE is to do basic debugging to isolate where the error is happening so we have information to work with. In your original question we had no idea you where using custom classes.
Anyway, the problem is that your custom TableModel is not notifying the table when a change to the data is made. In your refresh(...) method you need to add the following after you reset the List containing the data:
fireTableRowsInserted(0, names.size()-1);
There is no need for table.repaint() in any of your code.

Java JTable: How to render left column cells for simple row header purposes

My aim is to create a JTable, and render the far left column cells only, with the aim of creating row headers for the table.
All row table examples I have come across online seem convoluted or do not fit my purposes, so I am wondering is there a simple way of creating JTable row headers through rendering the left column cells only?
Below I have code of a simple table with 2 columns and two rows. Is it possible someone could modify this, or explain in simple terms, how I could go about rendering the far left column for row header purposes.
Thank you.
import javax.swing.*;
import java.awt.*;
import javax.imageio.*;
import java.io.*;
import java.awt.image.BufferedImage;
import java.util.*;
public class GUITable extends JFrame{public GUITable(){
init();
}
public final void init(){
String[] columnNames = {"", "Gross Weight"};
Object[][] data = {
{"", new Integer(100)},};
final JTable table = new JTable(data, columnNames);
table.setPreferredScrollableViewportSize(new Dimension(500, 70));
table.setFillsViewportHeight(true);
JScrollPane scrollPane = new JScrollPane(table);
add(scrollPane);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
} catch (Exception e) {
e.printStackTrace();
}
GUITable ex = new GUITable();
ex.setVisible(true);
}
});
}
}
Yes - by using a custom TableCellRenderer, you can modify the way the first column (and first column only) displays.
Essentially you can use this to set the TableCellRenderer on the first column only:
table.getColumnModel().getColumn(0).setCellRenderer(new CustomRenderer());
And you can extend the DefaultTableCellRenderer to take care of any special rendering you want to do:
//Custom Renderer - does the default rendering except if told the row should be a different color
public static class CustomRenderer extends DefaultTableCellRenderer{
public CustomRenderer(){
super();
//Customize the rendering however you want
setBackground(UIManager.getColor("TableHeader.background"));
}
}
To put it all together in your example:
import javax.swing.*;
import javax.swing.table.DefaultTableCellRenderer;
import java.awt.*;
public class TestTable extends JFrame{
public TestTable(){
init();
}
public final void init(){
String[] columnNames = {"", "Gross Weight"};
Object[][] data = {{"", new Integer(100)},};
final JTable table = new JTable(data, columnNames);
// Add Renderer to first column only
table.getColumnModel().getColumn(0).setCellRenderer(new CustomRenderer());
table.setPreferredScrollableViewportSize(new Dimension(500, 70));
table.setFillsViewportHeight(true);
JScrollPane scrollPane = new JScrollPane(table);
scrollPane.setPreferredSize(new Dimension(300, 200));
add(scrollPane);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
} catch (Exception e) {
e.printStackTrace();
}
TestTable ex = new TestTable();
ex.pack();
ex.setVisible(true);
}
});
}
//Custom Renderer - does the default rendering except if told the row should be a different color
public static class CustomRenderer extends DefaultTableCellRenderer{
public CustomRenderer(){
super();
//Customize the rendering however you want
setBackground(UIManager.getColor("TableHeader.background"));
}
}
}
your code example could be
import javax.swing.*;
import java.awt.*;
public class GUITable extends JFrame {
public GUITable() {
init();
}
public final void init() {
String[] columnNames = {"", "Gross Weight"};
Object[][] data = {{"", new Integer(100)},};
final JTable table = new JTable(data, columnNames);
table.setPreferredScrollableViewportSize(new Dimension(500, 70));
table.setFillsViewportHeight(true);
JScrollPane scrollPane = new JScrollPane(table);
add(scrollPane);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
}
public static void main(String[] args) {
try {
UIManager.setLookAndFeel(
"com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");//Java6
//UIManager.setLookAndFeel(
//"javax.swing.plaf.nimbus.NimbusLookAndFeel");//Java7
} catch (Exception fail) {
}
SwingUtilities.invokeLater(new Runnable() {
public void run() {
GUITable ex = new GUITable();
ex.setVisible(true);
}
});
}
}
not sure from your descriptions, are you meaning Row Number Table by #camickr, or another half_sized attempt

Categories

Resources