首页 > 解决方案 > 如何在两个 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 个选定行中,只有索引01将被保留。我不仅想保留 10 个中已过滤的 5 个,还希望保留其他(现在隐藏)的,并且在再次更改过滤器后仍然可以看到选择。这样做的原因是,我们有一个模仿树的 JTable。如果我们折叠该树中的一个项目并预先选择了父项及其子项,我们希望它们在再次展开父项后仍被选中。我认为这是可能的,因为这在使用镜像选择模型时似乎有效,但我不确定为什么。

我不太确定我想要实现的目标是否可能,如果是的话,我不知道我是否采取了正确的方法。

以下是所有文件作为要点:https ://gist.github.com/Bios-Marcel/ada8781c79b7f13b3e0b3d8486462913

如果有用于同步表的现有事物(库),我也愿意研究它。

标签: javaswingjtablefilteringselection

解决方案


一般来说,一个模型可以被多个视图共享。

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();
            }
        });
    }
}

推荐阅读