Java JTable - show rows only which matches the string - java

I have a JTable populated with data from a database. I have added a search function that display the rows, if word matched to some rows when the data is typed into a JTextField. But at the minute as you type a string or word that do not matches any of the rows data, what I want to do is to hide all the rows, and the JTable will show just the column names. Just like when something matches IT, shows those rows only which has the string that user typed and hide others.
Here is my code that I am using:
if (text.length() == 0) {
sorter.setRowFilter(null);
} else {
try {
sorter.setRowFilter(
RowFilter.regexFilter(text));
} catch (PatternSyntaxException pse) {
System.err.println("Bad regex pattern");
}
}
}

You want to use a DocumentListener, along with the row filter. You can see how to Write a DocumentListener.
Basically the listener listens for changes in the underlying document of the text field. From the methods you override in the DocumentListener, you can get the text, like you're currently doing, and set the regex ex filter like you're currently doing
Here's simple example (disregard the applet. I just got the boilerplate code from this post and added the document listener). Note: that same post has an answer provided that will allow you to filter in a case-insensitive way, if you're looking for that functionality
import javax.swing.JApplet;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.RowFilter;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;
public class TestTableSorterFilter extends JApplet {
private String[] columnNames
= {"Country", "Capital", "Population in Millions", "Democracy"};
private Object[][] data = {
{"USA", "Washington DC", 280, true},
{"Canada", "Ottawa", 32, true},
{"United Kingdom", "London", 60, true},
{"Germany", "Berlin", 83, true},
{"France", "Paris", 60, true},
{"Norway", "Oslo", 4.5, true},
{"India", "New Delhi", 1046, true}
};
private JTable jTable = new JTable(data, columnNames);
private TableRowSorter<TableModel> rowSorter
= new TableRowSorter<>(jTable.getModel());
private JTextField jtfFilter = new JTextField();
private JButton jbtFilter = new JButton("Filter");
public TestTableSorterFilter() {
jTable.setRowSorter(rowSorter);
JPanel panel = new JPanel(new BorderLayout());
panel.add(new JLabel("Specify a word to match:"),
BorderLayout.WEST);
panel.add(jtfFilter, BorderLayout.CENTER);
add(panel, BorderLayout.SOUTH);
add(new JScrollPane(jTable), BorderLayout.CENTER);
jtfFilter.getDocument().addDocumentListener(new DocumentListener(){
#Override
public void changedUpdate(DocumentEvent arg0) {}
#Override
public void insertUpdate(DocumentEvent arg0) {
String text = jtfFilter.getText();
if (text.trim().length() == 0) {
rowSorter.setRowFilter(null);
} else {
rowSorter.setRowFilter(RowFilter.regexFilter(text));
}
}
#Override
public void removeUpdate(DocumentEvent arg0) {
String text = jtfFilter.getText();
if (text.trim().length() == 0) {
rowSorter.setRowFilter(null);
} else {
rowSorter.setRowFilter(RowFilter.regexFilter(text));
}
}
});
}
}

Related

SetCaretPosition in JTable

How can I set caret position in a JTable?
I know JTextField has setCaretPosition(n) function. But I cannot access JTextField in the JTable.
I would like the Table text caret position equal text length. It is possible to mouseclick event but it should be normal position.
My code:
public class TableTest extends javax.swing.JFrame
{
public TableTest()
{
javax.swing.JTable jTable1 = new javax.swing.JTable();
jTable1.setModel(new javax.swing.table.DefaultTableModel(
new Object[][]
{
{
"This is too long text!", "This is too long text!",
},
{
"This is too long text!", "This is too long text!",
}
},
new String[]
{
"Title 1", "Title 2",
}
));
add(jTable1);
pack();
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocationRelativeTo(null);
}
public static void main(String args[])
{
new TableTest().setVisible(true);
}
}
This table cells are shown:
[This is too...] but it should be [..long text!]
This table cells are shown: [This is too...] but it should be [..long text!]
This the renderer that displays the text in a cell. The default renderer is a JLabel and it will display ... when the text is truncated. If you want the ... at the start of the cell then you need to create a custom renderer.
Check out the Left Dot Renderer for a renderer that does this.
The default "startEditing" action, suggested here, is provided by BasicTableUI. It typically selects all the text, which you want to avoid. You can invoke setCaretPosition() in a custom Action bound to your preferred keystroke. The example below uses the name "myEditing" and the Space key.
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.KeyStroke;
import javax.swing.table.DefaultTableModel;
import javax.swing.text.JTextComponent;
/**
* #see https://stackoverflow.com/a/38051001/230513
*/
public class TableTest {
private void display() {
JFrame f = new JFrame("TableTest");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
DefaultTableModel model = new DefaultTableModel(
new String[][]{
{"This is too long text!", "This is too long text!",},
{"This is too long text!", "This is too long text!",}
},
new String[]{
"Title 1", "Title 2",}
) {
#Override
public Class<?> getColumnClass(int columnIndex) {
return String.class;
}
};
JTable table = new JTable(model) {
#Override
public Dimension getPreferredScrollableViewportSize() {
return new Dimension(320, 160);
}
};
table.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), "myEditing");
table.getActionMap().put("myEditing", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
table.editCellAt(table.getSelectedRow(), table.getSelectedColumn());
Component editor = table.getEditorComponent();
if (editor != null) {
editor.requestFocus();
if (editor instanceof JTextComponent) {
JTextComponent jtc = (JTextComponent) editor;
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
jtc.setCaretPosition(jtc.getDocument().getLength());
}
});
}
}
}
});
table.putClientProperty("JTable.autoStartsEdit", Boolean.FALSE);
f.add(new JScrollPane(table));
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new TableTest()::display);
}
}
For reference, a similar approach for a mouse event is shown in this previous version.

Using setBorder does not have any affect with DefaultTableCellRenderer

Some samples I found were modified to try and add padding to the cell using setBorder.
Essentially trying to accomplish the same behavior as setIntercellSpacing but using the BorderFactory class instead.
Based on the following code, there does not appear to be any effect on the cell padding.
What step did I miss?
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JComponent;
import javax.swing.JTextField;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.DefaultTableCellRenderer;
public class EvenOddRowCellRenderer extends JFrame {
DefaultTableModel tmodel = new DefaultTableModel(new Object[][] { { "some", "text" },
{ "any", "text" }, { "even", "more" }, { "text", "strings" }, { "and", "other" },
{ "text", "values" } }, new Object[] { "Column 1", "Column 2" });
public EvenOddRowCellRenderer() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JTable table = new JTable(tmodel);
table.setDefaultRenderer(Object.class, new MyRenderer());
getContentPane().add(new JScrollPane(table), BorderLayout.CENTER);
pack();
}
public static void main(String arg[]) {
new EvenOddRowCellRenderer().setVisible(true);
}
}
class MyRenderer extends DefaultTableCellRenderer {
public MyRenderer ()
{
super();
setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
}
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
boolean hasFocus, int row, int column) {
JTextField editor = new JTextField();
if (value != null)
editor.setText(value.toString());
editor.setBackground((row % 2 == 0) ? Color.white : Color.cyan);
return editor;
}
}
The default renderer is a JLabel. You are setting the Border on the renderer, which is ok.
The problem is you create a second component, (the text field) and you are returning the JTextField as the renderer component. You didn't add the Border to the text field. Get rid of the JTextField.
Just use return this; to return the label as the renderer.

JTable search and deleting rows

I am learning how to use JTable and I have issues with both my search and delete function.
From my codes, if I checked row 1 and row 3 and press the delete button, it will remove perfectly.
If I enter J in my search textfield and press the search button, John and Jane will be displayed
and now if I check the 2 rows and press the delete button then press the search button again,
John and Jane will be removed including Kate and Ann.
Please run the codes below if you guys still don't understand what I am saying.
follow the steps that I mentioned and you will see what's wrong.
help me. Thanks
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.RowFilter;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;
public class TableExample extends JFrame {
private static final long serialVersionUID = 1L;
protected static final boolean DEBUG = false;
private JTable table;
TableModel model;
JPanel panel = new JPanel(new BorderLayout());
JButton button = new JButton("Delete");
JTextField searchTextField = new JTextField(15);
JButton searchBtn = new JButton("Search");
JPanel flowLayoutPanel = new JPanel(new FlowLayout());
TableRowSorter<TableModel> sorter;
public TableExample() {
String[] columnNames = {"Employer", "Company", "Salary", "Boolean"};
Object[][] data = {
{"Kate", "20",new Integer(5000), new Boolean(false)},
{"John", "35", new Integer(3000), new Boolean(false)},
{"Ann", "20", new Integer(4000), new Boolean(false)},
{"Jane", "12", new Integer(4000), new Boolean(false)},
{"May", "42", new Integer(4500), new Boolean(false)}
};
model = new DefaultTableModel(data, columnNames) {
#Override
public Class getColumnClass(int column) {
switch(column) {
case 0:
case 1: return String.class;
case 2: return Integer.class;
case 3: return Boolean.class;
default: return Object.class;
}
}
};
table = new JTable(model) {
public boolean isCellEditable(int row, int col) {
return true;
}
};
sorter = new TableRowSorter<TableModel>(model);
table.setPreferredScrollableViewportSize(table.getPreferredSize());
table.setRowSorter(sorter);
table.setGridColor(Color.black);
JScrollPane scrollPane = new JScrollPane(table);
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
for(int row = 0; row < table.getRowCount(); ++row) {
if((Boolean) table.getValueAt(row, 3) == true) {
((DefaultTableModel) model).removeRow(row);
row--;
}
}
}
});
searchBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String text = searchTextField.getText();
if (text.length() == 0) {
sorter.setRowFilter(null);
} else {
sorter.setRowFilter(RowFilter.regexFilter(text));
}
}
});
flowLayoutPanel.add(searchTextField);
flowLayoutPanel.add(searchBtn);
panel.add(flowLayoutPanel,BorderLayout.NORTH);
panel.add(scrollPane , BorderLayout.CENTER);
panel.add(button, BorderLayout.PAGE_END);
getContentPane().add(panel);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
TableExample frame = new TableExample();
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.pack();
frame.setLocation(150, 150);
frame.setVisible(true);
}
});
}
}
Since the row number changes when you filter the table. Use original row number to delete a correct row.
Use table.convertRowIndexToModel(row) to get the actual row number.
Maps the index of the row in terms of the view to the underlying TableModel. If the contents of the model are not sorted the model and view indices are the same.
sample code:
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
for(int row = 0; row < table.getRowCount(); ++row) {
if((Boolean) table.getValueAt(row, 3) == true) {
((DefaultTableModel) model).removeRow(table.convertRowIndexToModel(row));
row--;
}
}
}
});
Read more...

JTable update after row selection

Here is an SSCCE of a JTextField that auto-completes a JTable.
It works fine until I wanted to do something when a row is selected. The problem is that whenever a row in the JTable is selected, changing the JTextField text throws an exception:
Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: -1
at java.util.Vector.elementAt(Vector.java:430)
at javax.swing.table.DefaultTableModel.getValueAt(DefaultTableModel.java:632)
at javax.swing.JTable.getValueAt(JTable.java:2681)
at fr.ensicaen.si.client.AnimalAutoComplete$1.valueChanged(AnimalAutoComplete.java:73)
at javax.swing.DefaultListSelectionModel.fireValueChanged(DefaultListSelectionModel.java:167)
at javax.swing.DefaultListSelectionModel.fireValueChanged(DefaultListSelectionModel.java:147)
at javax.swing.DefaultListSelectionModel.fireValueChanged(DefaultListSelectionModel.java:194)
at javax.swing.DefaultListSelectionModel.changeSelection(DefaultListSelectionModel.java:388)
at javax.swing.DefaultListSelectionModel.changeSelection(DefaultListSelectionModel.java:398)
at javax.swing.DefaultListSelectionModel.removeSelectionIntervalImpl(DefaultListSelectionModel.java:559)
at javax.swing.DefaultListSelectionModel.clearSelection(DefaultListSelectionModel.java:403)
at javax.swing.JTable.clearSelection(JTable.java:2075)
at fr.ensicaen.si.client.AnimalAutoComplete$AutoCompleteListener.fill(AnimalAutoComplete.java:97)
at fr.ensicaen.si.client.AnimalAutoComplete$AutoCompleteListener.insertUpdate(AnimalAutoComplete.java:93)
In fact, selecting a row and do something works. But when the JTextField is modified, the ListSelectionListener is fired again (but shouldn't?) and I can't figure out why!
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.border.EmptyBorder;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableModel;
import java.awt.GridLayout;
public class AnimalAutoComplete extends JFrame {
private JPanel contentPane;
private JScrollPane animalPane;
private JTable animalTable;
private JPanel panel;
private JTextField animalField;
/** Create the frame. */
public AnimalAutoComplete() {
/* Frame structure */
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 710, 471);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
contentPane.setLayout(new BorderLayout(0, 0));
setContentPane(contentPane);
animalPane = new JScrollPane();
contentPane.add(animalPane, BorderLayout.CENTER);
panel = new JPanel();
contentPane.add(panel, BorderLayout.NORTH);
panel.setLayout(new GridLayout(1, 0, 0, 0));
/* JTable model */
DefaultTableModel animalModel = new DefaultTableModel();
animalTable = new JTable(animalModel);
animalPane.setViewportView(animalTable);
animalModel.addColumn("Animal");
/* Initially fills the JTable */
List<String> animals = new ArrayList<String>();
animals.add("Dog");
animals.add("Cat");
animals.add("Fish");
for (String a : animals) {
animalModel.addRow( new Object[] {a} );
}
/* Text fields */
animalField = new JTextField();
panel.add(animalField);
animalField.setColumns(10);
/* This listener updates the JTable depending on the JTextField */
animalField.getDocument().addDocumentListener(new AutoCompleteListener(animalTable, animals, animalField));
/* This listener will do something useful when an animal is selected*/
animalTable.getSelectionModel().addListSelectionListener(
new ListSelectionListener(){
public void valueChanged(ListSelectionEvent event) {
System.out.println(animalTable.getValueAt(animalTable.getSelectedRow(), 0));
}
}
);
}
private final class AutoCompleteListener implements DocumentListener {
private final JTable animalTable;
private final List<String> animals;
private JTextField searchedAnimal;
private AutoCompleteListener(JTable animalTable,
List<String> animals, JTextField textAnimal) {
this.animalTable= animalTable;
this.animals = animals;
this.searchedAnimal = textAnimal;
}
public void changedUpdate(DocumentEvent arg0) {fill();}
public void insertUpdate(DocumentEvent arg0) {fill();}
public void removeUpdate(DocumentEvent arg0) {fill();}
public void fill() {
animalTable.clearSelection();
DefaultTableModel model = (DefaultTableModel) animalTable.getModel();
model.setRowCount(0);
for (String a : animals) {
if (a.startsWith(this.searchedAnimal.getText())) {
model.addRow(new Object[] {a});
}
}
}
}
public static void main(String[] args) {
AnimalAutoComplete frame = new AnimalAutoComplete();
frame.setVisible(true);
}
}
The problem is that the selection event is fired twice when a table row(or col/cell) gets selected. First time with index -1 and the second time with correct index. So check with a condition if animalTable.getSelectedRow() is returning -1:
public void valueChanged(ListSelectionEvent event) {
int selRow = animalTable.getSelectedRow()
if(selRow >= 0)
{
System.out.println(animalTable.getValueAt(selRow, 0));
// or do other things
}
}
As #camickr has suggested below, you can also make use of event.getValueIsAdjusting() method: which returns true if the selection is still changing. Many list selection listeners are interested only in the final state of the selection and can ignore list selection events when this method returns true. In fact using this function is preferable against the one mentioned above as it makes the action code more specific.

Deleting row from JTable after valueChanged event is triggered

I'm using ListSelectionListener to update my JTextField (countryTxt) from selected row.
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableModel;
public class App {
JFrame frame = new JFrame();
JTable table = new JTable();
DefaultTableModel model = new DefaultTableModel(new Object[][] {},
new String[] { "Country", "City", "Street" });
JButton button = new JButton("Remove");
JTextField countryTxt = new JTextField();
int row;
public App() {
table.setModel(model);
data();
table.getSelectionModel().addListSelectionListener(
new ListSelectionListener() {
#Override
public void valueChanged(ListSelectionEvent e) {
if (!e.getValueIsAdjusting()) {
row = table.getSelectedRow();
countryTxt.setText((String) model
.getValueAt(row, 0));
}
}
});
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
model.removeRow(row);
}
});
frame.add(countryTxt,BorderLayout.NORTH);
frame.add(new JScrollPane(table), BorderLayout.CENTER);
frame.add(button, BorderLayout.SOUTH);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
frame.setLocationRelativeTo(null);
}
public void data() {
model.addRow(new String[] { "USA", "New York", "First street" });
model.addRow(new String[] { "Russia", "Moscow", "Second street" });
model.addRow(new String[] { "Japan", "Osaka", "Osaka street" });
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new App();
}
});
}
}
But when I select a row and click a button it trows me and ArrayIndexOutOfBounds exception. When I don't select a row in my table and click a button everything works fine. Obviously I can delete a row when valueChanged event is not triggered. So my question is: How to delete a row after valueChanged event is triggered. Thanks in advance.
Have a look at the javadoc of the getLeadSelectionIndex() method
Return the second index argument from the most recent call to setSelectionInterval(), addSelectionInterval() or removeSelectionInterval()
This is not what you expect. You better use the JTable#getSelectedRow() which of course still requires you to check whether it is different from -1 .
A few observations:
Selecting a row via keyboard or mouse updates the countryTxt field correctly.
You can use Control>-Tab to tab out of the table and back to your panel.
Don't use setBounds(); do use pack().
I tested your example without MigLayout, but I don't think that's relevant to your findings.
I had to track down a similar problem involving list removal a while ago. The main issue here is that the button listener's call to model.removeRow(row) was sending a valueChanged event to the model's selection listener, which would then attempt to update the text field using a nonexistent selection (i.e. a list index of -1). I've made these fixes to your code, and the relevant sections are marked with comments. This code allows items to be selected/deleted without throwing an exception.
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableModel;
public class App {
JFrame frame = new JFrame();
DefaultTableModel model = new DefaultTableModel(new Object[][] {},
new String[] { "Country", "City", "Street" });
JTable table = new JTable(model);
JButton button = new JButton("Remove");
JTextField countryTxt = new JTextField();
public App() {
data();
table.getSelectionModel().addListSelectionListener(
new ListSelectionListener() {
#Override
public void valueChanged(ListSelectionEvent e) {
if (!e.getValueIsAdjusting()) {
// get the current selected row
int i = table.getSelectedRow();
// if there is a selected row, update the text field
if(i >= 0) {
countryTxt.setText((String) model
.getValueAt(i, 0));
}
}
}
});
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
// get the current selected row
int i = table.getSelectedRow();
// if there's no selection, but there are some rows,
// we'll just delete the first row
if(i < 0 && model.getRowCount() > 0) {
i = 0;
}
// if we have a valid row to delete, do the deletion
if(i >= 0) {
countryTxt.setText("");
model.removeRow(i);
table.revalidate();
}
}
});
frame.add(countryTxt,BorderLayout.NORTH);
frame.add(new JScrollPane(table), BorderLayout.CENTER);
frame.add(button, BorderLayout.SOUTH);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
frame.setLocationRelativeTo(null);
}
public void data() {
model.addRow(new String[] { "USA", "New York", "First street" });
model.addRow(new String[] { "Russia", "Moscow", "Second street" });
model.addRow(new String[] { "Japan", "Osaka", "Osaka street" });
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new App();
}
});
}
}

Categories