Question

  1. What are several ways of detecting a key stroke without the need of focusing on the component that the event was implemented? Here's my idea on this:
    Even without focusing on myComponent, upon pressing a key, the action should take part.
    ** Same question for the mousePressed event. A mouse click will be detected even when not clicking on the component.**

    myComponent.addKeyListener( new KeyAdapter() {
     @Override
     public void keyPressed( KeyEvent e ){
      // My action here         
     }
    
    });
    
  2. Upon answering Question1, can it also be done even if the application is running on background? Say I have a browser, every time I click or press a key, the given action will be executed.

I also accept suggestions to read as an answer. If your answer would be KeyBinding related, please do elaborate. All answer and comments will be greatly appreciated.


I used JNativeHooks examples here and it works perfectly fine. Any other method by just Java alone?

Was it helpful?

Solution

For the first question, regarding the KeyStroke thingy, I guess you can use KeyBinding instead of using KeyListener, that can give you the desired result, without the focus related issues of the component in question, though within the Java Dimensions.

In the example below, the focus is on the JTextField first, so if you will Press CTRL + D, then the paintAction thingy attached to the CustomPanel will work, even though the focus lies with the JTextField.

Though if you will use the setMnemonic() method for JButton, then the JButton will gain focus and will perform it's own action associated with it, which is to draw Ovals. This you can see by Pressing ALT + C, to see the desired effect. Again to perform the drawing related thingy, both the components in question don't need the focus, but still they respond to the KeyStrokes.

Here is the example code :

import java.awt.*;
import java.awt.event.*;
import java.util.Random;
import javax.swing.*;

public class SSCCE
{
    private final int WIDTH = 500;
    private final int HEIGHT = 500;
    private CustomPanel customPanel;
    private JButton circleButton;
    private JTextField tfield;
    private Random random;
    private int mode;

    private Action paintAction = new AbstractAction()
    {
        @Override
        public void actionPerformed(ActionEvent ae)
        {
            mode = random.nextInt(3);
            Color color = new Color(random.nextFloat(), random.nextFloat()
                                                        , random.nextFloat(), random.nextFloat());
            customPanel.setValues(random.nextInt(WIDTH), 
                            random.nextInt(HEIGHT), random.nextInt(WIDTH), 
                                                                    random.nextInt(HEIGHT), color, mode);
        }
    };

    private ActionListener buttonAction = new ActionListener()
    {
        @Override
        public void actionPerformed(ActionEvent ae)
        {
            Color color = new Color(random.nextFloat(), random.nextFloat()
                                                        , random.nextFloat(), random.nextFloat());
            customPanel.setValues(random.nextInt(WIDTH), 
                            random.nextInt(HEIGHT), random.nextInt(WIDTH), 
                                                                    random.nextInt(HEIGHT), color, 2);
        }
    };

    public SSCCE()
    {
        random = new Random();
    }

    private void displayGUI()
    {
        JFrame frame = new JFrame("SSCCE");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

        JPanel contentPane = new JPanel();
        contentPane.setLayout(new BorderLayout(5, 5));

        customPanel = new CustomPanel();
        customPanel.getInputMap(
            JComponent.WHEN_IN_FOCUSED_WINDOW).put(
                KeyStroke.getKeyStroke(KeyEvent.VK_D
                    , InputEvent.CTRL_DOWN_MASK), "paintAction");
        customPanel.getActionMap().put("paintAction", paintAction);

        JPanel footerPanel = new JPanel();
        circleButton = new JButton("Draw Circle");
        circleButton.setMnemonic(KeyEvent.VK_C);
        circleButton.addActionListener(buttonAction);

        tfield = new JTextField(20);
        tfield.setText("USELESS, just to get the focus for itself.");
        tfield.requestFocusInWindow();
        footerPanel.add(tfield);
        footerPanel.add(circleButton);

        contentPane.add(customPanel, BorderLayout.CENTER);
        contentPane.add(footerPanel, BorderLayout.PAGE_END);

        frame.setContentPane(contentPane);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    public static void main(String... args)
    {
        EventQueue.invokeLater(new Runnable()
        {
            @Override
            public void run()
            {
                new SSCCE().displayGUI();
            }
        });
    }
}

class CustomPanel extends JPanel
{
    private final int WIDTH = 500;
    private final int HEIGHT = 500;
    private int mode = 0;
    private Color colorShape;
    private int x = 0;
    private int y = 0;
    private int width = 0;
    private int height = 0;

    public void setValues(int x, int y, int w, int h, Color color, int mode)
    {
        this.x = x;
        this.y = y;
        this.width = w;
        this.height = h;
        this.colorShape = color;
        this.mode = mode;

        repaint();
    }

    @Override
    public Dimension getPreferredSize()
    {
        return (new Dimension(WIDTH, HEIGHT));
    }

    @Override
    protected void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        g.setColor(colorShape);
        if (mode == 1)
            g.fillRect(x, y, width, height);
        else if (mode == 2)
            g.fillOval(x, y, width, height);
    }
}

Related to mousePressed() thingy, @mKorbel, had presented the whole thingy as usual in a delightful manner.

And regarding your second question, seems like you yourself had done some homework on that. Seems like either using what you showed in your question is the workaround for catching Operating System related events and transfer that to your Java Application or Java Native Interface, I guess might also can work for this.

OTHER TIPS

  • all JComponent has method dispatchEvent,

  • you can to redirect mouse & key event from one JComponent to the another

  • for JButton to use doClick() instead

for example

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;

public class LostMouseEvent {

    private JPanel panel1;

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                new LostMouseEvent();
            }
        });
    }

    public LostMouseEvent() {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                panel1 = new JPanel() {
                    private static final long serialVersionUID = 1L;

                    @Override
                    public Dimension getPreferredSize() {
                        return new Dimension(600, 400);
                    }
                };
                JPanel panel2 = new JPanel() {
                    private static final long serialVersionUID = 1L;

                    @Override
                    public Dimension getPreferredSize() {
                        return new Dimension(500, 300);
                    }
                };
                JScrollPane pane = new JScrollPane(panel2);
                panel1.setBorder(BorderFactory.createLineBorder(Color.blue));
                panel2.setBorder(BorderFactory.createLineBorder(Color.green));
                panel1.setLayout(new CircleLayout());
                panel1.add(pane);
                frame.add(panel1);
                MouseListener rml = new RealMouseListener();
                panel1.addMouseListener(rml);
                MouseListener fml = new FakeMouseListener();
                panel2.addMouseListener(fml);
                frame.pack();
                frame.setVisible(true);

            }
        });
    }

    private class RealMouseListener extends MouseAdapter {

        @Override
        public void mousePressed(MouseEvent me) {
            System.out.println(me);
            Point point = me.getPoint();
            System.out.println(me.getX());
            System.out.println(me.getXOnScreen());
            System.out.println(me.getY());
            System.out.println(me.getYOnScreen());
        }
    }

    private class FakeMouseListener extends MouseAdapter {

        @Override
        public void mousePressed(MouseEvent me) {
            JPanel panel2 = (JPanel) me.getSource();
            MouseEvent newMe = SwingUtilities.convertMouseEvent(panel2, me, panel1);
            System.out.println(newMe.getX());
            System.out.println(newMe.getXOnScreen());
            System.out.println(newMe.getY());
            System.out.println(newMe.getYOnScreen());
            panel1.dispatchEvent(me);
        }
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top