Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java Swing - ActionListener much slower than KeyListener

Tags:

java

swing

For a program, I was using a KeyListener to add something to an ArrayList when pressing the button '1'. Objects in this list are being visualised constantly. With the KeyListener, this worked fluently, even when keeping the button pressed.

Later, I added a JMenuBar to the GUI. Adding something to the ArrayList now has an own JMenuItem with its accelerator set to the KeyStroke '1' and an ActionListener which performs the same stuff than the KeyListener before. However, the performance now is very bad. Keeping '1' pressed is going to lag extremely, it's very slow compared to the KeyListener.

Why is it so slow? Am I doing something wrong? Is there a better way?

    ...
    AL al = new AL();
    menu.add(createMenuItem("Add", KeyEvent.VK_1, al));
}

private JMenuItem createMenuItem(String text, int key, ActionListener al){
    JMenuItem menuItem = new JMenuItem(text);
    menuItem.setAccelerator(KeyStroke.getKeyStroke(key, 0));
    menuItem.addActionListener(al);
    return menuItem;
}

private class AL implements ActionListener{
    public void actionPerformed(ActionEvent e){
        int keycode = ((JMenuItem)e.getSource()).getAccelerator().getKeyCode();
        bla(keycode);
    }
}
like image 864
morrow Avatar asked Mar 21 '26 20:03

morrow


1 Answers

It looks like the slowdown is how the menu accelerators are handled. It might be L&F or even OS since when I profile it, there is no hotspot in the Java code (WindowsXP) dependent. A workaround could be to add the key binding to the root pane instead of using an menu accelerator.

Press '1' to trigger KeyListener on button (fast) Press '2' to trigger menu accelerator (slow) Press '3' to trigger KeyBinding on button (fast) Press '4' to trigger KeyBinding on root pane (fast)

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.KeyStroke;

public class TestKeySpeed {
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                final JTextArea area = new JTextArea(20, 40);
                area.setEditable(false);

                JButton button = new JButton("Just something that has focus");
                button.addKeyListener(new KeyAdapter() {
                    @Override
                    public void keyPressed(KeyEvent e) {
                        if (e.getKeyCode() == KeyEvent.VK_1) {
                            area.append("1");
                        }
                    }
                });

                AbstractAction action = new AbstractAction("Add") {
                    {
                        putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke('2'));
                    }

                    @Override
                    public void actionPerformed(ActionEvent e) {
                        area.append("2");
                    }
                };
                button.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
                        KeyStroke.getKeyStroke('3'), "add3");
                button.getActionMap().put("add3", action);

                JMenu menu = new JMenu("File");
                menu.add(action);
                JMenuBar bar = new JMenuBar();
                bar.add(menu);
                JFrame frame = new JFrame("Test");
                frame.getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
                        KeyStroke.getKeyStroke('4'), "add4");
                frame.getRootPane().getActionMap().put("add4", action);

                frame.setJMenuBar(bar);
                frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                frame.getContentPane().add(new JScrollPane(area));
                frame.getContentPane().add(button, BorderLayout.PAGE_END);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);

                button.requestFocusInWindow();
            }
        });
    }
}
like image 137
Walter Laan Avatar answered Mar 23 '26 10:03

Walter Laan