我一直在尝试获取两个在排序和筛选(RowSorter)和选择(SelectionModel)中基本上完全同步的表。为了实现这一目标,我做了两节课。这些是SelectionModel
和RowSorter
的包装器类。他们基本上只是委派了每个改变状态的公共方法。在下面,您可以看到它们的简化版本,其中仅包含一个覆盖的二传手。但是,其他所有内容也将被覆盖。我不想夸大这个问题。
MirrorableRowSorter
(短路):
public class MirrorableRowSorter<M extends TableModel> extends TableRowSorter<M>
{
private MirrorableRowSorter<M> delegate;
/**
* @param delegate the delegate to set
*/
public void setDelegate( MirrorableRowSorter<M> delegate )
{
this.delegate = delegate;
}
@Override
public void setSortKeys( List<? extends SortKey> sortKeys )
{
if ( delegate != null )
{
delegate.setSortKeysNonDelegating( sortKeys );
}
super.setSortKeys( sortKeys );
}
private void setSortKeysNonDelegating( List<? extends SortKey> sortKeys )
{
super.setSortKeys( sortKeys );
}
//... other methods
}
MirrorableSelectionModel
(短路):
public class MirrorableSelectionModel extends DefaultListSelectionModel
{
private MirrorableSelectionModel delegate = null;
/**
* @param delegate the delegate to set
*/
public void setDelegate( final MirrorableSelectionModel delegate )
{
this.delegate = delegate;
}
@Override
public void setSelectionInterval( final int index0, final int index1 )
{
if ( delegate != null )
{
delegate.setSelectionIntervalNonDelegating( index0, index1 );
}
super.setSelectionInterval( index0, index1 );
}
private void setSelectionIntervalNonDelegating( final int index0, final int index1 )
{
super.setSelectionInterval( index0, index1 );
}
//... other methods
总是有一个委派和一个非委派的方法。我这样做是为了避免两个对象之间发生乒乓。
然后我写了一个带有两个表的小例子:
import java.awt.BorderLayout;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.RowFilter;
public class ExampleMirroredTables
{
public static void main( String[] args )
{
JFrame frame = new JFrame();
frame.setLayout( new BorderLayout() );
final JTable tableOne = new JTable( new Object[][]{
new Object[]{ "1", "2", "3", "4", "5", "6", "7", "8", "9", "10" },
new Object[]{ "2", "2", "3", "4", "5", "6", "7", "8", "9", "10" },
new Object[]{ "3", "2", "3", "4", "5", "6", "7", "8", "9", "10" },
new Object[]{ "4", "2", "3", "4", "5", "6", "7", "8", "9", "10" },
new Object[]{ "5", "2", "3", "4", "5", "6", "7", "8", "9", "10" },
new Object[]{ "6", "2", "3", "4", "5", "6", "7", "8", "9", "10" },
new Object[]{ "7", "2", "3", "4", "5", "6", "7", "8", "9", "10" },
new Object[]{ "8", "2", "3", "4", "5", "6", "7", "8", "9", "10" },
new Object[]{ "9", "2", "3", "4", "5", "6", "7", "8", "9", "10" },
new Object[]{ "10", "2", "3", "4", "5", "6", "7", "8", "9", "10" }
}, new Object[]{ "1", "2", "3", "4", "5", "6", "7", "8", "9", "10" } );
final JTable tableTwo = new JTable( new Object[][]{
new Object[]{ "1", "2", "3", "4", "5", "6", "7", "8", "9", "10" },
new Object[]{ "2", "2", "3", "4", "5", "6", "7", "8", "9", "10" },
new Object[]{ "3", "2", "3", "4", "5", "6", "7", "8", "9", "10" },
new Object[]{ "4", "2", "3", "4", "5", "6", "7", "8", "9", "10" },
new Object[]{ "5", "2", "3", "4", "5", "6", "7", "8", "9", "10" },
new Object[]{ "6", "2", "3", "4", "5", "6", "7", "8", "9", "10" },
new Object[]{ "7", "2", "3", "4", "5", "6", "7", "8", "9", "10" },
new Object[]{ "8", "2", "3", "4", "5", "6", "7", "8", "9", "10" },
new Object[]{ "9", "2", "3", "4", "5", "6", "7", "8", "9", "10" },
new Object[]{ "10", "2", "3", "4", "5", "6", "7", "8", "9", "10" }
}, new Object[]{ "1", "2", "3", "4", "5", "6", "7", "8", "9", "10" } );
final MirrorableSelectionModel mirrorModelOne = new MirrorableSelectionModel();
final MirrorableSelectionModel mirrorModelTwo = new MirrorableSelectionModel();
mirrorModelOne.setDelegate( mirrorModelTwo );
mirrorModelTwo.setDelegate( mirrorModelOne );
tableOne.setSelectionModel( mirrorModelOne );
tableTwo.setSelectionModel( mirrorModelTwo );
final MirrorableRowSorter mirrorSorterOne = new MirrorableRowSorter();
final MirrorableRowSorter mirrorSorterTwo = new MirrorableRowSorter();
mirrorSorterOne.setDelegate( mirrorSorterTwo );
mirrorSorterTwo.setDelegate( mirrorSorterOne );
mirrorSorterOne.setModel( tableOne.getModel() );
mirrorSorterTwo.setModel( tableTwo.getModel() );
tableOne.setRowSorter( mirrorSorterOne );
tableTwo.setRowSorter( mirrorSorterTwo );
JButton filterButton = new JButton( "Filter randomize" );
AtomicBoolean even = new AtomicBoolean( true );
filterButton.addActionListener( __ ->
{
even.set( !even.get() );
System.out.println( Arrays.toString( tableOne.getSelectedRows() ) );
mirrorSorterTwo.setRowFilter( new RowFilter()
{
@Override
public boolean include( Entry entry )
{
final int moduloResult = ((Integer) entry.getIdentifier()) % 2;
return even.get() ? moduloResult == 0 : moduloResult != 0;
}
} );
System.out.println( Arrays.toString( tableOne.getSelectedRows() ) );
} );
frame.add( filterButton, BorderLayout.NORTH );
frame.add( tableOne, BorderLayout.WEST );
frame.add( new JLabel( " THE MIRROR " ), BorderLayout.CENTER );
frame.add( tableTwo, BorderLayout.EAST );
frame.pack();
frame.setLocationRelativeTo( null );
frame.setVisible( true );
}
}
您将可以看到是否使用了选择,它将始终完美地反映出来。但是,如果现在在选择所有行(Ctrl + A)之后单击顶部的筛选器按钮,则在所选的10行中,将仅保留索引0
和1
。我不仅要保留已选择的10个过滤器中的5个,还要保留其他(现已隐藏)的过滤器,并在再次更改过滤器后仍然看到选择。这样做的原因是,我们有一个模仿树的JTable。如果我们在该树中折叠一个项目并事先选择了父级及其子级,我们希望在再次扩展父级后仍选择它们。我认为这是可能的,因为在使用镜像选择模型时这似乎可行,但是我不确定为什么。
我不确定我要实现的目标是否完全可能,如果可以,我不知道我是否采取了正确的方法。
这里是所有文件的摘要:https://gist.github.com/Bios-Marcel/ada8781c79b7f13b3e0b3d8486462913
[如果有一个用于同步表的东西(库),我也愿意研究一下,如果能得到任何提示,那将是很好的。
如果能得到任何提示,那就太好了。
通常,一个模型可以被多个视图共享。
tableTwo.setSelectionModel( tableOne.getSelectionModel() );
不确定每个模型中的行数不同时会发生什么。
但是,由于您正在使用多个TableModel,因此不能将相同的方法用于排序/过滤。
因此,也许另一种方法是使用一个具有20列的TableModel。然后,您将拥有两个具有2个不同模型视图的表。
第一个表将显示前10列,第二个表将显示后10列。
基本概念证明:
import java.awt.*;
import java.util.*;
import javax.swing.*;
import javax.swing.table.*;
public class TableSync extends JPanel
{
public TableSync()
{
setLayout( new BorderLayout() );
String[] columnNames = {"Date", "String", "Integer", "Boolean"};
Object[][] data =
{
{new Date(), "A", Integer.valueOf(1), Boolean.TRUE },
{new Date(), "B", Integer.valueOf(2), Boolean.FALSE},
{new Date(), "C", Integer.valueOf(12), Boolean.TRUE },
{new Date(), "D", Integer.valueOf(5124), Boolean.FALSE}
};
DefaultTableModel model = createTableModel(data, columnNames);
JTable table1 = new JTable( model );
table1.removeColumn( table1.getColumnModel().getColumn(2) );
table1.removeColumn( table1.getColumnModel().getColumn(2) );
JTable table2 = new JTable( model );
table2.removeColumn( table2.getColumnModel().getColumn(0) );
table2.removeColumn( table2.getColumnModel().getColumn(0) );
table2.setSelectionModel( table1.getSelectionModel() );
table1.setAutoCreateRowSorter(true);
table2.setRowSorter( table1.getRowSorter() );
table1.setPreferredScrollableViewportSize(table1.getPreferredSize());
add(new JScrollPane(table1), BorderLayout.LINE_START);
table2.setPreferredScrollableViewportSize(table2.getPreferredSize());
add(new JScrollPane(table2), BorderLayout.LINE_END);
}
private DefaultTableModel createTableModel(Object[][] data, String[] columnNames)
{
DefaultTableModel model = new DefaultTableModel(data, columnNames)
{
// Returning the Class of each column will allow different
// renderers and editors to be used based on Class
@Override
public Class getColumnClass(int column)
{
for (int row = 0; row < getRowCount(); row++)
{
Object o = getValueAt(row, column);
if (o != null)
return o.getClass();
}
return Object.class;
}
};
return model;
}
private static void createAndShowUI()
{
JFrame frame = new JFrame("TableSync");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( new TableSync() );
frame.pack();
frame.setLocationRelativeTo( null );
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}