Swing Class, JLabel appearence unusual - java

I noticed something that I considered to be strange it this following code:
public class QuestionFour extends JFrame {
private JTextArea txta1;
private JTextField txt1;
private JButton btnSort;
private JButton btnShuffle;
private JButton btnReverse;
private JPanel pnl1;
private JLabel lbl1;
private LinkedList linkedList;
public QuestionFour() {
super();
this.setLocationRelativeTo(null);
this.setSize(500, 200);
this.setVisible(true);
this.setLayout(new FlowLayout());
txt1 = new JTextField(); // 1
lbl1 = new JLabel("Enter a number: "); // 2
this.add(lbl1);
}
public static void main(String args[]) {
QuestionFour ob = new QuestionFour();
}
}
The problem that was occuring was, when I run the code the JLabel does not appear but if I comment the line where typed 1 as a comment, the JLabel appears, which I consider odd since I only instantiate the TextField but don't add it to the JFrame.
Can someone please explain this to me?

Possibly, it's an anomaly caused by invocation of setVisible(true) outside of UI thread. Try this one:
public QuestionFour()
{
setLocationRelativeTo(null);
this.setSize(500, 200);
setLayout(new FlowLayout());
this.txt1 = new JTextField(); // 1
this.lbl1 = new JLabel("Enter a number: "); // 2
this.add(this.lbl1);
javax.swing.SwingUtilities.invokeLater(() -> setVisible(true));
}
Notice: Read/write access to any UI component (such as JTextField etc.) must be done within Event Dispatching Thread (UI thread). SwingUtilities provides you convenient methods. You should invoke setVisible() within EDT, too.
One further problem was that you invoke setVisible(true) at the beginning and then add UI components, belatedly. This represents a "write access" to UI components ("you're adding something into the main panel"). Your class constructor doesn't run within EDT, so in this case you had to encapsulate this.add(this.lbl1) into the method of SwingUtilities. But it's better when you construct your entire UI first and then finally set it visible.
For more information about the Swing library and thread-safety take a look at this: https://docs.oracle.com/javase/tutorial/uiswing/concurrency/dispatch.html

Related

Style errors with swing: method creates an object but does not assign variable or field

"This method creates an object but does not assign this object to any variable or field. This implies that the class operates through side effects in the constructor, which is a bad pattern to use, as it adds unnecessary coupling. Consider pulling the side effect out of the constructor, into a separate method, or into the calling method."
This short test program runs as I expected, but I don't know how to address this checkstyle error. Most of the examples of using javax.swing seem to have this structure.
There is also a error causes by EXIT_ON_CLOSE, but without it the process lingers after I close the window and must be force quit.
public class GUI implements ActionListener {
private int clicks = 0;
private JLabel label = new JLabel("Clicks= " + clicks);
private JFrame frame = new JFrame();
public GUI() {
// make a Jbutton named button
JButton button = new JButton("Click Me");
button.addActionListener(this);
// arrange the button and label
JPanel panel = new JPanel();
panel.add(button);
panel.add(label);
// put the panel in a frame
frame.add(panel, BorderLayout.CENTER);
// EXIT_ON_CLOSE has a style error too.
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setTitle("Graphical User Interface");
frame.pack();
frame.setVisible(true);
}
// update label and number of clicks when button is clicked
public void actionPerformed(ActionEvent e) {
clicks++;
label.setText("Clicks= " + clicks);
}
// This is the code that InteliJ says has bad form.
public static void main(String[] args) {
new GUI();
} }
One bad pattern here is that you are not creating your GUI on Event Dispatch Thread. Not sure if this is related to your problem.. but you should always use EDT to create the GUI in Swing.
EDIT: take a look here. In the main method you just have "new GUI()" without a reference variable. Writing your app like this example you are not going to use the constructor to create all :)

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

int and boolean not changing after action performed

I copied all of the relevant code below, and my problem is that after running the action performed (which is connected to a button) the values I tried to change in the action performed didn't actually change.
I put a sout(ques) at the end of the action performed and I can see the change in value but when I move outside of it, it reverts back to the 0;
public class GameRunner extends JPanel implements ActionListener{
private int x=50,y=600;
private Ball b = new Ball(x,y);
private Timer timer;
private boolean correct , incorrect;
private JButton button;
private JTextField f;
private int ques = 0;
private String[][] math = {{"2X^2","4x"},{"q2","a2"},{"q3","a3"},{"q4","a4"},{"q5","a5"},
{"q6","a6"},{"q7","a7"},{"q8","a8"}};
public void actionPerformed(ActionEvent actionEvent) {
if (f.getText().equals(math[ques][1])) {
correct = true;
} else {
incorrect = true;
}
f.setText("");
if(ques<7)
ques++;
else
ques = 0;
System.out.println(ques);
//I can see the change here
}
public void paint(Graphics g){//called whenever refreshed...
System.out.println(ques);
// But now outside of the action performed the ques and the correct incorrect do not change
if(correct)
b.move();
if(incorrect)
b.move2();
}
public static void main(String[] args) {
GameRunner gui = new GameRunner ();
gui.go();
}
public void go(){
button = new JButton("Guess");
f = new JTextField(15);
button.addActionListener(this);
JFrame frame = new JFrame("Derivative Game");
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
frame.setPreferredSize(new Dimension(700, 700));
JPanel pan = new JPanel(new BorderLayout());
JPanel pan2 = new GameRunner();
JPanel pan3 = new JPanel();
pan3.add( f);
pan3.add(button);
pan3.setBackground(new Color(80, 218, 213));
pan.add( pan3,BorderLayout.CENTER);
pan.setBackground(new Color(80, 218, 213));
frame.add(pan2);
frame.getContentPane().add(BorderLayout.SOUTH, pan);
frame.setSize(700, 760);
frame.setVisible(true);
frame.setResizable(false);
}
}
The basic problem is that you've actually got two instances of GameRunner here: the one you create in main(), and another one that you add to the JFrame. Since you only call go() on the one not in the JFrame, that instance's paint() method will never be called.
You need to refactor your code to eliminate that second stray GameRunner instance. While you're at it, you should also use paintComponent() instead of paint(), and you should take any "business logic" (like those calls to move()) out of your painting code.
In other words, get rid of this line:
JPanel pan2 = new GameRunner();
Since you're already "in" an instance of GameRunner, you shouldn't be creating another one. Then to use the "current" instance of GameRunner, you can use the this keyword:
frame.add(this);
Edit- You also aren't telling your GameRunner JPanel to repaint itself after the button is clicked. You might want to add a call to repaint() in your actionPerformed() method.

Output layout of entities not as expected

I'm a beginner in Java, especially in the GUI Design area. I created a simple Java Swing interface to accept a string from the user and display it using a JOptionPane (ActionListener not yet implemented.)
The main problem I'm facing is with the alignment of the objects in the output. No matter what bounds I give to the objects, they always appear in one line. Also, sometimes, the output Frame will show absolutely nothing. After multiple runs, it will finally show me the objects, but not in the layout I expected them to be.
This is my code:
package guiapp;
import javax.swing.*;
import java.awt.*;
public class GUIApp {
private static JFrame frame;
private static JPanel panel;
private static JLabel label;
private static JTextField text;
private static JButton click;
public static void CreateGUI(){
frame = new JFrame("Hello to NetBeans!");
frame.setSize(750, 750);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel = new JPanel();
panel.setBackground(Color.BLUE);
label = new JLabel("Enter a string: ");
label.setBounds(50,35,150,40);
label.setVisible(true);
text = new JTextField();
text.setBounds(250,35,150,40);
text.setVisible(true);
click = new JButton("Click here!");
click.setBounds(150,80,150,40);
click.setVisible(true);
panel.add(text);
panel.add(label);
panel.add(click);
frame.add(panel);
frame.setVisible(true);
}
public static void main(String[] args) {
CreateGUI();
}
}
Can someone please tell me where I'm going wrong? I seem to have got the layout syntax wrong.
No matter what bounds I give to the objects, they always appear in one
line.
This is probably because of the default layout manager of JPanel: FlowLayout. On the other hand Swing is designed to be used with layout managers and the use of methods such as setBounds(...), setLocation(...) and setSize(...) is discouraged. See Laying out Components within a Container lesson.

JFrame not updating when I call repaint()

I have a chess game I'm trying to make, and I'm trying to call the repaint() method on one of my JFrames in the game loop. This particular JFrame shows the total kills for each player. I'm pretty sure repaint() is actually being called, but for some reason it doesn't seem to be updating my JLabels correctly, which are supposed to hold the number of kills values for each player.
Here's my code for the custom JFrame extension class which houses the JLabels representing the kills.
private ChessGame game;
private JPanel killsPanel;
private String p1kills;
private String p2kills;
private JLabel kills;
private JLabel p1, p2;
private JLabel p1NumKills = new JLabel();
private JLabel p2NumKills = new JLabel();
//the player's kill values are increasing and registering, just not within the jlabels representing them
public KillsFrame(ChessGame game){
this.game = game;
killsPanel = new JPanel(new MigLayout("", "[center][right][left][c]", "[top][center][b]"));
kills = new JLabel("KILLS");
p1 = new JLabel(game.getCurrentPlayer().getName() + " - ");
p2 = new JLabel(game.getOtherPlayer().getName() + " - ");
//this is the part that should be working but isn't.
//p1kills and p2kills aren't being updated or something.
p1kills = "" + game.getCurrentPlayer().getKills();
p2kills = "" + game.getOtherPlayer().getKills();
p1NumKills.setText(p1kills);
p1NumKills.setText(p2kills);
killsPanel.add(kills, "span");
killsPanel.add(p1);
killsPanel.add(p1NumKills, "wrap");
killsPanel.add(p2);
killsPanel.add(p2NumKills, "wrap");
killsPanel.setBackground(Color.lightGray);
add(killsPanel);
pack();
setTitle("Scoreboard");
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
setResizable(true);
setVisible(true);
setLocationRelativeTo(null);
}
Then I just call this frame's repaint() in a main method in a different class:
public static void main(String[] args) throws InterruptedException {
JFrame gameFrame = new ChessMain();
gameFrame.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
gameFrame.pack();
gameFrame.setResizable(true);
gameFrame.setLocationRelativeTo(null);
gameFrame.setVisible(true);
gameFrame.setTitle("Chess X");
LoginFrame loginFrame = new LoginFrame(((ChessMain) gameFrame).getGame());
System.out.println(((ChessMain) gameFrame).getGame().toString());
System.out.println(((ChessMain) gameFrame).getGame().currentPlayer.getName()+ ", it's your turn.");
//this is what creates the kills frame.
KillsFrame kf = new KillsFrame(((ChessMain) gameFrame).getGame());
while(true){
((ChessMain) gameFrame).getGame().run();
kf.repaint();//*************************************
Thread.sleep(1);
}
}
Any and all help is greatly appreciated.
Your error lies when you are trying to "sleep" your main thread. This is bound to cause errors if not make your GUI non-responsive. Your best bet is to use the swing.Timer class (tutorial here http://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html). Fire events at regular intervals to repaint(). This will result in a smooth animation.
if for some reason you Have to sleep a thread in a swing program, create a new thread using the SwingWorker class (And not otherwise). see http://docs.oracle.com/javase/7/docs/api/javax/swing/SwingWorker.html#process(java.util.List)

Categories