I'm making a Hangman game and it seems that my code doesn't provide me much freedom with using layouts. I added an image to my JFrame then I added a JPanel to my image which I'm using for all the JLabels and JTextFields but it seems to me that its inefficient because in order to change the layout of my JTextFields or JLabels I have to change the layout of my image which messes up the entire looks of the game. How can I make this code more efficient and give myself more freedom to change the layouts of my JLabels and JTextFields without messing everything up? Thanks for the help in advanced.
/*PACKAGE DECLARATION*/
package Game;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
/************************
* GAME MECHANICS CLASS *
* **********************/
public class GameStructure {
/* INSTANCE DECLARATIONS */
private String []wordList = {"computer","java","activity","alaska","appearance","article",
"automobile","basket","birthday","canada","central","character","chicken","chosen",
"cutting","daily","darkness","diagram","disappear","driving","effort","establish","exact",
"establishment","fifteen","football","foreign","frequently","frighten","function","gradually",
"hurried","identity","importance","impossible","invented","italian","journey","lincoln",
"london","massage","minerals","outer","paint","particles","personal","physical","progress",
"quarter","recognise","replace","rhythm","situation","slightly","steady","stepped",
"strike","successful","sudden","terrible","traffic","unusual","volume","yesterday"};
private int []length = new int [64];
private JTextField tf;//text field instance variable (used)
private JLabel jl2;//label instance variable (used)
private JLabel jl3;//label instance (working on)
private String letter;
/*****************
* LENGTH METHOD *
* ***************/
public void length(){
jl3 = new JLabel();
int j = 0;
for(j = 0; j<64; j++) {
length[j] = wordList[j].length();//gets length of words in wordList
}//end for
int l = 0;
String line = "";
//create line first then put into .setText
for(int m = 0; m<length[l]; m++) {
line += "__ ";
l++;
}//end for
jl3.setText(line);
}//end length method
/*****************
* WINDOW METHOD *
* ***************/
public void window() {
LoadImageApp i = new LoadImageApp();//calling image class
JFrame gameFrame = new JFrame();//declaration
JPanel jp = new JPanel();
//JPanel jp2 = new JPanel();//jpanel for blanks
JLabel jl = new JLabel("Enter a Letter:");//prompt with label
jl.setFont(new Font("Rockwell", Font.PLAIN, 20));//set font
tf = new JTextField(1);//length of text field by character
jl2 = new JLabel("Letters Used: ");
tf.setFont(new Font("Rockwell", Font.PLAIN, 20));//set font
jl2.setFont(new Font("Rockwell", Font.PLAIN, 20));//set font
jp.add(jl);//add label to panel
jp.add(tf);//add text field to panel
jp.add(jl2);//add letters used
gameFrame.add(i); //adds background image to window
i.add(jp); // adds panel containing label to background image panel
gameFrame.setTitle("Hangman");//title of frame window
gameFrame.setSize(850, 600);//sets size of frame
gameFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//exit when 'x' button pressed
gameFrame.setIconImage(new ImageIcon("Hangman-Game-grey.png").getImage());//set the frame icon to an image loaded from a file
gameFrame.setLocationRelativeTo(null);//window centered
gameFrame.setResizable(false);//user can not resize window
gameFrame.setVisible(true);//display frame
}//end window method
/*********************
* USER INPUT METHOD *
* *******************/
public void userInput() {
tf.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {//when enter key pressed
JTextField tf = (JTextField)e.getSource();
letter = tf.getText();
jl2.setText(jl2.getText() + letter + " ");//sets jlabel text to users entered letter
}//end actionPerformed method
});
}//end userInput method
}//end GameMechanics class
/*PACKAGE DECLARATION*/
package Game;
/***********************
* IMPORT DECLARATIONS *
* *********************/
import java.awt.BorderLayout;
import java.awt.Graphics;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
/***************
* IMAGE CLASS *
* *************/
public class LoadImageApp extends JPanel {
private static final long serialVersionUID = 1L;
private ImageIcon image;
/***********************
* PAINT IMAGE METHOD *
* *********************/
public void paintComponent (Graphics g) {
//setLayout(new BorderLayout());
super.paintComponent(g);
image = new ImageIcon("hangman.png");//image name & type
image.paintIcon(this, g, 270, 20);
}//end paintComponent method
}//end LoadImageApp class
/*PACKAGE DECLARATION*/
package Game;
/*******************
* GAME MAIN CLASS *
* *****************/
public class GameMain {
/***************
* MAIN METHOD *
* *************/
public static void main (String []args) {
GameStructure game = new GameStructure();//declaration
game.length();
game.window();
game.userInput();
}//end main method
}//end GameMain class
Some suggestions:
Don't override a JPanel's paint(...) method, but rather its paintComponent(Graphics g) method, not unless you need to change how it renders its child components or its borders (you don't). Also by doing this you gain some Swing graphics advantages including automatic double buffering.
Never read in an image into the paint or paintComponent method. These methods are one of the main determinants of how responsive your GUI appears to the user, and so you never want to do file I/O in the method. And also, why have code that inefficiently re-reads the same image in whenever paint or paintComponent is called? Why not simply store the image or ImageIcon in a variable once, and be done with it?
Learn and use the layout managers
JPanels that go over drawing or image rendering JPanels often should be non-opaque - so be sure to call setOpaque(false) on them, and also on some other overlying Swing components.
_________________________
Edit
For example, here is my SSCCE that shows an example of getting an image (here off of the internet) in a class constructor. Also note that my SSCCE will work on any computer connected to the internet since it does not require image files, unlike yours. Also code not related to displaying the GUI has been cut out making the remaining code more pertinent to the problem. Consider doing this next time you post an SSCCE.
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.*;
class GameStructure {
private JTextField tf;
private JLabel jl2;
public void window() {
LoadImageApp loadImageApp = new LoadImageApp();
JFrame gameFrame = new JFrame();
JPanel jp = new JPanel();
jp.setOpaque(false); //!!
jp.setBorder(BorderFactory.createTitledBorder("jp"));
JLabel jl = new JLabel("Enter a Letter:");
jl.setFont(new Font("Rockwell", Font.PLAIN, 20));
tf = new JTextField(1);
jl2 = new JLabel("Letters Used: ");
tf.setFont(new Font("Rockwell", Font.PLAIN, 20));
jl2.setFont(new Font("Rockwell", Font.PLAIN, 20));
jp.add(jl);
jp.add(tf);
jp.add(jl2);
gameFrame.add(loadImageApp);
loadImageApp.add(jp);
gameFrame.setTitle("Hangman");
gameFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// gameFrame.setIconImage(
// new ImageIcon("Hangman-Game-grey.png").getImage());
gameFrame.setResizable(false);
gameFrame.pack();
gameFrame.setLocationRelativeTo(null);
gameFrame.setVisible(true);
}
}
class LoadImageApp extends JPanel {
private static final long serialVersionUID = 1L;
private static final int PREF_W = 850;
private static final int PREF_H = 600;
private BufferedImage img;
public LoadImageApp() {
// just used as an example public image
String spec = "https://duke.kenai.com/"
+ "SunRIP/.Midsize/SunRIP.png.png";
URL url;
try {
url = new URL(spec);
img = ImageIO.read(url);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (img != null) {
g.drawImage(img, 0, 0, getWidth(), getHeight(), this);
}
}
}
public class GameMain {
public static void main(String[] args) {
GameStructure game = new GameStructure();
game.window();
}
}
I wonder if there is a way to change to source code format automatically produced
by Net Beans IDE in GUI - applet applications. For example placement of the items in the source code are relational but what if I want them in absolute coordinates. I am asking this question because I need source code in that format so that I can easily change source code and can do some manual job. More specially, I want to create a Button Group of 12x8 array with no gap between them . But using IDE to do this takes long time and indeed, I couldn't even placed the buttons with no gap between them. Any help highly appreciated!
This is simple to put together manually. GUI builders usually harm more than they help.
Here's the test run:
And here's the code. I put the classes together in one file to make it easier to paste. The classes should be in separate files.
import java.awt.GridLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class ButtonArray implements Runnable {
#Override
public void run() {
JFrame frame = new JFrame("JButton Array Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ButtonPanel buttonPanel = new ButtonPanel();
frame.add(buttonPanel.getMainPanel());
frame.setLocationByPlatform(true);
// frame.setSize(new Dimension(800, 600));
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new ButtonArray());
}
public class ButtonPanel {
private static final int WIDTH = 12;
private static final int HEIGHT = 8;
private JButton[][] buttonArray;
private JPanel mainPanel;
public ButtonPanel() {
buttonArray = new JButton[WIDTH][HEIGHT];
createPartControl();
}
private void createPartControl() {
mainPanel = new JPanel();
mainPanel.setLayout(new GridLayout(HEIGHT, WIDTH));
for (int i = 0; i < HEIGHT; i++) {
for (int j = 0; j < WIDTH; j++) {
buttonArray[j][i] =
new JButton(createButtonText(j, i));
mainPanel.add(buttonArray[j][i]);
}
}
}
private String createButtonText(int j, int i) {
StringBuilder builder = new StringBuilder();
builder.append("(");
builder.append(i);
builder.append(", ");
builder.append(j);
builder.append(")");
return builder.toString();
}
public JPanel getMainPanel() {
return mainPanel;
}
}
}
You need to use some grid like layout for the panel (ex. FormLayout) configure it and simply add all buttons there.
Hello I am trying to solve the following problem: Write a program that prompts the user to enter the x- and y-positions of a center point and a radius, using text fields. When the user clicks a "Draw" button, draw a circle with that center and radius in a component. I do not see what is wrong in my code but something is because it doesnt seem like repaint() is invoking paintComponent() as message will change to TESTING 1 but not TESTING 2 and no drawing is made.
My Code:
import java.util.*;
import javax.swing.*;
import java.awt.*;
import java.applet.*;
import java.awt.geom.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class q3{
public static class cgPanel extends JPanel{
private static double x;
private static double y;
private static double r;
private static JTextField xField;
private static JTextField yField;
private static JTextField rField;
private static JButton draw;
private static JLabel message;
//This is all just Layout work.
public cgPanel(){
setLayout(new BorderLayout());
JPanel drawPanel = new JPanel();
drawPanel.setBackground(Color.WHITE);
add(drawPanel, BorderLayout.CENTER);
message = new JLabel("");
JPanel sub1ForSub1 = new JPanel();
sub1ForSub1.add(message);
JLabel coordinates = new JLabel("Coordinates:");
JPanel sub2ForSub1 = new JPanel();
sub2ForSub1.add(coordinates);
JPanel subPanel1 = new JPanel();
subPanel1.setLayout(new GridLayout(2, 1));
subPanel1.add(sub1ForSub1);
subPanel1.add(sub2ForSub1);
JLabel xLabel = new JLabel("x:");
xField = new JTextField(4);
JLabel yLabel = new JLabel(" y:");
yField = new JTextField(4);
JLabel rLabel = new JLabel(" Radius:");
rField = new JTextField(4);
JPanel subPanel2 = new JPanel();
subPanel2.add(xLabel);
subPanel2.add(xField);
subPanel2.add(yLabel);
subPanel2.add(yField);
subPanel2.add(rLabel);
subPanel2.add(rField);
draw = new JButton("Draw");
ActionListener bL = new ButtonListener();
draw.addActionListener(bL);
JPanel subPanel3 = new JPanel();
subPanel3.add(draw);
JPanel Panel = new JPanel();
Panel.setLayout(new BorderLayout());
Panel.add(subPanel1, BorderLayout.NORTH);
Panel.add(subPanel2, BorderLayout.CENTER);
Panel.add(subPanel3, BorderLayout.SOUTH);
add(Panel, BorderLayout.SOUTH);
setVisible(true);
}
static class ButtonListener extends JComponent implements ActionListener{
public void actionPerformed(ActionEvent e){
try{
String xString = xField.getText();
String yString = yField.getText();
String rString = rField.getText();
message.setText("TESTING 1");
x = Double.parseDouble(xString);
y = Double.parseDouble(yString);
r = Double.parseDouble(rString);
repaint();
}
catch (NumberFormatException exception){
message.setText("Please enter a number.");
}
}
//This is where I cant seem to get the code in paintComponent to run when the Draw button is pressed.
public void paintComponent(Graphics g){
Graphics2D g2 = (Graphics2D) g;
Ellipse2D.Double circle = new Ellipse2D.Double(x - r, y - r, r*2, r*2);
g2.draw(circle);
message.setText("TESTING 2");
}
}
}
public static void main(String[] args){
JFrame frame = new JFrame();
frame.setSize(800, 800);
frame.setTitle("Circle Generator");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
cgPanel panel = new cgPanel();
frame.add(panel);
frame.setVisible(true);
}
}
So, a couple of things.
Your problem stems from that fact that your ButtonListener is extending a JComponent, so the repaint() method is calling the one for the ButtonListener (which really isn't a JComponent).
And the paintComponent method is also for the the ButtonListener.
Instead, you want your button listener to have access to your cgPanel, so it can tell IT to repaint. And your paintComponent needs to be moved to the cgPanel, but even then you probably don't want it there since you have a bunch of other components on cgPanel.
It's not clear from your code where you really want the circle to be drawn.
You should probably create a CirclePanel that extend JPanel, and overrides paintComponent to draw your circles, and then add that to your cgPanel. Then make your ButtonListener tell the CirclPanel instance to repaint.
Okay so I figured out the whole color business using HTML, but now when I put it in my Jlabel its not working! Might it have anything to do with the fact that i am using getText() from a TextArea to set the text of my Jlabel? Here's my code:
String air = "<html>\n" +
"<ul><font color=blue>blue</font>\n" +
"</ul>\n";
...
JLabel jl = new JLabel();
jl.setSize(700,700);
frame.add(jl);
jl.setText(environment.getText());
I get this....
<html><ul><font color=blue> text </font></ul>
I tried your code and it's work properly.
Can you post complete code ? Are you running java applet ?
I found similar issue in here : HTML no longer working in JLabel (and other components).
Hope it's can help you.
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextArea;
public class JLabelSample extends JFrame {
/**
*
*/
private static final long serialVersionUID = 1L;
public static void main(String[] args) {
JFrame frame = new JLabelSample();
frame.setVisible(true);
frame.setSize(400, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
String air = "<html>\n" + "<ul><font color=blue>blue</font>\n"
+ "</ul>\n";
JTextArea area = new JTextArea(air);
frame.add(area);
JLabel jl = new JLabel();
jl.setSize(100, 100);
frame.add(jl);
jl.setText(area.getText());
}
}
I've been working on this for some time, and I'd really appreciate some help right now.
I'm trying to get the JFrame containing the text input fields to close from my actionPerformed method, but I can't seem to get anything to work. JFrame.dispose wont let me access the right Jframe, and setVisible(false) is equally useless, unless I'm doing this completely wrong.
//halp
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
class PersonInput extends JPanel
implements ActionListener {
//Fields for data entry
private JFormattedTextField firstField, lastField, dateField;
public String x[] = new String[3];
public PersonInput() {
//Values for the fields
String first = "First Name";
String last = "Last Name";
String date = "MM/DD/YYYY";
//Create the text fields and set them up.
firstField = new JFormattedTextField();
firstField.setValue(new String(first));
lastField = new JFormattedTextField();
lastField.setValue(new String(last));
dateField = new JFormattedTextField();
dateField.setValue(new String(date));
dateField.setColumns(10);
JButton ok = new JButton("OK");
ok.setVerticalTextPosition(AbstractButton.BOTTOM);
ok.setHorizontalTextPosition(AbstractButton.CENTER);
ok.setActionCommand("ok");
ok.addActionListener(this);
ok.setToolTipText("Confirms user input and continues with the program.");
JPanel buttons = new JPanel(new GridLayout(0,1));
buttons.add(ok);
//Layout the text fields in a panel.
JPanel fieldPane = new JPanel(new GridLayout(0,1));
fieldPane.add(firstField);
fieldPane.add(lastField);
fieldPane.add(dateField);
//Put the panels in this panel, labels on left,
//text fields on right.
setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
add(fieldPane, BorderLayout.CENTER);
add(buttons, BorderLayout.LINE_END);
}
public void actionPerformed(ActionEvent e) {
if ("ok".equals(e.getActionCommand()))
{
JFrame frame1 = new JFrame("People Sorter");
x[0] = firstField.getText();
x[1] = lastField.getText();
x[2] = dateField.getText();
JOptionPane.showMessageDialog(frame1, "Person has been added.");
dispPerson();
frame.setVisible(false);
}
}
public void dispPerson()
{
System.out.println(x[0] + x[1] + x[2]);
}
public static void createAndShowGUI() {
//Create and set up the window.
JFrame frame = new JFrame("Person Input");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Add contents to the window.
frame.add(new PersonInput());
//Display the window.
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
//Turn off metal's use of bold fonts
UIManager.put("swing.boldMetal", Boolean.FALSE);
createAndShowGUI();
}
});
}
}
I'm all ears if anyone has any ideas; I've been stressed over this all day. Thanks much for lending me your time!
EDIT: Just for clarification, the frame I'm trying to close is the one instantiated in the createAndShowGUI method.
it seems that the problem is that we are trying to merge both static and non static contents. For a short explanation static contents can be referred without need of creating an instance (object) of that class. Which means that createAndShowGUI can be called:
inside another static method (like main)
From class reference PersonInput.createAndShowGUI()
or from an object, but that method or attribute will be always the same, static attributes are shared.
I can suggest 2 ways to solve your problem.
One is pass the object frame to PersonInput
//halp
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
class PersonInput extends JPanel
implements ActionListener {
//Fields for data entry
private JFormattedTextField firstField, lastField, dateField;
public String x[] = new String[3];
JFrame frame;
public PersonInput(JFrame frame) {
this.frame = frame;
//the rest of your code
}
The other way is to have the frame object outside the method and declare it static.
static JFrame frame = new JFrame("Person Input");;
public static void createAndShowGUI() {
//Create and set up the window.
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Add contents to the window.
frame.add(new PersonInput());
//Display the window.
frame.pack();
frame.setVisible(true);
}
Remember that static variable cannot be referenced from a static context