I have a JTable with rows that list files and folders in a directory. The folder rows have a button that hide and show rows listing the files in the folder. When the application starts up, the files rows that come after their folder row are hidden. I used RowFilter to initially hide these rows after the table was created in a subclass method:
...
public void createTable(){
//Create and populate table
...
//Add the filter to initially hide the subfile rows
TableRowSorter<DefaultTableModel> sorter = new TableRow Sorter<DefaultTableModel>(tableModel);
RowFilter hidefilter = getRowFilter();
sorter.setRowFilter(hidefilter);
myTable.setRowSorter(sorter);
}
...
private RowFilter getRowFilter() {
RowFilter<DefaultTableModel, Integer> filter = new RowFilter<DefaultTableModel, Integer>() {
#Override
public boolean include(RowFilter.Entry<? extends DefaultTableModel, ? extends Integer> entry) {
int modelRow = entry.getIdentifier();
if(/* current row column contains a certain flag */){
//Hide the row that represents a file in the folder
return false;
}
else return true;
}
};
return filter;
}
This code successfully hides the rows, but I'm wondering how to show and re-hide only some of the hidden rows. Can this be done with a filter class like the one I've already done? Is there a way for a filter function to be called by a button and filter based on a value found in a column?
The basic idea is that we need to have a Filter class that filters entries depending on the value of its fields (I only created one field to keep the example simple), and the filter also has methods that change the value of those fields.
Then we create a button that calls this method when pressed, followed by a call to table.getRowSorter().allRowsChanged(), which signals to the table that the data has changed so it has to redraw itself.
Here's a working example.
public class Test {
public static class Filter extends RowFilter<TableModel, Integer> {
private String includePrefix = "Foo";
#Override
public boolean include(
javax.swing.RowFilter.Entry<? extends TableModel, ? extends Integer> entry) {
return entry.getStringValue(0).startsWith( includePrefix );
}
// Calling this method changes the filter to allow a different prefix
public void swapPrefix() {
this.includePrefix = this.includePrefix.equals("Foo") ? "Bar" : "Foo";
}
}
public static void main(String[] args) {
//setup
JFrame frame = new JFrame();
frame.setLayout( new BoxLayout(frame.getContentPane(), BoxLayout.PAGE_AXIS));
JTable table = new JTable( new Object[][]{ new Object[]{ "Foo 1" },
new Object[]{ "Bar 1" },
new Object[]{ "Foo 2" },
new Object[]{ "Foo 3" },
new Object[]{ "Bar 2" }},
new Object[] { "Foo"});
//create and configure sorter
Filter filter = new Filter();
TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(table.getModel());
sorter.setRowFilter(filter);
table.setRowSorter(sorter);
JButton changeFilter = new JButton( "Change filter");
// pressing the button changes the filter first, then tells the table sorter to update the display
changeFilter.addActionListener( e -> { filter.swapPrefix(); table.getRowSorter().allRowsChanged(); });
//display window
frame.add( table );
frame.add(changeFilter);
frame.pack();
frame.setVisible( true );
}
}
Related
I am able to set the column's header but not able to set icon in all the rows of first column of JTable.
public class iconRenderer extends DefaultTableCellRenderer{
public Component getTableCellRendererComponent(JTable table,Object obj,boolean isSelected,boolean hasFocus,int row,int column){
imageicon i=(imageicon)obj;
if(obj==i)
setIcon(i.imageIcon);
setBorder(UIManager.getBorder("TableHeader.cellBorder"));
setHorizontalAlignment(JLabel.CENTER);
return this;
}
}
public class imageicon{
ImageIcon imageIcon;
imageicon(ImageIcon icon){
imageIcon=icon;
}
}
and below lines in my BuildTable() method.
public void SetIcon(JTable table, int col_index, ImageIcon icon){
table.getTableHeader().getColumnModel().getColumn(col_index).setHeaderRenderer(new iconRenderer());
table.getColumnModel().getColumn(col_index).setHeaderValue(new imageicon(icon));
}
How can we set it for all rows of first columns? I have tried with for loop but didnt get yet for rows to iterate to set icon. Or is there any other way?
There is no need to create a custom render. JTable already supports an Icon renderer. YOu just need to tell the table to use this renderer. This is done by overriding the getColumnClass(...) method of the table model:
import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;
public class TableIcon extends JPanel
{
public TableIcon()
{
Icon aboutIcon = new ImageIcon("about16.gif");
Icon addIcon = new ImageIcon("add16.gif");
Icon copyIcon = new ImageIcon("copy16.gif");
String[] columnNames = {"Picture", "Description"};
Object[][] data =
{
{aboutIcon, "About"},
{addIcon, "Add"},
{copyIcon, "Copy"},
};
DefaultTableModel model = new DefaultTableModel(data, columnNames)
{
// Returning the Class of each column will allow different
// renderers to be used based on Class
public Class getColumnClass(int column)
{
switch (column)
{
case 0: return Icon.class;
default: return super.getColumnClass(column);
}
}
};
JTable table = new JTable( model );
table.setPreferredScrollableViewportSize(table.getPreferredSize());
JScrollPane scrollPane = new JScrollPane( table );
add( scrollPane );
}
private static void createAndShowGUI()
{
JFrame frame = new JFrame("Table Icon");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TableIcon());
frame.setLocationByPlatform( true );
frame.pack();
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI();
}
});
}
}
You are just using iconRenderer for the render of your header. Also set the Column's Cell Reneder to be an instance of iconRenderer as well. Call setCellRenderer on the column.
http://download.oracle.com/javase/6/docs/api/javax/swing/table/TableColumn.html#setCellRenderer(javax.swing.table.TableCellRenderer)
Side note: Java coding standards specify that class names should start with capital letters, so iconRenderer should be IconRenderer instead.
I know the post is a little old but it's never too late ...
I will post here how to insert an icon without using a DefaultTableCellRenderer class, I use this for when I will only show an icon on the screen in a simple way not very elaborate.
I do it in a simple way ... I always create some tablemodel creators in the classes that I inherit. I usually pass by parameter the list of titles and types of objects.
Method that creates the tablemodel in the upper class:
protected void createTableModel(String[] columns, Class[] types){
String[] vetStr = new String[columns.length];
boolean[] vetBoo = new boolean[columns.length];
Arrays.fill(vetStr, null);
Arrays.fill(vetBoo, false);
table.setModel(new javax.swing.table.DefaultTableModel(
new Object [][] { vetStr },
columns
) {
boolean[] canEdit = vetBoo;
public Class getColumnClass(int columnIndex) {
return types [columnIndex];
}
public boolean isCellEditable(int rowIndex, int columnIndex) {
return canEdit [columnIndex];
}
});
table.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_OFF);
}
Inherited class constructor:
"See that here I set the type as ImageIcon.class for the column"
.... constructor....
super("Balança");
String[] columns = {"#", "Nome", "Porta", "Padrão"};
Class[] types = {Long.class, String.class, String.class, ImageIcon.class};
**strong text**super.createTableModel(columns, types);
When I list the items on the tablemodel there I show the image.
list.forEach( obj -> {
tableModel.addRow(new Object[]{
obj.getId(),
obj.getName(),
obj.getPort(),
(obj.getId() == Global.standardScale)?
new ImageIcon(getClass().getResource("./br/com/valentin/img/accept.png")): ""
});
});
I have a JTable. User will enter an ID in a particular column and press TAB. I need to add event on that column to fetch value from DB and populate the rest of the columns of that row and create a new row for the next entry.
I am new to Swing and its difficult to find what is the best way to do it as i can see examples which were written in 2010 or so. Not sure if thats relevant still.
What I don't know:
adding event handler to a particular column's cell in table.
add next row after populating the data.
You can use a TableModelListener for this. When user change ID column value, tableChanged() is invoked. Then the relevant data is fetched from the DB and set in the row. And a new row is added as well. Try below example.
(For demonstration purpose I have used a mock database in this example. It only gives rows for IDs "111" and "222".)
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.DefaultTableModel;
import java.util.Vector;
public class TableDataChange
{
public static void main(String[] args)
{
DefaultTableModel tableModel = new DefaultTableModel(
new Object[][] {{"", "", ""}},
new Object[] {"ID", "Column 2", "Column 3"});
tableModel.addTableModelListener(new TableModelListener()
{
#Override
public void tableChanged(TableModelEvent e)
{
String id = (String) tableModel.getValueAt(e.getFirstRow(), 0);
if (id != null)
{
Vector row = Database.loadRowForId(id);
tableModel.getDataVector().set(e.getFirstRow(), row);
tableModel.addRow(new Vector());
}
}
});
JTable table = new JTable(tableModel);
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(new JScrollPane(table));
f.setBounds(300, 200, 400, 300);
f.setVisible(true);
}
}
// Mock database
class Database
{
static Vector loadRowForId(String id)
{
Vector row = new Vector();
if (id.equals("111"))
{
row.add("111");
row.add("aaa");
row.add("bbb");
}
else if (id.equals("222"))
{
row.add("222");
row.add("ppp");
row.add("qqq");
}
return row;
}
}
You can try something like this,
//Add Key Listener
table.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent event) {
if (event.getKeyChar() == KeyEvent.VK_TAB) {
int selectedColumn = table.getSelectedColumn();
//Now you can search records related to ID and populate the table
}
}
});
I want to update a swing table's row value in the DB, clear the model, and load the new model with the new updated value. The problem is that when I click the button to update the value, the model is cleared but no data is shown in the table. What am I doing wrong?
Parts of the code not relative to the table have been removed in order to make the example easier.
public class ShowValues extends JFrame {
//Attributes
private DefaultTableModel model;
private JTable table = new JTable()
{
public boolean isCellEditable(int row, int column)
{
return false;
};
};
public ShowValues() {
btnDeleteItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0)
{
int selectedRowNumber = table.getSelectedRow();
String selectedItemId = (String) table.getModel().getValueAt(selectedRowNumber, 0);
//code to change selected item's state to deleted in the DB
model.setRowCount(0); //clear model
showTable(); //load new model
labelMsg.setText("Item deleted");
}
});
btnDeleteItem.setBounds(200, 320, 203, 57);
contentPane.add(btnDeleteItem);
}
public void showTable()
{
String columnNames[] = {"Item Id", "Item Name", "Item price"};
//code to get the data form the DB
String dataValues[][] = new String[itemNum][3];
for(int i=0; i<itemNum; i++)
{
dataValues[i][0] = idItem[i];
dataValues[i][1] = nameItem[i];
dataValues[i][2] = priceItem[i];
}
model = new DefaultTableModel(dataValues, columnNames);
table.setModel(model);
scrollPane = new JScrollPane(table);
scrollPane.setBounds(17, 20, 490, 205);
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
topPanel.add(scrollPane);
}
}
//code to change selected item's state to deleted in the DB
Why would you want to reload all the data just because the change the value of one property in the row? That is not very efficient.
Just use the setValueAt(...) method to change the value in the JTable.
The problem is that when I click the button to update the value, the model is cleared but no data is shown in the table
The problem is you create a new JTable, but you never add the table to the viewport of your JScrollPane.
The solution is to NOT create a new JTable. Just create a new TableModel. Then you can just use:
table.setModel( theNewlyCreateTableModel );
How to go to last row in JTable when we press Up key at first row and also how to go to first row when we press Down key at last row? Like Enter key does when we press Enter key at last row it will go to the first row.
I already done this coding but it just show data to text field:
private void jtKeyReleased(java.awt.event.KeyEvent evt) {
if(evt.getKeyCode()==KeyEvent.VK_DOWN ||evt.getKeyCode()==KeyEvent.VK_UP){
int row=jt.getSelectedRow();
String TableClick=(jt.getModel().getValueAt(row,0).toString());
try{
String sql="select Product,Roo,TotalStock from pro where
Product='"+TableClick+ "'";
PreparedStatement pst = (PreparedStatement)
conn.prepareStatement(sql);
ResultSet res = pst.executeQuery();
if(res.next()){
String add1=res.getString("Product");
proo.setText(add1);
// String add2=res.getString("Id");
//idd.setText(add2);
String add3=res.getString("Roo");
rooo.setText(add3);
String add4=res.getString("TotalStock");
stkk.setText(add4);
abc=res.getString("TotalStock");
}
} catch(Exception e) {
} //catch
} // if
}
You need to create two custom Actions:
one Action to wrap from the first row to the bottom and
another Action to wrap from the bottom to the top.
The easiest way to do this is to take advantage of existing Actions defined in JTable. The UP keys move up one line at a time and the Down key moves down one line at a time. You can also use CTRL+HOME to go to the top line and CTRL_END to go to the last line.
So I would suggest to start with the UP Action and modify it to implement the CTRL+END Action. The easiest way to do this is to take advantage of the concept of Wrapping Actions. This class is a wrapper class for an existing Action and allows you to add custom code to enhance the Action.
import java.awt.event.*;
import javax.swing.*;
public class UpAction extends WrappedAction implements ActionListener
{
private JTable table;
private Action endAction;
/*
* Specify the component and KeyStroke for the Action we want to wrap
*/
public UpAction(JTable table, KeyStroke keyStroke)
{
super(table, keyStroke);
this.table = table;
endAction = table.getActionMap().get("selectLastRow");
}
/*
* Provide the custom behaviour of the Action
*/
public void actionPerformed(ActionEvent e)
{
if (table.getSelectedRow() == 0)
endAction.actionPerformed( e );
else
invokeOriginalAction( e );
}
private static void createAndShowGUI()
{
JTable table = new JTable(7, 5);
new UpAction(table, KeyStroke.getKeyStroke("UP"));
JFrame frame = new JFrame("SSCCE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( new JScrollPane(table) );
frame.setLocationByPlatform( true );
frame.pack();
frame.setVisible( true );
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI();
}
});
}
}
You would need to create a similar Action for the DOWN functionality. Note the action string name for selecting the first row is: selectFirstRow. Check out Key Bindings for a list of all the Actions used by a given component.
I am able to set the column's header but not able to set icon in all the rows of first column of JTable.
public class iconRenderer extends DefaultTableCellRenderer{
public Component getTableCellRendererComponent(JTable table,Object obj,boolean isSelected,boolean hasFocus,int row,int column){
imageicon i=(imageicon)obj;
if(obj==i)
setIcon(i.imageIcon);
setBorder(UIManager.getBorder("TableHeader.cellBorder"));
setHorizontalAlignment(JLabel.CENTER);
return this;
}
}
public class imageicon{
ImageIcon imageIcon;
imageicon(ImageIcon icon){
imageIcon=icon;
}
}
and below lines in my BuildTable() method.
public void SetIcon(JTable table, int col_index, ImageIcon icon){
table.getTableHeader().getColumnModel().getColumn(col_index).setHeaderRenderer(new iconRenderer());
table.getColumnModel().getColumn(col_index).setHeaderValue(new imageicon(icon));
}
How can we set it for all rows of first columns? I have tried with for loop but didnt get yet for rows to iterate to set icon. Or is there any other way?
There is no need to create a custom render. JTable already supports an Icon renderer. YOu just need to tell the table to use this renderer. This is done by overriding the getColumnClass(...) method of the table model:
import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;
public class TableIcon extends JPanel
{
public TableIcon()
{
Icon aboutIcon = new ImageIcon("about16.gif");
Icon addIcon = new ImageIcon("add16.gif");
Icon copyIcon = new ImageIcon("copy16.gif");
String[] columnNames = {"Picture", "Description"};
Object[][] data =
{
{aboutIcon, "About"},
{addIcon, "Add"},
{copyIcon, "Copy"},
};
DefaultTableModel model = new DefaultTableModel(data, columnNames)
{
// Returning the Class of each column will allow different
// renderers to be used based on Class
public Class getColumnClass(int column)
{
switch (column)
{
case 0: return Icon.class;
default: return super.getColumnClass(column);
}
}
};
JTable table = new JTable( model );
table.setPreferredScrollableViewportSize(table.getPreferredSize());
JScrollPane scrollPane = new JScrollPane( table );
add( scrollPane );
}
private static void createAndShowGUI()
{
JFrame frame = new JFrame("Table Icon");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TableIcon());
frame.setLocationByPlatform( true );
frame.pack();
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI();
}
});
}
}
You are just using iconRenderer for the render of your header. Also set the Column's Cell Reneder to be an instance of iconRenderer as well. Call setCellRenderer on the column.
http://download.oracle.com/javase/6/docs/api/javax/swing/table/TableColumn.html#setCellRenderer(javax.swing.table.TableCellRenderer)
Side note: Java coding standards specify that class names should start with capital letters, so iconRenderer should be IconRenderer instead.
I know the post is a little old but it's never too late ...
I will post here how to insert an icon without using a DefaultTableCellRenderer class, I use this for when I will only show an icon on the screen in a simple way not very elaborate.
I do it in a simple way ... I always create some tablemodel creators in the classes that I inherit. I usually pass by parameter the list of titles and types of objects.
Method that creates the tablemodel in the upper class:
protected void createTableModel(String[] columns, Class[] types){
String[] vetStr = new String[columns.length];
boolean[] vetBoo = new boolean[columns.length];
Arrays.fill(vetStr, null);
Arrays.fill(vetBoo, false);
table.setModel(new javax.swing.table.DefaultTableModel(
new Object [][] { vetStr },
columns
) {
boolean[] canEdit = vetBoo;
public Class getColumnClass(int columnIndex) {
return types [columnIndex];
}
public boolean isCellEditable(int rowIndex, int columnIndex) {
return canEdit [columnIndex];
}
});
table.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_OFF);
}
Inherited class constructor:
"See that here I set the type as ImageIcon.class for the column"
.... constructor....
super("Balança");
String[] columns = {"#", "Nome", "Porta", "Padrão"};
Class[] types = {Long.class, String.class, String.class, ImageIcon.class};
**strong text**super.createTableModel(columns, types);
When I list the items on the tablemodel there I show the image.
list.forEach( obj -> {
tableModel.addRow(new Object[]{
obj.getId(),
obj.getName(),
obj.getPort(),
(obj.getId() == Global.standardScale)?
new ImageIcon(getClass().getResource("./br/com/valentin/img/accept.png")): ""
});
});