Focus on component when a panel is loaded - java

I have a frame, where i load a panel into. It works fine, but nothing has focus when it loads. Pressing tab doesn't help. I have to use the mouse to press a textfield.
I've tried: jtextfield1.requestFocus(); and jtextfiel1.requestFocusInWindow(); But it doesn't work.
What am I doing wrong?
The constructor in the JPanel:
public OpretOpdater(BrugerHandler brugerHandler, ReklamationHandler reklamationsHandler) {
initComponents();
jTextFieldOrdnr.requestFocusInWindow();
this.brugerHandler = brugerHandler;
this.rekH = reklamationsHandler;
startUp();
}
Putting the panel in the frame in the GUI:
public static void opret(ReklamationHandler reklamationHandler) {
rHandler = reklamationHandler;
SwingUtilities.invokeLater(opret);
}
static Runnable opret = new Runnable() {
#Override
public void run() {
JFrame f = jframe;
f.getContentPane().removeAll();
JPanel opret = new OpretOpdater(bHandler, rHandler);
f.getContentPane().add(opret);
f.pack();
f.setLocationRelativeTo(null);
}
};

You should call requestFocusInWindow() only when components are visible/shown on a container or after pack() has been called and all components are added to the container or else it wont work.
Also please be sure to create Swing components on Event Dispatch Thread. If you haven't already have read on Concurrency in Swing.
The reason I mention the above is not creating and manipulating Swing components on the EDT can cause random artifacts in the code. i.e focus is not being given etc.
This code below was created to show how calling requestFocusInWindow before a component is visible will not work but calling it after its visible works as expected.
Also note that removing the SwingUtilities block will cause the requestFocusInWindow not to work as expected (i.e we might be given focus or not depending on our luck :P):
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class Test {
public Test() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
JTextField f1 = new JTextField(10);
JTextField f2 = new JTextField(10);
//f2.requestFocusInWindow(); //wont work (if uncomment this remember to comment the one after setVisible or you wont see the reults)
JButton b = new JButton("Button");
JPanel p = new JPanel();
p.add(f1);//by default first added component will have focus
p.add(f2);
p.add(b);
frame.add(p);
//f2.requestFocusInWindow();//wont work
frame.pack();//Realize the components.
//f2.requestFocusInWindow();//will work
frame.setVisible(true);
f2.requestFocusInWindow();//will work
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {//if we remove this block it wont work also (no matter when we call requestFocusInWindow)
#Override
public void run() {
new Test();
}
});
}
}
I would suggest a read on How to Use the Focus Subsystem.

Often it is nice to indicate which field you want to have focus when you create the field and not separate the code by adding the request focus when the frame becomes visible.
Take a look at Dialog Focus which has a solution that is also applicable in this case. Using this approach your code would look like:
JTextField f2 = new JTextField(10);
f2.addAncestorListener( new RequestFocusListener() );

Related

Cannot refer to the non-final local variable display defined in an enclosing scope

This might be a very basic question. But I am stuck at this. The error that I get for the String variable display states:
Cannot refer to the non-final local variable display defined in an enclosing scope.
If I use a final keyword, I get the message:
The final local variable display cannot be assigned, since it is defined in an enclosing slope.*
The code is:
public class Frame {
public static void main(String[] args) {
String display=" ";
Frame ob=new Frame();
JFrame frame=new JFrame("Test");
frame.setBounds(300,100,800,500);
//Container c=frame.getContentPane();
frame.setLayout(null);
final JTextField name=new JTextField();
name.setBounds(500,212,150,20);
JLabel nameLabel=new JLabel("Name: ");
nameLabel.setForeground(Color.WHITE);
nameLabel.setBounds(450,171,100,100);
JTextField ohr=new JTextField();
ohr.setBounds(500,282,150,20);
JLabel ohrID=new JLabel("OHR ID: ");
ohrID.setForeground(Color.WHITE);
ohrID.setBounds(450,241,100,100);
final JButton button=new JButton("Submit");
button.setBounds(530,350,90,20);
frame.add(name);
frame.add(ohr);
frame.add(ohrID);
frame.add(nameLabel);
frame.add(button);
frame.getContentPane().setBackground(Color.DARK_GRAY);
frame.setVisible(true);
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
if(e.getSource()==button){
display=name.getText();
JOptionPane.showMessageDialog(null, "Hi "+ display);
System.exit(0);
}
}
});
}
Thanks in advance!
There are multiple issues with your code, and we'll address them right here, right now and solve your problem at the same time.
public class Frame { this particular line has an error, Frame is the name of an AWT class, so it might confuse you or anyone who reads this code later on, give it a more meaningful name and avoid those names that could be confused with other Java packages.
Frame ob=new Frame(); you create an instance of your class and never use it again, why?
frame.setLayout(null); NEVER, please don't use null-layout, Swing has to deal with multiple PLAFs, screen sizes and resolutions, different OS, pixel perfect apps might seem like the easiest way to create complex UIs but later on you'll find that errors like this happen very often.
.setBounds(...) on every component, again, this is due to null-layout but it's better to use Layout managers
final JTextField name=new JTextField(); There's no need to declare any of your components as final, this is due to a poor design of your class, your components should be declared as class members (outside any method including main).
Speaking about main, separate your program into smaller pieces, don't throw everything at main or at the very least create a method that is not static so you can call it after creating an instance of your class (or else later on you'll end up with tons of static variables and that's a poor design of your class once again).
System.exit(0); it will stop the JVM, it's never a good idea to do that, it's better to .dispose() the JFrame and have your JFrame's defaultCloseOperation set to EXIT_ON_CLOSE which will safely dispose your app and then stop the JVM.
display=name.getText();, for this particular case, display could be an inner variable rather than a class member. This will solve your particular question
JOptionPane.showMessageDialog(null, "Hi "+ display); that null should be a reference to your JFrame, this will place your dialog in the middle of that JFrame rather than in the middle of the screen.
You never place your program inside the EDT, see point #2 in this answer.
So, having all the above points in mind, here's an improved version of your code.
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class UsingVariablesInsideActionListenerExample {
//We declare our components here
private JFrame frame;
private JButton button;
private JTextField name;
private JTextField ohr;
private JLabel nameLabel;
private JLabel ohrID;
private JPanel pane;
private JPanel namePane;
private JPanel ohrPane;
public static void main(String[] args) {
SwingUtilities.invokeLater(new UsingVariablesInsideActionListenerExample()::createAndShowGUI); //This is using Java 8 lambdas to place your program in the EDT
}
private void createAndShowGUI() {
frame = new JFrame("Test"); //Create your JFrame
pane = new JPanel();
pane.setLayout(new BoxLayout(pane, BoxLayout.PAGE_AXIS)); //This will make this JPanel to arrange components vertically
namePane = new JPanel(); //By default, JPanels have FlowLayout which will arrange components horizontally
ohrPane = new JPanel();
name = new JTextField(10); //We create a JTextField with 10 columns
nameLabel = new JLabel("Name: ");
nameLabel.setForeground(Color.WHITE);
ohr = new JTextField(10);
ohrID = new JLabel("OHR ID: ");
ohrID.setForeground(Color.WHITE);
button = new JButton("Submit");
//Add the action listener
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == button) {
String display = name.getText(); //The display variable is now an inner variable rather than a class member
JOptionPane.showMessageDialog(frame, "Hi " + display);
frame.dispose(); //We dispose the JFrame and it will be closed after due to EXIT_ON_CLOSE below.
}
}
});
//We add the components to the namePane (horizontally), the order matters
namePane.add(nameLabel);
namePane.add(name);
//Now we add these components to the ohrPane (horizontally again)
ohrPane.add(ohrID);
ohrPane.add(ohr);
//We then add the name and ohr panes to a bigger JPanel (pane, which if you remember will add them vertically) and we add the button at the end
pane.add(namePane);
pane.add(ohrPane);
pane.add(button);
//We make them non opaque (transparent) so that we can see the background color of the JFrame
namePane.setOpaque(false);
ohrPane.setOpaque(false);
pane.setOpaque(false);
frame.add(pane);
frame.getContentPane().setBackground(Color.DARK_GRAY);
frame.pack(); //This will get every component's preferred size and make the JFrame as small as possible where it looks good on every OS, PLAF, screen size and resolution.
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true); //We make the frame visible (always at the very end, when we've added everything to it).
}
}
And this is how it looks like now.
The UI may not be perfectly equal to the one you have, but I'm sure you can play with the different layout managers, and nest various JPanels to get a much better looking UI than mine, or at least a more similar one to the one you had.
Variable used in side an inner class should be effectively final . You can use a string[] of length 1 instead of string to resolve this . Please read bellow post for more details
Difference between final and effectively final
Also check this post for more details
Variable used in lambda expression should be final or effectively final

Does placing setVisible() function in the beginning of the function be different when I placed it at the end of that function?

I'm just new to Java GUI Programming and I'm having a problem that the components inside my panel is missing when I place the setVisible()function at the beginning of the function called by the constructor but it works fine when it is at the end. See code below:
public static void main(String[] args)
{
new MainClass();
}
public MainClass()
{
setFrame();
}
private void setFrame()
{
JFrame frame = new JFrame();
frame.setSize(400,400);
frame.setResizable(false);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Some area where the object of my components inside the panel is created and initialized.
// If I just place a label and a button, it will appear on the panel. However if I add the JTextArea, all the components in my panel is gone. Just like the code below.
textArea1 = new JTextArea(20,34);
textArea1.setWrapStyleWord(true);
textArea1.setLineWrap(true);
JScrollPane scroll =
new JScrollPane(textArea1,
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
panel.add(scroll);
frame.add(panel);
// Works fine when setVisible(true); it placed here.
}
What could the problem possibly be with regard to placing the setVisible() function to the beginning or to the end of the method.
As already pointed out in the comments and the other answer:
You should call setVisible(true) at the end, after all components have been added.
This does not directly answer your question. The answer to your question is: Yes, it makes a difference. If you call setVisible before all compoents have been added, it may work, in some cases, with some programs, on some PCs, with some Java versions, with some operating systems - but you always have to expect that it may not work as expected in some cases.
You will find dozens of related questions here on stackoverflow and elsewhere. The usual symptoms of these problems are that some components are not displayed properly, and then suddenly appear when the window is resized. (Resizing a window basically triggers a layout and a repaint).
The likeliness of unexpected behavior is increased when you violate the threading rules of Swing. And, in some sense, you did violate the threading rules of Swing: You should always create the GUI on the Event Dispatch Thread!
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class SomeSwingGUI
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
createAndShowGUI();
}
});
}
// This method may (and will) only be called
// on the Event Dispatch Thread
private static void createAndShowGUI()
{
JFrame f = new JFrame();
// Add your components here
f.setVisible(true); // Do this last
}
}
And by the way: Timothy Truckle pointed out in a comment that you should not invoke setVisible from the constructor. This is true. More importantly: You should usually not directly create a class that extends JFrame. (In some (rare!) cases, this is appropriate, but the general guideline should be to not extend JFrame)
The components cannot be shown, because you add them after you call the setVisible() method of the Frame.
The Componet's add() method changes layout-related information, and invalidates the component hierarchy. If the container has already been displayed, the hierarchy must be validated thereafter in order to display the added component, as pointed here
.
So in order to show the items, you should either call the revalidate() method of the frame or call setVisible() after all your components are added.
Unless there is no special need, you should call setVisible() after you have added every other component.
public class TestMain extends JFrame {
public static void main(String[] args) {
JFrame test = new TestMain();
//if the setVisible() is called too early, you have to revalidate
test.revalidate();
}
public TestMain() {
setFrame();
}
private void setFrame() {
setSize(400,400);
setResizable(false);
setLayout(new FlowLayout());
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel(new FlowLayout());
setVisible(true);
JTextArea textArea1 = new JTextArea(25,15);
textArea1.setWrapStyleWord(true);
textArea1.setLineWrap(true);
panel.add(textArea1);
JScrollPane scroll =
new JScrollPane(panel,
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
// this method invalidates the component hierarchy.
getContentPane().add(scroll);
}
}

Hide a swing component without revalidating the layout?

If I set up a JFrame with some components and a layout manager, which initially looks perfectly fine, and then later due to some condition (say, clicking a button) I hide one of those components - the layout manager shuffles all the components around again.
See example code - initially 3 buttons appear. When you click the Hide button, the Hide button is hidden - but the two outer buttons then squash together. When you click the show button, they move apart again to make space. How can I stop that from happening, so that after I call pack (), components stay where they are no matter if they later become hidden?
In my real code I'm doing this with GridBagLayout, but used FlowLayout in the example below because its simpler and less code, and shows exactly the same behaviour.
I can only think of nasty ways of doing this, like using .setEnabled (false) instead of .setVisible (false), and then overriding the component's paintComponent () method to not draw the component when it is disabled.
It seems the exact opposite problem to here - Hide a button from Layout in Java Swing - where is complaining that hidden buttons do still take up space :) But there's no sample code there to show it working in that way.
Many thanks for any suggestions :)
Example:
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class RevalidateWhenSetChildInvisibleExample
{
private JButton button1;
private JButton button2;
private JButton button3;
public void run ()
{
// Set up action
Action hideButtonAction = new AbstractAction ()
{
#Override
public void actionPerformed (ActionEvent e)
{
button2.setVisible (false);
}
};
hideButtonAction.putValue (Action.NAME, "Hide");
Action showButtonAction = new AbstractAction ()
{
#Override
public void actionPerformed (ActionEvent e)
{
button2.setVisible (true);
}
};
showButtonAction.putValue (Action.NAME, "Show");
// Set up buttons
button1 = new JButton ("Dummy");
button2 = new JButton (hideButtonAction);
button3 = new JButton (showButtonAction);
// Set up content pane
JPanel contentPane = new JPanel ();
contentPane.setLayout (new FlowLayout ());
contentPane.add (button1);
contentPane.add (button2);
contentPane.add (button3);
// Set up frame
JFrame frame = new JFrame ();
frame.setContentPane (contentPane);
frame.pack ();
frame.setVisible (true);
}
public static void main (String args [])
{
SwingUtilities.invokeLater (new Runnable ()
{
public void run ()
{
new RevalidateWhenSetChildInvisibleExample ().run ();
}
});
}
}
You could use a CardLayout and then swap the button with an empty JPanel.
Read the section from the Swing tutorial on How to Use CardLayout for more information and examples.
The problem is the layout manager, which is not really a problem here because it is just doing its job. You could set the layout to null and then set the bounds for every button; this way they will NEVER move unless you change their position.
panel.setLayout(null);
button1.setBounds(10,10,50,20);
button2.setBounds(70,10,50,20);
button3.setBounds(xPos,yPos,width,height);
Another way is to use the GridLayout:
contentPane.setLayout(new GridLayout());
I tested it, and it worked fine, since the component did not get removed it stays the same.
Also, you should add the following to your code:
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
This makes the program exits when the JFrame is closed; without it the program still runs at the background.

Java button creation not working properly

Here is my program.This program simply creates a frame using swings in java and create a grid first and adds button to that frame:
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
public class one {
private static void createAndShowGUI() {
JFrame frame = new JFrame("HelloWorldSwing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400,400);
frame.getContentPane().setPreferredSize(new Dimension(500,500));
frame.pack();
frame.setVisible(true);
Container pane=frame.getContentPane();
pane.setLayout(new GridLayout(5,6));
JButton[] buttons = new JButton[26];
String b[]={"A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"};
for(int i = 0; i<buttons.length; i++) {
buttons[i] = new JButton(b[i]);
buttons[i].setSize(80, 80);
buttons[i].setActionCommand(b[i]);
buttons[i].addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
String choice = e.getActionCommand();
JOptionPane.showMessageDialog(null, "You have clicked: "+choice);
}
});
System.out.println("adding button\n");
pane.add(buttons[i]);
}
}
public static void main(final String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
The program compiles fine.But the button creation i can see only one button that is "A" and rest of the buttons i am not able to see in the pane.
It turns out that this seems to be a race condition and/or system dependent type issue, since it works for others. In any case, the Javadoc for java.awt.Container.add() states:
This method changes layout-related information, and therefore,
invalidates the component hierarchy. If the container has already been
displayed, the hierarchy must be validated thereafter in order to
display the added component.
You should therefore call pane.validate() after you have added all components (or, as MadProgrammer says, execute the setVisible() call after you have added all your components).
There are, essentially two ways to fix this problem.
The first, is call frame.setVisible(true) AFTER you have completed adding the buttons to the pane.
The second is to call
pane.invalidate();
pane.repaint();
After you have added all the buttons to the pane
I can see buttons(A-Z). I could not reproduce your problem.

Swing getHeight() value changing from 0 to 16

I am trying to make a box in Swing that has a label of "user", a text field for the username, and a button "sign in". This is my code
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.io.*;
import java.util.*;
public class Engine
{
JFrame frame;
public void go()
{
setUpGui();
userNameScreen();
}
public void setUpGui()
{
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public void userNameScreen()
{
JPanel background = new JPanel();
frame.getContentPane().add(background);
JLabel labelUserName = new JLabel("User:");
background.add(labelUserName);
System.out.println(labelUserName.getHeight()); // 0
JTextField textFieldUserName = new JTextField();
System.out.println(labelUserName.getHeight()); // 16
textFieldUserName.setPreferredSize(new Dimension(110,labelUserName.getHeight()));
background.add(textFieldUserName);
JButton buttonSignIn = new JButton("Sign In");
background.add(buttonSignIn);
/*
background.add(labelUserName);
background.add(textFieldUserName);
background.add(buttonSignIn);
frame.getContentPane().add(background);
*/
frame.pack();
}
}
My driver class just creates an instance of engine, then runs the method go().
I read that Swing components do not have attributes of height/width until they are added (because that is for the layout manager to decide how much room they have), so it makes sense that in the method userNameScreen(), adding in all components at the end* (commented out here) makes the textFieldUserName variable have no height.
However, you can see in that same method userNameScreen(), I have it do
System.out.println(labelUserName.getHeight());
twice. The first time, it is 0. The second, it is 16. I don't understand why the first time, it would register it as 0. It has already been added to the panel (in the line before), and there doesn't seem to be anything that would change its height between that first println() and the next. So why is the value 0 in the first one, and why does it change to 16 almost immediately after?
*I should note, when I say adding in all the stuff commented out at the end, it also includes removing/commenting out all the same commands done elsewhere in the code.
It is a side effect from not creating/modifying your Swing components on the EDT. Now the EDT is busy doing the layout while you are adding components in another thread.
Your main method should look like:
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new Engine().go();
}
});
}
I'm not sure why this is happening but may be because the addition of the component maybe on a background thread and might not have been updated till the next statement is called and its updated a few millisecs later and appears when you call it second time.

Categories