java - 使用 ListModel 来隐藏 TreeSet

标签 java swing jlist treeset defaultlistmodel

我的最终目标是拥有两个 JList,用户可以在它们之间来回移动元素。我使用 TreeSet 以便按字母顺序插入元素。这是一个直观的表示:

enter image description here

下面是我迄今为止的代码,位于 SSCCE 中。它大部分工作正常,但有时我会收到 ArrayOutOfBoundsException 异常。问题与程序认为选择的元素比实际选择的元素多有关。重现问题的一种方法是选择左侧的两个元素,然后按两次右侧按钮。

import java.awt.Dimension;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.*;
import javax.swing.*;

public class GUI extends JFrame {

    private GridBagLayout gridBag = new GridBagLayout();
    private static final String[] ALL_STRINGS = { "B", "A", "C" };

    private JButton leftButton = new JButton("<");
    private JButton rightButton = new JButton(">");
    private StringListModel listModel = new StringListModel(Arrays.asList(ALL_STRINGS));
    private StringListModel queueModel = new StringListModel();
    private JList<String> list = new JList<String>(listModel);
    private JList<String> queue = new JList<String>(queueModel);

    public GUI() {
        setWindowProperties();
        addComponents();
    }

    private void setWindowProperties() {
        setLayout(gridBag);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(new Dimension(300, 200));
        setTitle("JList, ListModel, and TreeSet");
        setResizable(false);
        setLocationRelativeTo(null);
    }

    private void addComponents() {
        leftButton.addActionListener(new QueueListener());
        rightButton.addActionListener(new QueueListener());

        JScrollPane listScroll = new JScrollPane(list);
        JScrollPane queueScroll = new JScrollPane(queue);

        Dimension scrollSize = new Dimension(50, 100);

        listScroll.setPreferredSize(scrollSize);
        queueScroll.setPreferredSize(scrollSize);

        add(listScroll);
        add(leftButton);
        add(rightButton);
        add(queueScroll);
    }

    private class QueueListener implements ActionListener {

        private JButton button;

        @Override
        public void actionPerformed(ActionEvent e) {
            button = (JButton) e.getSource();

            if (button.equals(leftButton)) {
                removeFromQueue();
            } else if (button.equals(rightButton)) {
                addToQueue();
            }
        }

        private void removeFromQueue() {
            List<String> Strings = queue.getSelectedValuesList();

            queueModel.removeAll(Strings);
            listModel.addAll(Strings);
        }

        private void addToQueue() {
            List<String> Strings = list.getSelectedValuesList();

            listModel.removeAll(Strings);
            queueModel.addAll(Strings);
        }
    }

    private class StringListModel extends DefaultListModel<String> {
        private TreeSet<String> model = new TreeSet<String>();

        public StringListModel() {
        }

        public StringListModel(List<String> Strings) {
            addAll(Strings);
        }

        public int getSize() {
            return model.size();
        }

        public String getElementAt(int index) {
            return (String) model.toArray()[index];
        }

        public void add(String String) {
            if (model.add(String)) {
                fireContentsChanged(this, 0, getSize());
            }
        }

        public void addAll(List<String> quets) {
            for (String String : quets) {
                model.add(String);
            }

            fireContentsChanged(this, 0, getSize());
        }

        public void clear() {
            model.clear();
            fireContentsChanged(this, 0, getSize());
        }

        public boolean contains(Object element) {
            return model.contains(element);
        }

        public String firstElement() {
            return model.first();
        }

        public Iterator iterator() {
            return model.iterator();
        }

        public String lastElement() {
            return model.last();
        }

        public void removeAll(Collection<?> elements) {
            for (Object element : elements) {
                removeElement(element);
            }

            fireContentsChanged(this, 0, getSize());
        }

        public boolean removeElement(Object element) {
            boolean removed = model.remove(element);
            if (removed) {
                fireContentsChanged(this, 0, getSize());
            }

            return removed;
        }
    }

    public static void main(String[] args) {
        new GUI().setVisible(true);
    }
}

最佳答案

这个实现似乎有效。请注意,我将 TreeSet 更改为简单的 List。缺点是插入/删除会慢一些,但至少我可以触发正确的事件。鉴于它是一个 UI 小部件,我认为这是可以接受的,因为底层模型永远不会很大(否则 UI 将变得不可用)。

我从 AbstractListModel 扩展,但您也可以从 DefaultListModel 扩展。在这种情况下,您可能希望确保 addElement 方法计算正确的索引并调用 super.add(calculatedIndex, element)

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.*;
import javax.swing.*;

public class GUI extends JFrame {

  private GridBagLayout gridBag = new GridBagLayout();
  private static final String[] ALL_STRINGS = {"B", "A", "C"};

  private JButton leftButton = new JButton( "<" );
  private JButton rightButton = new JButton( ">" );
  private StringListModel listModel = new StringListModel( Arrays.asList( ALL_STRINGS ) );
  private StringListModel queueModel = new StringListModel();
  private JList<String> list = new JList<String>( listModel );
  private JList<String> queue = new JList<String>( queueModel );

  public GUI() {
    setWindowProperties();
    addComponents();
  }

  private void setWindowProperties() {
    setLayout( gridBag );
    setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
    setSize( new Dimension( 300, 200 ) );
    setTitle( "JList, ListModel, and TreeSet" );
    setResizable( false );
    setLocationRelativeTo( null );
  }

  private void addComponents() {
    leftButton.addActionListener( new QueueListener() );
    rightButton.addActionListener( new QueueListener() );

    JScrollPane listScroll = new JScrollPane( list );
    JScrollPane queueScroll = new JScrollPane( queue );

    Dimension scrollSize = new Dimension( 50, 100 );

    listScroll.setPreferredSize( scrollSize );
    queueScroll.setPreferredSize( scrollSize );

    add( listScroll );
    add( leftButton );
    add( rightButton );
    add( queueScroll );
  }

  private class QueueListener implements ActionListener {

    private JButton button;

    @Override
    public void actionPerformed( ActionEvent e ) {
      button = ( JButton ) e.getSource();

      if ( button.equals( leftButton ) ) {
        removeFromQueue();
      }
      else if ( button.equals( rightButton ) ) {
        addToQueue();
      }
    }

    private void removeFromQueue() {
      List<String> Strings = queue.getSelectedValuesList();

      queueModel.removeAll( Strings );
      listModel.addAll( Strings );
    }

    private void addToQueue() {
      List<String> Strings = list.getSelectedValuesList();

      listModel.removeAll( Strings );
      queueModel.addAll( Strings );
    }
  }

  private class StringListModel extends AbstractListModel<String> {
    private List<String> model = new ArrayList<>();

    public StringListModel() {
    }

    public StringListModel( List<String> strings ) {
      addAll( strings );
    }

    @Override
    public int getSize() {
      return model.size();
    }

    @Override
    public String getElementAt( int index ) {
      return model.toArray( new String[model.size()])[index];
    }

    public void addAll( Collection<String> strings ){
      for ( String string : strings ) {
        add( string );
      }
    }
    public void add( String string ){
      int index = calculateIndex( string );
      model.add( index, string );
      fireIntervalAdded( this, index, index );
    }

    public void remove( String string ){
      int index = model.indexOf( string );
      if ( index != -1 ){
        model.remove( index );
        fireIntervalRemoved( this, index, index );
      }
    }

    public void removeAll( Collection<String> strings ){
      for ( String next : strings ) {
        remove( next );
      }
    }
    private int calculateIndex( String input ){
      if ( model.size() == 0 ){
        return 0;
      }
      int index = 0;
      while ( model.get( index ).compareTo( input ) <=0 ){
        index++;
        if ( index == model.size() ){
          return index;
        }
      }
      return index;
    }
  }

  public static void main( String[] args ) {
    EventQueue.invokeLater( new Runnable() {
      @Override
      public void run() {
        new GUI().setVisible( true );
      }
    } );

  }
}

关于java - 使用 ListModel 来隐藏 TreeSet,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18560930/

相关文章:

java - 将计时器添加到 Java Swing

java - 我的 Jlabel 没有显示

java - 修改 DefaultListModel 中的元素不更新模型

java - 扫描仪在使用 next() 或 nextFoo() 后跳过 nextLine()?

java - 可序列化是如何工作的?

java - 使用 Spring Boot 应用程序作为 gradle 的依赖项

java - 将单个字符串与 JList<String> 一起用于操作

java - 如何从异步回调创建多个 Flux

java - java中组合框的自动刷新

java - JList - locationToIndex() 始终返回线程中鼠标位置的相同索引