The TableRowSorter
class provides the setComparator(int column, Comparator<?> comparator)
method. That should do exactly what you need. Provide a Comparator
that orders File
s by getName()
. Added to what you already have, I think that gives you what you are looking for.
How to display parts of text in jtable
سؤال
I have an application similar to TableFilterDemo which produces a db with some fields similar to this
{new File("/path/with/names/included/here.file"), "foo", "bar", new Integer(5), new Double(0.7)},
The path might be very long so I have implemented a cell renderer which only shows the file name, but sets the full path as a tooltip.
table.getColumnModel().getColumn(0).setCellRenderer(new DefaultTableCellRenderer(){
@Override
protected void setValue(Object value) {
Object result = value;
if ((value != null) && (value instanceof File)) {
File file = (File) value;
result = file.getName();
super.setValue(result);
super.setToolTipText(file.toString());
}
}
});
The problem now is that sorting the file column still sorts by full path, not just the filename. I´m guessing I need to implement some sort of comparator, but I don't know where to start.
EDIT: To be clear, I have made a gist to show what I mean.
I want to be able to show just the filename (here.file, there.file) in the cells instead of the full path. When sorting the file column, also sort by the filename instead of the full path. The full path should be a tooltip when the mouse pointer is over the respective file cell. When filtering the table, I would like to have the full path included in the search as well.
EDIT3: SSCCE with solution
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableRowSorter;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.io.File;
import javax.swing.table.DefaultTableCellRenderer;
public class TableFilterDemo extends JPanel {
private JTable table;
private JTextField filterText;
private TableRowSorter<MyTableModel> sorter;
public TableFilterDemo() {
setLayout(new BorderLayout());
// Comparator for the file column, sorts by name instead of full path
Comparator<File> fileComparator = new Comparator<File>() {
@Override
public int compare(File file1, File file2) {
return file1.getName().compareTo(file2.getName());
}
};
// Create a table with a sorter.
MyTableModel model = new MyTableModel();
sorter = new TableRowSorter<MyTableModel>(model);
sorter.setComparator(0, fileComparator);
table = new JTable(model);
table.setRowSorter(sorter);
table.setPreferredScrollableViewportSize(new Dimension(500, 70));
table.setFillsViewportHeight(true);
// For the purposes of this example, better to have a single
// selection.
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
table.getColumnModel().getColumn(0).setCellRenderer(new DefaultTableCellRenderer() {
@Override
protected void setValue(Object value) {
Object result = value;
if ((value != null) && (value instanceof File)) {
File file = (File) value;
result = file.getName();
super.setValue(result);
super.setToolTipText(file.toString());
}
}
});
// Add the scroll pane to this panel.
add(
new JScrollPane(table), BorderLayout.CENTER);
// Create a separate form for filterText and statusText
filterText = new JTextField();
// Whenever filterText changes, invoke newFilter.
filterText.getDocument().addDocumentListener(new DocumentListener() {
public void changedUpdate(DocumentEvent e) {
newFilter();
}
public void insertUpdate(DocumentEvent e) {
newFilter();
}
public void removeUpdate(DocumentEvent e) {
newFilter();
}
});
add(filterText, BorderLayout.SOUTH);
}
/**
* Update the row filter regular expression from the expression in the text
* box.
*/
private void newFilter() {
RowFilter<MyTableModel, Object> rf = null;
// If current expression doesn't parse, don't update.
try {
rf = RowFilter.regexFilter("(?i)" + filterText.getText());
} catch (java.util.regex.PatternSyntaxException e) {
return;
}
sorter.setRowFilter(rf);
}
class MyTableModel extends AbstractTableModel {
private String[] columnNames = {"File", "a", "b", "c", "d"};
private Object[][] data = {
{new File("/long/path/with/names/included/2012-10-11-001.file"),
"foo", "bar", new Integer(5), new Double(0.7)},
{new File("/path/full/of/text/2012-10-11-002.file"),
"baz", "bah", new Integer(2), new Double(1.7)},
{new File("/a/long/path/full/of/other/text/2012-10-11-003.file"),
"baz", "bah", new Integer(2), new Double(1.7)},};
public int getColumnCount() {
return columnNames.length;
}
public int getRowCount() {
return data.length;
}
public String getColumnName(int col) {
return columnNames[col];
}
public Object getValueAt(int row, int col) {
return data[row][col];
}
public Class getColumnClass(int c) {
return getValueAt(0, c).getClass();
}
/*
* Don't need to implement this method unless your table's data can
* change.
*/
public void setValueAt(Object value, int row, int col) {
data[row][col] = value;
fireTableCellUpdated(row, col);
}
}
/**
* Create the GUI and show it. For thread safety, this method should be
* invoked from the event-dispatching thread.
*/
private static void createAndShowGUI() {
// Create and set up the window.
JFrame frame = new JFrame("TableFilterDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Create and set up the content pane.
TableFilterDemo newContentPane = new TableFilterDemo();
newContentPane.setOpaque(true); // content panes must be opaque
frame.setContentPane(newContentPane);
// Display the window.
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
// Schedule a job for the event-dispatching thread:
// creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
المحلول
نصائح أخرى
The example below shows how to display just the file name with the full path as a tool tip. You can incorporate the full path in your filter as shown here
Addendum: @Paul Hicks asks, "Can you point out the bits that are new and important?"
The essential change is to make the model, rather than the renderer, return the value that you want to display. Then let the view query the model for the detail needed to render the tool tip.
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableRowSorter;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.io.File;
/** @see https://stackoverflow.com/a/23450990/230513 */
public class TableFilterDemo extends JPanel {
private JTable table;
private JTextField filterText;
private TableRowSorter<MyTableModel> sorter;
public TableFilterDemo() {
setLayout(new BorderLayout());
// Create a table with a sorter.
MyTableModel model = new MyTableModel();
sorter = new TableRowSorter<MyTableModel>(model);
table = new JTable(model) {
@Override
public String getToolTipText(MouseEvent e) {
Point p = e.getPoint();
int row = convertRowIndexToModel(rowAtPoint(p));
MyTableModel model = (MyTableModel) table.getModel();
return model.getPath(row);
}
};
table.setRowSorter(sorter);
table.setPreferredScrollableViewportSize(new Dimension(500, 200));
table.setFillsViewportHeight(true);
// For the purposes of this example, better to have a single
// selection.
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
// Add the scroll pane to this panel.
add(
new JScrollPane(table), BorderLayout.CENTER);
// Create a separate form for filterText and statusText
filterText = new JTextField();
// Whenever filterText changes, invoke newFilter.
filterText.getDocument().addDocumentListener(new DocumentListener() {
public void changedUpdate(DocumentEvent e) {
newFilter();
}
public void insertUpdate(DocumentEvent e) {
newFilter();
}
public void removeUpdate(DocumentEvent e) {
newFilter();
}
});
add(filterText, BorderLayout.SOUTH);
}
/**
* Update the row filter regular expression from the expression in the text
* box.
*/
private void newFilter() {
RowFilter<MyTableModel, Object> rf = null;
// If current expression doesn't parse, don't update.
try {
rf = RowFilter.regexFilter("(?i)" + filterText.getText());
} catch (java.util.regex.PatternSyntaxException e) {
return;
}
sorter.setRowFilter(rf);
}
class MyTableModel extends AbstractTableModel {
String path = System.getProperty("user.dir");
File[] data = new File(path).listFiles();
private String getPath(int row) {
return data[row].getPath();
}
@Override
public int getColumnCount() {
return 1;
}
@Override
public int getRowCount() {
return data.length;
}
@Override
public String getColumnName(int col) {
return "path";
}
@Override
public Object getValueAt(int row, int col) {
return data[row].getName();
}
@Override
public Class getColumnClass(int c) {
return getValueAt(0, c).getClass();
}
}
/**
* Create the GUI and show it. For thread safety, this method should be
* invoked from the event-dispatching thread.
*/
private static void createAndShowGUI() {
// Create and set up the window.
JFrame frame = new JFrame("TableFilterDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Create and set up the content pane.
TableFilterDemo newContentPane = new TableFilterDemo();
newContentPane.setOpaque(true); // content panes must be opaque
frame.setContentPane(newContentPane);
// Display the window.
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
// Schedule a job for the event-dispatching thread:
// creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}