1

I have to implement a way for update a jbutton text based on the user keyboard inputs, let's say that i have this gui

enter image description here

When a user clicks on one of this buttons he must be able to change the number on it by typing on the keyboard, the interaction has to be direct, so without having a text field or typing in console.

I thought to use the Reader class, but actually i'm not understanding how to do it... Searching online i found only ways for do it by console/JTextField that is not what i need. I think that this will be a pseudoImplementation:


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

public class Gui extends JFrame implements ActionListener {

    JButton btn;
    
    public static void main( String[] args ) {
        new Gui();
    }
    
    public Gui() {
        this.getContentPane().setLayout(new FlowLayout());;
        this.setTitle("Title");
        
        JButton btn = new JButton("3");
        this.getContentPane().add(btn);
        btn.addActionListener(this);
        btn.setActionCommand("listenForInput");
        btn.setSize(30, 30);s
        
        this.pack();
        this.setSize(100, 100);
        this.setVisible(true);
    }


    @Override
    public void actionPerformed(ActionEvent e) {
        String cmd = e.getActionCommand();
        if( cmd.equals("listenForInput")) {
            /* ClassThatExtendsReader reader = new ClassThatExtendsReader();
            String oldText = btn.getText();
            boolean notPressedEnter = ture;
            String newNum = "";
            while( notPressedEnter ) {
                String char = reader.nextLine();
                if( char == Enter )  notPressedEnter = false;
                newNum = newNum + char;
                btn.setText(newNum);
            }
            if( newNum is not integer ) {
                showMessage("wrong input");
                btn.setText(oldText);
            } 
            */
        } 
    }

}


So:

  1. Intialize the JFrame with a button inside
  2. add a listener to the button for detect when it is clicked
  3. when it is clicked read for the pressed chars
  4. each time that a char is entered update the button text
  5. when pressed ENTER stop the loop
    • I think that the loop inside the listener should be in a Runnable class to put it in a Thread
Mat.C
  • 1,379
  • 2
  • 21
  • 43
  • Does this answer your question? [How to detect a key press in Java](https://stackoverflow.com/questions/21969954/how-to-detect-a-key-press-in-java) – Martheen Jun 05 '21 at 10:50
  • There is no need for any sort of looping or threading. You change the *state* of the program with both button presses. The first press *activates* a KeyListener and makes sure that the listened to component has focus, and the second press *de-activates* the KeyListener. Or the KeyListener is de-activated by listening specifically for the enter key (or use an ActionListener if listening in a JTextField). – Hovercraft Full Of Eels Jun 05 '21 at 11:59

2 Answers2

3

Instead of adding an ActionListener to the JButton, you should add a KeyListener.

The KeyListener of a Component only detects that a key has been pressed when that Component has focus (in a JButton, when it has been pressed).


A simple example with two JButtons would be:

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

public class Gui extends JFrame implements KeyListener {

    private JButton firstBtn, secondBtn;

    public static void main(String[] args) {
        //All the interaction with Swing should be performed on the EDT
        SwingUtilities.invokeLater(() -> new Gui());
    }

    public Gui() {
        setLayout(new FlowLayout());;
        setTitle("Gui");

        firstBtn = new JButton("1");
        firstBtn.addKeyListener(this);

        secondBtn = new JButton("2");
        secondBtn.addKeyListener(this);

        add(firstBtn);
        add(secondBtn);

        setLocationRelativeTo(null); //Center the JFrame on the Screen
        setSize(400, 100);
        setVisible(true); //Make the JFrame Visible
    }


    @Override
    public void keyPressed(KeyEvent e) {
        if(e.getSource() instanceof JButton){
            JButton buttonFocused = ((JButton)e.getSource());
            buttonFocused.setText(buttonFocused.getText() + e.getKeyChar());
        }
    }

    @Override
    public void keyTyped(KeyEvent e) {}

    @Override
    public void keyReleased(KeyEvent e) {}
}
hexstorm
  • 318
  • 4
  • 14
3

C, I could not Find any way to do it with a Reader, but I extended normal JButtons to add that functionality

here is the code


import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

public class ChangeButtonText {
    static class SuperButton extends JButton {
        public SuperButton(String text) {
            super(text);
            // make this button focusable so that it can register keys
            setFocusable(true);
            // add a key listener(actually KeyAdapter, as we don't need all the methods)
            addKeyListener(new KeyAdapter() {
                @Override
                public void keyPressed(KeyEvent e) {
                    // get the key code from the key event
                    int keyCode = e.getKeyCode();
                    // now run a check to see if it is a number, if it is not, then there is no use
                    // in an ideal world we would use unary operators here
                    // but let's just use comparators for now
                    // 47 : key code for 0
                    // 57 : key code for 9
                    if (!(keyCode >= KeyEvent.VK_0 && keyCode <= KeyEvent.VK_9)) return;
                    // well, now we know that the keyCode is a number
                    // and so now we will set it as the text
                    char number = (char) keyCode;
                    setText(Character.toString(number));
                }
            });
        }
    }

    public static void main(String[] args) {
        // test
        JFrame frame = new JFrame("Change Button Text");
        frame.setSize(720, 480);
        frame.setVisible(true);
        JPanel panel = new JPanel();
        panel.setLayout(new GridLayout(1, 0));
        // let's create a few Super Buttons!
        SuperButton jack = new SuperButton("0");
        SuperButton superPotatoMan = new SuperButton("1");
        SuperButton sushiEater1001 = new SuperButton("7");
        // set some interesting colors
        jack.setBackground(Color.RED);
        jack.setOpaque(true);
        superPotatoMan.setBackground(Color.GREEN);
        superPotatoMan.setOpaque(true);
        sushiEater1001.setBackground(Color.WHITE);
        sushiEater1001.setOpaque(true);
        // add them to the JPanel we created
        panel.add(jack);
        panel.add(superPotatoMan);
        panel.add(sushiEater1001);
        frame.add(panel, BorderLayout.CENTER);
    }

}

I showed the output of this program in this video please watch this video

hope this was helpful :)

  • Good approach, just try to never use `setLayout(null)` and set the bounds of a JPanel manually. Swing is designed to be used with layouts, never manually adding sizes. You could use the default BorderLayout of the JFrame's content pane and add the JButton's Panel to the Center of it. – hexstorm Jun 05 '21 at 13:44
  • 1
    Thank you @hexstorm, I edited my code to match what you said – picky potato Jun 05 '21 at 13:57
  • (1+) good idea to edit the value typed. Swing components that accept user input are focusable by default. No real need to extend the JButton, you can just create an class for your KeyListner and share the same KeyListener for all buttons. – camickr Jun 05 '21 at 14:39