I have created a small app that uses JButtons to increment numbers. The buttons aren't supposed to be clickable but rather be activated by keyboard (ie the numbers in the textField increase with the keyboard pushed and not by using a mouse to click the button). My issue is that when the app is first launched, the keyboard doesn't do anything until I first click one of the buttons - even though clicking the button doesn't progress anything. I have tried to make one of the buttons requestFocusInWindow() thinking that if it was already focused on, the the keys would work, but that hasn't seemed to work whether I put it in my main method or controller class. I've tried to figure out if I need to do a KeyboardFocusManager or a addFocusListener() (but I don't want something always happening if a button gains/loses focus). I've tried so many things my head is spinning, trying to add either to my main method with frame or my controller class. Below is what my current code is:
Class with Main
import javax.swing.JFrame;
public class Count {
public static void main(String[] args) {
CountController frame = new CountController();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(560, 150);
frame.setVisible(true);
//I've tried to add the button and requestFocusInWindow here
//as well as tried a frame.addWindowFocusListener
}
} // end class
Controller Class
imports ...
public class CountController extends JFrame implements KeyListener {
private JLabel ...
private JTextField ...
private JButton ....
int ...
// no-argument constructor
public CountController() {
super("Title");
setLayout(null); // position GUI components explicitly
//set up JLabels in following manner
label = new JLabel("some label");
label.setBounds(47, 5, 45, 25);
label.setHorizontalAlignment(JLabel.CENTER);
add(label);
//set up JTextFields in following manner
textField = new JTextField("0");
textField.setBounds(47, 30, 45, 25);
textField.setHorizontalAlignment(JTextField.CENTER);
textField.setBackground(Color.WHITE);
textField.setEditable(false);
add(textField);
//set up JButtons in the following manner
button = new JButton("some text");
button.setBounds(15, 70, 110, 25);
button.setBackground(Color.WHITE);
add(button);
button.addKeyListener(this);
//I've tried adding requestFocusInWindow() here as well
} // end constructor
//begin KeyListener stuff
#Override
public void keyPressed(KeyEvent event){
int keyCode = event.getKeyCode();
switch(keyCode){
case #: //# is ASCII #
do some things;
call a method();
break;
}
}
#Override
public void keyReleased(KeyEvent event){
button.setBackground(Color.WHITE);
}
#Override
public void keyTyped(KeyEvent event){
// nothing but this is needed for implementing KeyListener
}
//List of methods that are called from switch
...
//I've tried to add a public void initialize(){}
}//end CountController class
I would appreciate any input on getting this to work so that I don't have to first click a button before my keys work.
Thanks!
In order for the listener to trigger an event, the component it is registered to must first BE focused AND have focus. Use the key bindings API instead.
The following uses JComponent.WHEN_IN_FOCUSED_WINDOW to define the context under which the key events will be generated. In this configuration, it won't matter what has focus, the events will still be generated, so long as the window is currently focused
import java.awt.Color;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
public class Count {
public static void main(String[] args) {
CountController frame = new CountController();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(560, 150);
frame.setVisible(true);
//I've tried to add the button and requestFocusInWindow here
//as well as tried a frame.addWindowFocusListener
}
public static class CountController extends JFrame {
// no-argument constructor
public CountController() {
super("Title");
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
//set up JLabels in following manner
JLabel label = new JLabel("some label");
label.setHorizontalAlignment(JLabel.CENTER);
add(label, gbc);
//set up JTextFields in following manner
JTextField textField = new JTextField("0", 20);
textField.setHorizontalAlignment(JTextField.CENTER);
textField.setBackground(Color.WHITE);
textField.setEditable(false);
add(textField, gbc);
//set up JButtons in the following manner
JButton button = new JButton("some text");
add(button, gbc);
InputMap im = button.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
ActionMap am = button.getActionMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_3, KeyEvent.SHIFT_DOWN_MASK), "Pressed.#");
am.put("Pressed.#", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
textField.setText(textField.getText() + "#");
}
});
} // end constructor
}//end CountController class
} // end class
Although I'd register the bindings against the parent container and not the buttons, but that's just me.
And, reasons for not using null layouts ... this is what your original code looks like on my PC
Although it's not quite clear to me what your code should accomplish, your problem is that you addKeyListener(this) to the button but your button doesn't have the focus and the key doesn't do anything when pressed. Try adding the KeyListener() to some other GUI component, like the textfield for example, since it's the first component and has the focus on start(from the code you've provided), and see if it works.
Related
I have a JDialog which can generate another one. The two JDialogs have the property setAlwaysOnTop(true) and aren't modal. The second Jdialog generated from the first one appears always behind. I would like it to appear in front.
I tried several things : toFront(), requestFocus(), etc..
Here a short example to reproduce the problem:
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JPanel;
public class SwingTester {
public static void main(String[] args) {
createWindow();
}
private static void createWindow() {
final JDialog modelDialog = createDialog();
modelDialog.setVisible(true);
}
private static JDialog createDialog(){
final JDialog modelDialog = new JDialog();
modelDialog.setBounds(132, 132, 300, 200);
Container dialogContainer = modelDialog.getContentPane();
dialogContainer.setLayout(new BorderLayout());
JPanel panel1 = new JPanel();
JButton okButton = new JButton("Ok");
okButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
final JDialog modelDialog = createDialog();
modelDialog.setVisible(true);
}
});
panel1.add(okButton);
dialogContainer.add(panel1, BorderLayout.SOUTH);
modelDialog.setAlwaysOnTop(true);
return modelDialog;
}
After a click on the OK button, we see another dialog appearing behind the current one. The new dialog has the focus but is still behind
Actually, I tried your code, and it is no problem appears. If you want to see it too clearly, create dialog with random location. In every click, new dialog is random place on the top.
modelDialog.setBounds(new Random().nextInt(400), new Random().nextInt(400), 300, 200);
But my suggestion is create dialog as modal, in addition to set old one as parent.
I have run into the issue of not being able to collect the real text from my JTextField when calling getText(). I have provided a simplified version of the code I was running that includes only the aspects of the program to where it will reproduce the issue. I am attempting to collect the text from the JTextField upon the clicking of the button b.
The correct value returned from getText() should be what ever was input, but instead here it simply returns an empty String. In testing I have found that initializing the TJextField with a String will have it return that String no matter what, even if it is changed before pressing the button. and if one uses setText() inside of init() for example, it will continue to return an empty String.
public class TopClass {
public static void main(String[] args){
BottomClass bottom = new BottomClass();
bottom.init();
}
}
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class BottomClass implements ActionListener{
JFrame frame = new JFrame();
JTextField tf = new JTextField();
JButton b = new JButton("BUTTON");
public void init(){
BottomClass bc = new BottomClass();
b.addActionListener((ActionListener) bc);
frame.add(tf, BorderLayout.NORTH);
frame.add(b, BorderLayout.SOUTH);
frame.setSize(100,100);
frame.show();
}
public void actionPerformed(ActionEvent e) {
System.out.println("TEXT: "+tf.getText());
}
}
Several things aren't clean at all in this code, so I just rewrote most of BottomClass.
You can do it by implementing ActionListener, but BottomClass is more than just an EventListener, so (in the name of realistic responsibility) I just added a new instance of ActionListener to the component that needs it (JButton)
You create an instance bc of BottomClass inside the method init() IN BottomClass. This makes no sense at all and was simply deleted.
You initialize your components at the wrong point. They should either be initialized in the constructor, or inside your nice and fitting init() method. I'm 99% sure that the placement of your initializations is what caused your trouble.
I'm not sure how much of an error this is, but when adding your components you specify BorderLayout constraints despite never defining BorderLayout as the LayoutManager to use. I added the setLayout(new BorderLayout()) call.
It's usually good form to have a constructor, especially if you have a different class calling it. Even if it's empty, a written empty constructor is more easily readable and understandable than an invisible one.
All that said, here's a working version of BottomClass, TopClass doesn't need any adjustments:
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class BottomClass
{
JFrame frame;
JTextField tf;
JButton b;
public BottomClass()
{
// Empty constructor
}
public void init()
{
frame = new JFrame();
frame.setLayout(new BorderLayout());
tf = new JTextField();
b = new JButton("Button");
b.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
System.out.println("TEXT: " + tf.getText());
}
});
frame.add(tf, BorderLayout.NORTH);
frame.add(b, BorderLayout.SOUTH);
frame.setSize(100, 100);
frame.setVisible(true);
}
}
I can't seem to find a solution online for why I'm getting this error on attempted run
I'm working on making a simple test system for a different program when are button press will yield value in a text box. I would like them to be on different lines to make it cleaner, so I looked into layouts. I decided a Box Layout would fit me best. I looked at different examples before attempting this and my code ended up looking like this, (apologies for the messy code)
Update
Got the box layout error to disappear but the code will not center them on the panel/frame. The label and button align left while the textfield becomes very large. I don't need it todo that
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import static javax.swing.BoxLayout.Y_AXIS;
import static javax.swing.SwingConstants.CENTER;
public class button extends JFrame {
static JFrame f;
static JButton b;
static JLabel l;
// main class
public static void main(String[] args)
{
// create a new frame to stor text field and button
f = new JFrame("panel");
BoxLayout layout = new BoxLayout(f, BoxLayout.Y_AXIS);
f.setLayout(layout);
// create a label to display text
l = new JLabel("panel label");
b = new JButton("button1");
JTextField textArea = new JTextField(5);
textArea.setEditable(false);
//textArea.append("Hello World");
// create a panel to add buttons
JPanel p = new JPanel();
// add buttons and textfield to panel
f.add(p);
f.setSize(300, 300);
p.add(l);
p.add(b);
p.setBackground(Color.white);
p.add(textArea);
f.show();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
Random r = new Random();
textArea.setText(String.valueOf(r));
}
});
}
}
Error
Exception in thread "main" java.awt.AWTError: BoxLayout can't be shared
at java.desktop/javax.swing.BoxLayout.checkContainer(BoxLayout.java:461)
at java.desktop/javax.swing.BoxLayout.invalidateLayout(BoxLayout.java:245)
at java.desktop/javax.swing.BoxLayout.addLayoutComponent(BoxLayout.java:278)
at java.desktop/java.awt.Container.addImpl(Container.java:1152)
at java.desktop/java.awt.Container.add(Container.java:1029)
at java.desktop/javax.swing.JFrame.addImpl(JFrame.java:553)
at java.desktop/java.awt.Container.add(Container.java:436)
at button.main(button.java:36)
I would like the three items to all to be stacked one on top of another with a space between them. The order doesn't matter right now.
Swing was first added to the JDK in 1998 and has undergone a lot of changes since. Unfortunately, when you read Web pages about Swing, it is not obvious when that page was last updated. Consequently you may be learning outdated techniques for writing Swing code.
First of all, according to the code you posted, class button does not need to extend class JFrame since you use a static variable as your application's JFrame. Also, JFrame is a top-level container which makes it a special kind of container and not the same kind of continer as a JPanel. You need to set the layout manager for your JPanel and then add the JLabel, JTextField and JButton to that JPanel. And then add the JPanel to the JFrame.
Calling method pack() of class JFrame will automatically set the preferred sizes for the components inside the JFrame. It appears in the code below.
Please also look at Java coding conventions which allows others to more easily read and understand your code. And note that, according to these conventions, I renamed your class from button to Buttons and also because there are already several class in the JDK named Button.
Here is my rewrite of your code...
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.WindowConstants;
public class Buttons implements Runnable {
public void run() {
createAndShowGui();
}
private void createAndShowGui() {
JFrame f = new JFrame("Box");
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
JPanel p = new JPanel();
BoxLayout layout = new BoxLayout(p, BoxLayout.Y_AXIS);
p.setLayout(layout);
JLabel l = new JLabel("panel label");
JTextField textField = new JTextField(5);
JButton b = new JButton("button1");
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
Random r = new Random();
textField.setText(String.valueOf(r.nextBoolean()));
}
});
p.add(l);
p.add(textField);
p.add(b);
f.add(p);
f.pack();
f.setLocationByPlatform(true);
f.setVisible(true);
}
public static void main(String[] args) {
Buttons instance = new Buttons();
EventQueue.invokeLater(instance);
}
}
I've made a JFrame with Diferent JButtons and i'd like to get an image from another class. Any ideas? Or how draw on the same class but on the action performed?
Because it doesnt let me to do any drawings...my complier always gives me error messages
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import javax.swing.*;
public class red extends JFrame {
public JButton b;
public JButton b1;
public JButton b2;
public JButton b3;
public JButton b4;
public static Image p;
public static Graphics g;
public red() throws IOException {
gui1 x = new gui1();
setTitle(" ");
setSize(1200,700);
setLayout(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
b= new JButton("click");
b1= new JButton();
b.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e0){
b1.setBounds(0, 0, 200, 200);
b.show(false);
add(x);
}
});
b.setBounds(0, 0, 100, 100);
add(b1);
add(b);
setVisible(true);
}
public static void main(String[] args) throws IOException {
red k = new red();
}
}
import java.awt.*;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;
public class gui1 extends Canvas {
public static Image p;
public void paint(Graphics g){
g.drawImage(p, 700, 200, 100, 100, this);
}
{
try {
p= ImageIO.read(new File("Lighthouse.jpg"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Phew! I see A LOT of errors in your code (even after I corrected the compilation errors):
You're not following the Java naming conventions:
Class names should be nouns, in mixed case with the first letter of each internal word capitalized
while red is a noun it should be more descriptive and be capitalized. The same goes for gui1
You're extending JFrame which in plain english would say: red is a JFrame, you should really avoid this and create your GUI based on JPanels instead... see Java Swing using extends JFrame vs callint it inside of class
You're setting size (a REAAAAAAALLY big one window for the JButton sizes you're using), instead use pack()
You're using null-layout, while pixel-perfect GUIs might seem like the easiest way to create complex GUIs for Swing newbies, the more you use them the more problems related to this you'll find in the future, they are hard to maintain and cause random problems, they don't resize, etc. Please read Null layout is evil and Why is it frowned upon to use a null layout in Swing? for more information about why you should avoid its use and why you should change your GUI to work with Layout Managers along with Empty Borders for extra spacing between components.
You're making use of a deprecated method JFrame#show() you should be using JFrame#setVisible(...) instead.
Related to point #4, you shouldn't be calling setBounds(...) method, but let that calculations to the layout managers.
You're not placing your program on the Event Dispatch Thread (EDT), Swing is not thread safe, you can fix this by changing your main() method as follows:
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
//Your constructor here
}
});
}
You're mixing AWT and Swing components, instead of using AWT's Canvas use Swing's JPanel which has more functionality and support.
Images will become embedded resources once they're packaged in a JAR file, so it's wise to start treating them as if they already were, not as external files as shown in the embedded-resource tag.
Once you change from Canvas to JPanel you should override its paintComponent(...) method and not paint(...) and call it's super.paintComponent(g) method as the first line, also don't forget to add the #Overrides annotation. See the tutorial on Swing custom painting.
You're abusing the use of static keyword, see how does the static keyword works?
After seeing all the above errors I recommend you to go back and Learn the basics of the language before starting with a graphical environment which will only add more difficulty to your learning.
From what I understand you want to draw an image on a button click, if that's the case then you can wrap your image in a JLabel and add that JLabel to a JPanel which then is added to a parent JPanel which is later added to the JFrame:
As you can see in the GIF above, the icon is displayed after user presses the button.
Obviously this can be improved for the GUI to be more "attractive" with combinations of layout managers and empty borders as stated before.
This was done with the following code:
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class ImageDrawingFromOneClassToAnother {
private JFrame frame;
private JPanel pane;
private JPanel leftPane;
private JPanel rightPane;
private ImageIcon icon;
private JButton button;
private JLabel label;
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new ImageDrawingFromOneClassToAnother().createAndShowGui();
}
});
}
public void createAndShowGui() {
frame = new JFrame(getClass().getSimpleName());
icon = new ImageIcon(this.getClass().getResource("king.png")); //Read images as if they were already embedded resources
button = new JButton("Draw image");
label = new JLabel(""); //Create an empty label
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
label.setIcon(icon); //On button click, we set the icon for the empty label
}
});
pane = new JPanel() {
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 200); //Set a size for the main panel
}
};
pane.setLayout(new GridLayout(1, 2)); //The main panel
leftPane = new JPanel(); //The button panel
leftPane.setLayout(new BoxLayout(leftPane, BoxLayout.PAGE_AXIS));
leftPane.add(button);
rightPane = new JPanel(); //The panel where the image will be drawn
rightPane.add(label);
//We add both (button and image) panels to the main panel
pane.add(leftPane);
pane.add(rightPane);
frame.add(pane); //Add the main panel to the frame
frame.pack(); //Calculate its preferred size
frame.setVisible(true); //Set it to be visible
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class SideNotes {
public static JPanel panel = new JPanel();
private List<String> notes = new ArrayList<String>();
private static JButton add = new JButton("Add note");
public SideNotes() {
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.add(add);
loadNotes();
add.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
addNote();
}
});
}
public void addNote() {
String note = JOptionPane.showInputDialog("Enter note: ", null);
notes.add(note);
JLabel label = new JLabel(note);
panel.add(label);
panel.revalidate();
panel.repaint();
}
private void loadNotes() {
for (int i = 0; i < notes.size(); i++) {
JCheckBox jcb = new JCheckBox(notes.get(i), false);
panel.add(jcb);
panel.revalidate();
panel.repaint();
}
}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setSize(200, 400);
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.add(add);
frame.add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
new SideNotes();
}
}
Why isn't my JCheckBox showing up? The text shows up but not the actual box. What's the deal?
I have edited my post to contain all of my code in case that helps solve the issue.
needmoretextneedmoretextneedmoretextneedmoretextneedmoretextneedmoretextneedmoretext
Possible reasons:
panel has not been added to GUI
panel has been added but for some reason is not visible.
panel is too small to show the child component. This can happen for instance if you set a component's size or preferredSize or if you place it in a FlowLayout-using container without thought.
panel uses null layout.
panel's layout manager is not one that easily accepts a new component -- think GroupLayout for this one.
There are other unspecified layout manager problems going on. Do you call pack() on your GUI? Do you use null layout or absolute positioning anywhere? Do you need to put panel in a JScrollPane?
Consider creating and posting an sscce for better help.
Edit
Your posted code doesn't ever add any JCheckBoxes to the JPanel, just JLabels. To prove this is so, click on the labels and you'll see that they don't respond to clicks.
Your code grossly over-uses static fields. Get rid of all static modifiers on all variables. They should all be instance variables. The only static anything in your code above should be the main method, and that's it. If this causes errors, then fix the errors, but not by making fields static.
Give your SideNotes class a method, getPanel() that returns the panel field.
Create a SideNotes instance in the beginning of your main method. Then call the above method on the instance to get the JPanel for the JFrame. i.e., frame.add(sideNotes.getPanel());.
Don't add JLabels to your GUI (I've no idea why you're doing this). Add JCheckBoxes in the actionPerformed method.
Every time you press the button, a new Note (JLabel) is added to the panel. But you never call loadNotes() after adding a new Note. So the JLabel is added but not its respective JCheckBox as intended.
Besides of this I'd suggest you make this change:
public void addNote() {
String note = JOptionPane.showInputDialog("Enter note: ", null);
if(notes != null) {
notes.add(note);
JLabel label = new JLabel(note);
panel.add(label);
panel.add(new JCheckBox(note, false));
panel.revalidate();
panel.repaint();
}
}
So you don't need to call loadNotes() and update the GUI just once.