Выбор JList случайным образом переходит к предыдущему индексу - PullRequest
0 голосов
/ 13 июня 2019

У меня есть JList, в котором я хочу иметь возможность перемещаться по разным ячейкам, вводить текст, затем нажимать «ввод», чтобы зафиксировать изменение. Проблема в том, что когда я изменяю несколько ячеек, а затем перемещаюсь с помощью клавиш «вверх» и «вниз» и пытаюсь набрать текущую выбранную ячейку, выделение каким-то образом переходит на ранее заполненную ячейку. Я сократил свой код до того, что я считаю минимумом, чтобы воспроизвести проблему:

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

Если вы запустите код и начнете с выбора ячейки и ввода чего-то, а затем нажмите клавишу ввода, все будет работать так, как должно. Если вы затем используете клавиши со стрелками вниз, чтобы набрать несколько других ячеек в какой-то момент, выделение перейдет к ранее выбранной ячейке, и я не могу найти какой-либо способ узнать, что вызывает этот скачок, не говоря уже о том, чтобы предотвратить его.

Ответы [ 2 ]

0 голосов
/ 14 июня 2019

Вы столкнулись с особенностью JList. Когда вы вводите каждый символ, JList пытается прокрутить до записи, которая начинается с введенной вами буквы. Таким образом, если вы ввели «Джо» и «Дейв», а затем попытались ввести «Джерри», JList выберет строку «Джо».

См .: Отключить выбор букв в JList

Следуя методике из этого вопроса:

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

Эти строки идут перед строкой 47 в вашем примере кода. Это немного грубый способ убрать характер автоматического выбора JList.

Слушатель, которого мы удаляем, добавляется к JList на BasicListUI в методе installListeners(), как list.addKeyListener(getHandler()). Обратитесь к источнику для BasicListUI. Класс, возвращаемый getHandler(), является универсальным слушателем, который реализует несколько различных интерфейсов слушателя, включая KeyListener, и именно здесь реализовано поведение автоматического выбора.

Нечетное использование getSimpleName() для определения имени класса необходимо, потому что Handler является закрытым классом в BasicListUI, поэтому мы не можем использовать instanceof. Излишне говорить, что такого рода махинации создают довольно хрупкий код. Если вы хотите использовать этот подход, убедитесь, что вы хорошо его документировали и подготовились к его исправлению при переходе на будущие версии Java.

Если вы столкнулись с дизайном такого компонента, возможно, вы используете не тот компонент. Возможно, вам лучше использовать одну колонку JTable.

0 голосов
/ 14 июня 2019

Ну, я исправил проблему, добавив int, который отслеживает, какой индекс выбран, когда я нажимаю клавишу, и добавил

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

до ListSelectionListener. Все еще не знаю, почему это происходит, хотя

...