JList选择随机跳转到上一个索引

JList selection randomly jumps to previous index

我有一个 JList,我希望能够在其中导航到不同的单元格,键入文本,然后按 "enter" 提交更改。问题是当我更改几个单元格然后通过向上和向下键导航并尝试在当前选定的单元格中键入时,选择以某种方式跳转到以前填充的单元格。我已将我的代码缩减到我认为能重现该问题的最低限度:

package listtest;

import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.DefaultListModel;
import javax.swing.JDialog;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.UIManager;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

public class JListSelection{

    static int selectedIndex;
    static JList<String> serials;
    static DefaultListModel<String> model;
    static private JPopupMenu editPopup;
    static private JTextField editTextField;

    public static void main(String[] args) {
        JPanel pan = new JPanel(null);
        selectedIndex = 0;
        serials = new JList<String>();

        model = new DefaultListModel<String>();
        serials = new JList<String>(model);
        for(int i = 0; i < 19; i++) {
            model.addElement(" ");
        }
        serials.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        serials.addListSelectionListener(new ListSelectionListener() {
            @Override
            public void valueChanged(ListSelectionEvent e) {
                selectedIndex = serials.getSelectedIndex();
                System.out.println("in listener: " + serials.getSelectedIndex());
            }
        });
        serials.addKeyListener(new KeyAdapter() {
            public void keyPressed(KeyEvent e) {
                System.out.println("In keypressed: " + e.getKeyCode() + " " + serials.getSelectedIndex());
            }
            public void keyReleased(KeyEvent e) {
                int code = e.getKeyCode();
                switch( code ){ 
                    case KeyEvent.VK_UP:
                        System.out.println("UP " + serials.getSelectedIndex());
                        break;
                    case KeyEvent.VK_DOWN:
                        System.out.println("DOWN " + serials.getSelectedIndex());
                        break;
                }
                if(e.getKeyCode() >= KeyEvent.VK_A && e.getKeyCode() <= KeyEvent.VK_Z
        || e.getKeyCode() >= KeyEvent.VK_0 && e.getKeyCode() <= KeyEvent.VK_9) {

                    System.out.println(selectedIndex + " " + serials.getSelectedIndex());
                    Rectangle r = serials.getCellBounds(selectedIndex, selectedIndex);

                    if (editPopup == null) {
                        createEditPopup();
                    }

                    editPopup.setPreferredSize(new Dimension(r.width, r.height));
                    editPopup.show(serials, r.x, r.y);

                    editTextField.setText(
                        serials.getSelectedValue().toString().equals(" ") ? 
                        e.getKeyChar()+"" : serials.getSelectedValue().toString());
                        editTextField.requestFocusInWindow(); 
                }
            }
        });

        serials.setBounds(0, 0, 200, 800);
        pan.add(serials);
        JDialog di = new JDialog();
        di.setContentPane(pan);
        di.pack();
        di.setLocationRelativeTo(null);
        di.setSize(300, 400);
        di.setVisible(true);
    }

    private static void createEditPopup(){
        editTextField = new JTextField();

        editTextField.setBorder(
            UIManager.getBorder("List.focusCellHighlightBorder"));
        editTextField.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e){
                DefaultListModel<String> model = (DefaultListModel<String>) 
                    serials.getModel();
                model.set(selectedIndex, editTextField.getText());
                editPopup.setVisible(false);
            }
        });

        editPopup = new JPopupMenu();
        editPopup.setBorder(new EmptyBorder(0, 0, 0, 0));
        editPopup.add(editTextField);
    }
}

如果您 运行 代码并首先选择一个单元格并键入内容,然后按 Enter 键,它就会正常工作。如果您随后使用向下箭头键在某个时候键入其他几个单元格,则选择将跳转到先前选择的单元格,我无法找出任何方法来查看导致此跳转的原因,更不用说阻止它了。

好吧,我通过添加一个 int 来解决问题,该 int 在我按下某个键并添加时跟踪所选索引的内容

if(Math.abs(selectedIndex- keyPressedIndex) != 1) {
        serials.setSelectedIndex(keyPressedIndex);
        selectedIndex = serials.getSelectedIndex();
}

ListSelectionListener。仍然不知道为什么会这样

您遇到了 JList 的一个特征。发生的事情是当您键入每个字符时,JList 试图滚动到以您键入的字母开头的条目。 因此,如果您输入 "Joe" 和 "Dave",然后尝试输入 "Jerry",则 JList 将 select "Joe" 行。

参见:

遵循该问题的技巧:

// Add these lines just before your first "addKeyListener"
for (KeyListener lsnr : serials.getKeyListeners()) {
  if(lsnr.getClass().getSimpleName().equals("Handler")){
    serials.removeKeyListener(lsnr);
  }
}

在您的代码示例中,这些行位于第 47 行之前。 杀死 JList.

的 autoselect 性质有点粗略

我们要删除的侦听器由 BasicListUIinstallListeners() 方法中添加到 JList,如 list.addKeyListener(getHandler())。查阅 BasicListUI 的来源。 getHandler() 返回的 class 是一个 catch-all 监听器,它实现了几个不同的监听器接口,包括 KeyListener,这就是实现 autoselect 行为的地方。

getSimpleName() 的奇怪用法来确定 class 名称是必要的,因为 HandlerBasicListUI 中的私有 class,所以我们不能使用 instanceof。 不用说,这些恶作剧的代码有些脆弱。如果您希望使用这种方法,请确保您对其进行了很好的记录,并准备在迁移到未来的 Java 版本时对其进行修复。

如果您发现自己在设计这样一个组件时遇到困难,那么您可能使用了错误的组件。也许你最好使用 single-column JTable.