java - 如何在两个 JTable 之间同步选择、排序和过滤
问题描述
我一直在尝试获得两个在排序和过滤(RowSorter)和选择(SelectionModel)中基本上完全同步的表。为了实现这一点,我做了两个类。SelectionModel
这些是和 的包装类RowSorter
。他们基本上只是委托每个改变状态的公共方法。您可以在下面看到它们的缩短版本,其中仅包含一个被覆盖的 setter。但是,所有其他的也会被覆盖。我不想过分夸大这个问题。
(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();
}
});
}
}
推荐阅读
- ruby-on-rails - 在事务中对同一 ActiveRecord 对象进行多次更新后,after_commit 中的陈旧数据
- scala - 根据Scala中其他Seq的值对一个Seq进行排序
- macos - macOS:将 NullAudio 示例缩减为没有输出
- elasticsearch - 如何按弹性搜索响应字段设置特定顺序?
- java - android studio生成签名apk问题(找不到kotlin-compiler-27.0.1.jar)
- data-structures - 我们需要最短路径树的任何现实问题?
- shell - shell中的sql语句被pwd中的文件名替换
- r - 计算 3D 网格的旋转平移
- javascript - 元素添加到 DOM 但不可见
- php - 如何将 CryptoJS.AES.encrypt NodeJS 转换为 PHP