JPanel doesn't react to KeyBindings - java

I am working on an application that requires a JPanel to react to the escape key being pressed.
I am pretty sure i used the right method for registering keybindings to a component but clearly i am still doing something wrong. This is the code responsible for registering end reacting to the said keybinding:
private void initializeKeyBindings() {
Action a = new AbstractAction() {
private static final long serialVersionUID = 1L;
#Override public void actionPerformed(ActionEvent e) {
menu.setVisible(true);
System.out.println("Herp");
}
};
this.getInputMap().put(KeyStroke.getKeyStroke("ESCAPE"), "ESCAPE");
this.getActionMap().put("ESCAPE", a);
}
This method is called in the constructor of my JPanel after all other components are initialized. I've tried debugging it and i found that the action itself is registered in the JPanel but the code in the actionPerformed() method is never reached. I suspect there might be a problem with this JPanel not having focus since i am using a CardLayout in the overlying JFrame. I sincerely hope anyone can help me with this as it is holding up my progress very badly.

you miss there
xxx.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(...)

Related

My JDialog sometimes receives a redundant keystroke from the calling app ? (code provided)

I develop a complex music application in java (8) based on the Netbeans RCP 8.2 and I have a strange problem that occurs randomly.
I have a JFrame with a panel which contains many JComponents. I use the panel's InputMap/ActionMap to capture "a","b",...,"g" key presses and call an action.
The action retrieves the key char then shows a JDialog which contains a JTextField used to edit some text data.
Before showing the dialog with dialog.setVisible(true), the action calls dialog.prepare(char key) so that the JDialog can initialize itself before being shown. Actually dialog.prepare(char key) only appends the passed char (converted to uppercase) in the JTextField.
This works most of the time: I press for example "c" in the JFrame then the JDialog appears with "C" at the end of the JTextField.
But sometimes, maybe 1/20 times, I get "Cc" at the end of the JTextfield !
It's like the original key press event (which comes from a JComponent in the JFrame's panel and was processed using InputMap/ActionMap) was also redundantly processed by the JDialog.
I verified that it's not a keyboard hardware problem. I reproduced the issue on a second computer with Win8 (mine is Win10).
I tried without success 1/ using a KeyListener instead of InputMap/ActionMap
and 2/ use java.awt.EventQueue.invokeLater() to append the key char in the JTextField.
I created a small independant app (see below) to reproduce the issue and facilitate debugging...but this small app works fine, I could not reproduce the issue :-( Then I compared again with my real app code, and it's really the same code, except the real app is a complete Netbeans RCP application.
So could it be that Netbeans RCP impacts the way Swing handle key events ? It looks odd to me...
I'm lost, any hint/suggested test would be greatly appreciated !
/**
* Try to reproduce double key problem... Failed because this works OK !! :-(
*/
public class PbKeyDouble extends JFrame {
MyDialog dialog;
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
PbKeyDouble o = new PbKeyDouble();
o.setVisible(true);
}
});
}
public PbKeyDouble() {
// GUI INITIALIZATION
// Add a basic panel
JPanel panel = new JPanel();
getContentPane().add(panel);
panel.setPreferredSize(new Dimension(300, 200));
JButton button = new JButton("BUTTON");
panel.add(button);
// Button not used, it's only to simulate the real app where a component in the panel has the focus
button.requestFocusInWindow();
// If "A" or "B" key is pressed anywhere, MyAction.actionPerformed() will be called
panel.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke("A"), "MyAction");
panel.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke("B"), "MyAction");
panel.getActionMap().put("MyAction", new MyAction());
// Prepare JFrame
setDefaultCloseOperation(EXIT_ON_CLOSE);
pack();
setLocationRelativeTo(null);
}
private class MyAction extends AbstractAction {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("EDT? " + SwingUtilities.isEventDispatchThread()); // Always prints TRUE
if (dialog == null) {
dialog = new MyDialog();
}
// Retrieve the key used to trigger the action
char c = e.getActionCommand().charAt(0);
// Prepare the dialog (insert the char)
dialog.prepare(c);
// Show dialog
dialog.setVisible(true);
}
}
private class MyDialog extends JDialog {
JTextField textfield;
/**
* A simple dialog with just a textfield.
*/
public MyDialog() {
textfield = new JTextField("Hello");
textfield.setColumns(100);
getContentPane().add(textfield);
pack();
setLocationRelativeTo(null);
}
/**
* Append the key (uppercased) at the end of the textfield
*/
public void prepare(char c) {
String text = textfield.getText();
textfield.setText(text + " " + Character.toUpperCase(c));
}
/**
* Overridden to add a global key binding on ESC key to exit the dialog.
* <p>
* This is only to facilitate the test where I need to try many times the process pressing "a" ESC "a" ESC etc.
*
* #return
*/
#Override
protected JRootPane createRootPane() {
JRootPane contentPane = new JRootPane();
contentPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke("ESCAPE"), "actionCancel");
contentPane.getActionMap().put("actionCancel", new AbstractAction("Cancel") {
#Override
public void actionPerformed(ActionEvent e) {
setVisible(false);
}
});
return contentPane;
}
}
}
I found the problem, though it is still not logical to me. Explanation welcome !
All Swing components should be created and modified on the Event Dispatch Thread (EDT).
Yes, it was the case in my code and still it was not working...
To try to understand what happens I attached a KeyListener to the JTextField of the JDialog.
I found out that when it was working (key not doubled) my KeyListener received only a keyReleased() event. When it was not working (key doubled "Cc") my KeyListener received a keyTyped() event then keyReleased().
So I understand that AWT/Swing event handler mechanism "sends" each KeyEvent to the currently focused component (instead of to the component which the KeyEvent originates from). As I show the Dialog somewhere in the middle of the keyPressed/keyTyped/keyReleased sequence, sometimes the keyTyped was "wrongly" directed to the JTextField.
To solve this I executed the whole actionPerformed() code using SwingUtilities.invokeLater(), to make sure Dialog is shown after all EDT pending events are processed, and it seems to work so far...
I could find some good information in Java keybinding but what I don't understand is that it is recommended to use InputMap/ActionMap to avoid all the KeyListeners problems with focus changes etc. I used only InputMap/ActionMap and still it did not help...
So why InputMap does not react only to keyTyped() event ?
But sometimes, maybe 1/20 times, I get "Cc" at the end of the JTextfield !
Random issues are usually the result of threading issues.
All Swing components should be created and modified on the Event Dispatch Thread (EDT).
The code from your main() method is not executed on the EDT which could be the problem.
The code to create the GUI should be wrapped in a SwingUtilities.invokeLater(...).
Check out the Swing tutorial on Concurrency for more information.

How to close JInternalFrame by pressing escape key?

I created one JFrame with JDesktopPane, in which I am calling JInternalFrame. Now I want to close that internal frame by pressing escape key.
I tried 2-3 ways, but no output.
I did that by using code given below:
public static void closeWindow(JInternalFrame ji){
ActionListener close=New ActionListener(){
public void actionPerformed(ActionEvent e){
ji.dispose();
}
};
When I called above method from my intern frame class constructor by supplying its object , I was able to close it. But when there I write some other lines of code to the constructor. The above method call doesn't work. Please help me. I unable to find the problem in the code.
Also I tried to add KeyListener to internal frame, so I able to work with key strokes,but it also doesn't work.
Again I tried to setMnemonic to button as escape as below:
jButton1.setMnemonic(KeyEvent.VK_ESCAPE);
But also gives no output.
You need to implement the KeyListener interface, or add one that is Anonymous. In this example, I just implemented it.
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
public class JInternalFrame extends JFrame implements KeyListener {
public JInternalFrame()
{
super();
// other stuff to add to frame
this.setSize(400, 400);
this.setVisible(true);
this.addKeyListener( this );
}
#Override
public void keyTyped(KeyEvent e) {
// Don't need to implement this
}
#Override
public void keyPressed(KeyEvent e) {
if( e.getKeyCode() == KeyEvent.VK_ESCAPE ) {
System.exit(0); //Change this to dispose or whatever you want to do with the frame
}
}
#Override
public void keyReleased(KeyEvent e) {
//Dont need to implement anything here
}
public static void main(String[] args)
{
JInternalFrame frame = new JInternalFrame();
}
}
Now if this is an internal jframe as mentioned, it is probably better to implement the keylistener in the JDesktopPane and call the dispose method on the JInternalFrame after pressing escape instead of implementing keylistener in this frame. It all depends on which GUI component has focus of input.
This issue is old now but I recently got stuck on a similar problem. Adding the key listener to the content pane of the internal frame instead of the internal frame itself did the job for me.
this.getContentPane().addKeyListener(this);

Refresh java program with Button

I am trying to make a refresh button that will essentially restart the program when ever I click the button. I don't know how I should go about doing this.
I've place the Graphical User Interface i decided to use do complete this action. Any and all help would be greatly appreciated.
package pdfView;
import javax.swing.*;
import java.awt.*;
public class View extends JFrame {
public View() {
super("PDF Viewer");
setLookAndFeel();
setSize(500, 125);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
FlowLayout flo = new FlowLayout();
setLayout(flo);
JTextField Search = new JTextField ("Search", 29);
JButton Search1 = new JButton("Search");
//this is where i have the button
JButton ReFresh = new JButton("ReFresh");
add(Search);
add(Search1);
add(ReFresh);
setVisible(true);
}
private void setLookAndFeel() {
try {
UIManager.setLookAndFeel(
"com.sun.java.squing.plaf.nimbus.NimbusLookAndFeel"
);
} catch (Exception exc){
}
}
public static void main(String[] args) {
View pdf = new View();
}
}
What do you mean by refresh or restart?
Do you mean:
Let the application be as it is, just update what it's showing?
Really restart the application?
Updating what the application is showing
You first need to decide what actually should cause your application to refresh. You already talked about a Button. The mechanism for activating something like a button is called Action. You can do that stuff manually, using an ActionListener, or you could extend AbstractAction, which is what I recommend. Extending AbstractAction allows you to use the same logical action something in more than one location on the UI. Look at typical applications, they offer Cut/Copy/Paste through menu, toolbar, popupmenu and keyboard shortcuts. The simplest way to achieve this in Java is using Action by extending AbstractAction.
The methods you need to call to update your application are invalidate(), validate() or repaint().
Restarting an application
So you want to run through main() again? That should actually not be required, unless you have an application that supports updating itself. Even then it can sometimes be avoided by smart usage of a ClassLoader.
Some more notes on your code
Usage by extension anti-pattern
I wouldn't extend JFrame just to display a window on the screen. Usage by extension is an anti-pattern. You don't need to extend JFrame to get a JFrame displayed on the screen and do what you want.
Referring static members
I would refer to constants via their original declaration. I.e. I'd refer to EXIT_ON_CLOSE via WindowConstants.EXIT_ON_CLOSE, not JFrame.EXIT_ON_CLOSE.
Typo
You have a typo in your UIManager.setLookAndFeel() code. Search for swing and you will see the typo.
Exception information
You might actually want to print the exception to stderr using exc.printStackTrace() instead of ignoring it completely, because when you have a typo in the LaF class name, as you do, and you don't print the exception, you might actually not come to know what's going wrong.
Sequence of widget construction and UIManager.setLookAndFeel()
The sequence of UIManager.setLookAndFeel() and the effective new JFrame() via super(...) does not guarantee you that the whole UI will be in Nimbus, parts of it might still be in Metal. I recommend to set the LaF before even constructing the first widget, to be on the safe side. As far as I remember, it's not guaranteed that changing the LaF after component construction has an effect, unless you tell the UIManager to update the LaF. See also this quote from the documentation of UIManager:
Once the look and feel has been changed it is imperative to invoke updateUI on all JComponents. The method SwingUtilities.updateComponentTreeUI(java.awt.Component) makes it easy to apply updateUI to a containment hierarchy. Refer to it for details. The exact behavior of not invoking updateUI after changing the look and feel is unspecified. It is very possible to receive unexpected exceptions, painting problems, or worse.
http://docs.oracle.com/javase/8/docs/api/javax/swing/UIManager.html
setSize() vs. pack() with a little help of Insets and Border
Instead of setting the size manually, you might want to play with Insets or Border and JFrame.pack() in order to get a decent layout of your window. Setting the size manually assumes that you know a lot about the screen resolution and the font size of the user.
The pack() method performs automatic size calculation based on the contents. Insets and Border allow you to create some space and borders, even with some designs or labels, around components so they wouldn't be cramped tightly in a window but be nicely spaced.
First you have to assign an actionListener to the ReFresh Jbutton.
You can either implement the interface ActionListener to the class, and override the actionPerformed() method like this
public class View extends JFrame implements ActionListener{
private JButton ReFresh;
public View() {
super("PDF Viewer");
setLookAndFeel();
setSize(500, 125);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
FlowLayout flo = new FlowLayout();
setLayout(flo);
JTextField Search = new JTextField ("Search", 29);
JButton Search1 = new JButton("Search");
//this is where i have the button
ReFresh = new JButton("ReFresh");
ReFresh.addActionListener(this);
add(Search);
add(Search1);
add(ReFresh);
setVisible(true);
}
private void setLookAndFeel() { //right way for nimbus: http://docs.oracle.com/javase/tutorial/uiswing/lookandfeel/nimbus.html
try {
for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
UIManager.setLookAndFeel(info.getClassName());
break;
}
}
}
catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void actionPerformed(ActionEvent e)
{
if(e.equals(ReFresh))
{
super.repaint();
}
}}
public static void main(String[] args) {
View pdf = new View();
}
Or you can do inline assignment to addActionListener, like this
ReFresh.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
super.repaint();
}
});
You can try these methods to refresh/reload the JFrame,
invalidate();
validate();
repaint();
you can also use dispose(); and then new View(); to create the new JFrame, but in this sequence it will close the window and create new one.
or you can even try setVisible(false); then setVisible(true);
I recommend the first 3.

Displaying a JPanel on JFrame by a label click

I have tried it over and over.Its not working.When I click the lablel, nothing happens.
private void jLabel1MouseClicked(java.awt.event.MouseEvent evt)
{
setLayout(new BorderLayout());
JPanel o = new JPanel ();
o.setPreferredSize(new Dimension(122,200));
o.setBackground(Color.red);
add(o,BroderLayout.CENTER);
// TODO add your handling code here:
}
Looks a stupid question, but, are you sure that your method is getting called? Is your object registered as an event listener of this label?
Just to be sure, you should implement the MouseListener interface:
public class YourClass () implements MouseListener{
public YourClass(){
...
label.addListener(this);
}
// and then implement the method to handle the event
public void mouseClicked(MouseEvent e) {
// TODO: Handle the event
}
}
jLabel1MouseClicked does not look like the event handler method
EDIT: By the way, you may want to implement the other methods in this interface, even if you don't need them. Check the documentation: MouseListener example
if the Listener is implemented correctly, then you should change this
o.setPreferredSize(new Dimension(122,200));
with this
o.setSize(122,200);
you can put a setVisible method too, but it should work without it, too
I Think you are using Netbeans, If yes then the method you showed is auto-generated which means that it has correctly implemented Listeners in it's Auto-Generated Code Segment, well for now this means that you have error in Showing JPanel not in implementing listener, so I found some suggestions for you,
you have written BroderLayout but its BorderLayout but this seems to be a typo when posting Question.
Secondly you are not updating your frame, just adding the JPanel is not enough for showing it, so Add this code, this.revalidate();
so finally your block code should be like this,
private void jLabel1MouseClicked(java.awt.event.MouseEvent evt)
{
setLayout(new BorderLayout());
JPanel o = new JPanel ();
o.setPreferredSize(new Dimension(122,200));
o.setBackground(Color.red);
add(o,BorderLayout.CENTER);
revalidate();
}

Using JInternalFrame and some button

Can we use a JInternalFame with a button in the main frame? The frame contains a JDesktopPane, of course. The button should open up the JInternalFrame How?
I don't know a way to put a JButton directly on a JDesktopPane, but you can use menu items to create and select a JInternalFrame. In this example, each menu item uses an Action defined in the JInternalFrame to select the corresponding frame.
class MyFrame extends JInternalFrame {
private Action action;
MyFrame(JDesktopPane desktop, String name, int offset) {
…
action = new AbstractAction(name) {
#Override
public void actionPerformed(ActionEvent ae) {
try {
MyFrame.this.setSelected(true);
} catch (PropertyVetoException e) {
e.printStackTrace();
}
}
};
}
public Action getAction() { return action; }
}
Addendum: as #camickr suggests, it is technically possible to put a JButton directly on a JDesktopPane, but it might prove difficult to use in practice.
I don't really understand the question so I will just make some observations.
a) a JInternalFrme is like a frame in that you can add any component to it that you want
b) A JButton works the same whether it is added to an internal frame or a frame
I suggest you start by reading the Swing tutorial for working examples. You might start with the sections on "How to Use Internal Frames" and "How to Use Buttons".
If you still have problems then post your SSCCE that shows what you have tried.

Categories