I'm making a Chess-like game in Java Swing (I know Swing isn't the proper way to go about it but I want to attempt it nevertheless)
I am having a problem with making the chess pieces show up on the display at the proper paces. Due to the nature of the chess pieces physical positions, I cannot use a layout manager.
The code looks something like this (it's admittedly awkwardly designed) :
public class Window extends JFrame {
private JPanel board;
public Window() {
super();
board = new JPanel();
JLabelOrganizer jlo = new JLabelOrganizer();
for (JLabel: JLabelOrganizer) {
JLabel.setBounds(calcX(), calcY(), width, height);
board.add(JLabel);
}
board.setBounds(x, y, width1, height1);
board.setLayout(null);
add(board);
setLayout(null);
}
public class JLabelOrganizer {
public JLabelOrganizer {
instantiate Type1 and Type2 JLabel objects and store them
}
public class Type1 extends JLabel {
}
public class Type2 extends JLabel {
}
}
}
The classes Type1 and Type2 represent the pieces.
When this runs, the JLabels (Type1 and Type2) do not show up at the correct place as designated from setBounds(). However, the board JPanel holding these pieces is set up at the correct place as designated from the call of its own setBounds().
Does anything have an idea as to why this is happening? Could it be because I'm inheriting from JLabel or the JLabel classes are inner classes? Thanks.
Edit: Forgot to specify that the JLabels only show up in the upper left hand corner of where the JPanel is located no matter what x and y positions I set them to.
I found the answer after looking at another post.
My class inheriting JLabel won't show up but if I change the type to JLabel it does
I accidentally overrided JLabel's getX() and getY() for my own purposes. Java Swing uses a component's getX() and getY() which is why the change with setBounds() was not being reflected.
Related
I have 2 issues.
First issue: I have to set the JFrame as non resizable, however, an error is thrown up when I enter in frame.setResizable(false);
Second issue: I have ran into the problem of the JFrame not fitting the component within it perfectly. I have set the dimensions for the JFrame to 600x720 and the board component as being 600x600. However, when I extend the JFrame I can see that there is more of the component to be revealed. How would I change this to accommodate the component to fit snugly but also leave space for another component of size 100x120? My understanding of this is that JFrame sets the size with the borders included, however, I want the space within the JFrame to be exactly 600x720 pixels without including the border.
The code is set out below.
Game Class
package snake;
//This class is used to run the game.
public class Game {
/**
* #author HyperBlue
*/
//Declaring a static variable of type Board. This can be accessed from anywhere in the program. The fact that it is static means that it cannot be edited.
public static Board board;
public static void main(String[] args) {
// TODO Auto-generated method stub
//Creates an object board from the Board() construct
board = new Board();
}
}
Board Class
package snake;
//Importing allows us to use pre-defined classes in Java, this contains its methods. We can also import entire packages which contain a number of classes in that package.
//This class allows us to assign/capture the width and height of an object.
import java.awt.Dimension;
//The Toolkit is an abstract class containing abstract and (possibly) non-abstract methods. Abstract classes cannot be instantiated (i.e. we cannot make an object from them). Abstract methods have no body (no code), for example we declare it as "public abstract boolean isChanged() ;", the semi colon shows it has no body (i.e. no {}).
import java.awt.Toolkit;
//ActionEvent gets information about an event (input) and its source. You can create an object from this.
import java.awt.event.ActionEvent;
//The ActionListener defines what should be done when a certain action is performed by the user.
import java.awt.event.ActionListener;
//This imports the JFrame class from the swing package.
import javax.swing.JFrame;
import javax.swing.Timer;
//This class is used to create the game board.
//The ActionListener is implemented because it is implementing an interface. What ActionListener does is it handles events; the ActionListener defines what should be done when a certain action is performed by the user.
public class Board implements ActionListener {
//The JFrame is the window in which everything will be placed into, this will provide the framed window (what is visible to us) in which the game will run in. We are creating a variable frame of type JFrame.
public JFrame frame;
//Creating a variable drawBoard of type DrawBoard. This allows us to add the component of drawBoard to the Board.
public DrawBoard drawBoard;
//Defining a new Timer called ticker. This is using the form new Timer(int delay in milliseconds, ActionListener listener). What the timer does is it allows threads to schedule the execution of instructions. In this case to constantly refresh the drawBoard component at regular intervals. This will give the appearance of motion. What "this" does is it is in reference to the current instance,
public Timer ticker = new Timer(20, this);
//This is a constructor for the class Board. This will allow us to create an object.
public Board() {
//Making an instance of dimension dim and assigning it to the size of the screen.
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
//Declaring instance of the JFrame 'frame'. This JFrame is called to declare a title for this frame - "Snake".
frame = new JFrame("Snake");
//JFrame is initially set to invisible, so we use the setVisible method (setting it to true) to make the JFrame 'frame' visible.
frame.setVisible(true);
frame.setPreferredSize(new Dimension(600, 720));
frame.getContentPane().add(drawBoard = new DrawBoard());
frame.pack();
//What this does is it places the JFrame 'frame' into the middle of the user's screen, this diminishes the issue of not all screens being the same resolution and size. This is done by setting the (x, y) position of the JPanel. For example, the x position is gained by dividing the size of the monitor by 2 and negating the size of the JPanel by 2 from that value, this places it in the middle of the screen's x axis. This is true for the y-axis too.
frame.setLocation((dim.width / 2) - (frame.getWidth() / 2), (dim.height / 2) - (frame.getHeight() / 2));
//Sets the operation which will happen when the user closes the JFrame 'frame', the EXIT_ON_CLOSE exits the application using the System exit method. This means that when the JFrame is closed, the application will be exited (closed).
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Starts the timer, this starts is sending action events to its listeners.
ticker.start();
}
//Overriding the actionPerformed method from the ActionListener class.
#Override
//This is the actionPerformed method. We parse it the ActionEvent e, what this is is an object which gives information about the event and its source. This allows us to perform an action based upon a specific event (e.g. a keyboard key pressed).
public void actionPerformed(ActionEvent e) {
//This repaints this component for every tick
//drawBoard.repaint();
}
}
DrawBoard Class
package snake;
//Allows use of default sRGB colours.
import java.awt.Color;
//Graphics is an abstract class that allows us to draw onto components.
import java.awt.Graphics;
import javax.swing.JPanel;
//Warnings will not be thrown (are suppressed).
#SuppressWarnings("serial")
//This class is used to create the board component in which the snake can move on.
//What extending does is it allows us to inherit the methods and attributes (properties) of another class. In this case, the DrawBoard class (subclass - inherits state and behaviour from all of its ancestors) inherits properties from the JPanel class (superclass - gives properties to its subclasses).
public class DrawBoard extends JPanel{
//Declaring the colour 'yellow' as the hex colour code (turned to decimal using a hex calculator so Java can use it) which was chosen in the design stage.
public static Color yellow = new Color(13816442);
//We are overriding the protected method in order to define our own body (and properties) for the paintComponent method. Overriding this allows us to define how we will paint the component DrawBoard. Protected means that it can only be accessed by things within the same package.
#Override
//A component is an object which has a graphical representation that can interact with the user (e.g. buttons).
//What this does is it paints the component using the graphics class, defined as instance g.
protected void paintComponent(Graphics g) {
//'Super.' refers to the method calling its super class, which in this case is JPanel. Doing this allows me to use in built 'drawings' such as rectangle and oval, which can be drawn by calling their methods.
super.paintComponent(g);
//Setting the colour in which graphics objects are made to the colour defined in the colour 'yellow'
g.setColor(yellow);
//Filling in a rectangle which starts at the point (0, 120) - [this is from the top left of the screen, with (0, 120) referring to 120 pixels down] and has a width and height of (600, 600), in other words provides a background of colour 'yellow' defined.
g.fillRect(0, 120, 600, 600);
}
}
Regarding:
How to Get JPanel to Fit Snugly With It's Components?
Let the layout managers do this work for you.
Suggestions:
Don't set sizes or preferred sizes.
Instead let the component's preferred size and your layout managers do the sizing for you.
If you do need to actively have a hand in setting some sizes, override getPreferredSize() and return an appropriate dimension, but do so taking care not to upset the preferred size of the constituent components. This must be done with care.
Re " I have set the dimensions for the JFrame to 600x720 and the board component as being 600x600. However, when I extend the JFrame I can see that there is more of the component to be revealed." -- You're forgetting the size of the top bar of the JFrame, something that may change size depending on the look and feel. Again, don't set the JFrame's size, and this will be a moot point.
To center a JFrame, simply call frame.setLocationRelativeTo(null); after it has been packed.
Avoid over-use of comments as these make your code nearly unreadable.
If you have problems and need help with an error such as you mention here: "I have to set the JFrame as non resizable, however, an error is thrown up when I enter in frame.setResizable(false);", then show the offending code and the error message.
Don't call frame.setVisible(true); until all components have been added and the JFrame has been packed.
For example
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.*;
import javax.swing.*;
public class Game {
public static Board board;
public static void main(String[] args) {
board = new Board();
}
}
class Board implements ActionListener {
public JFrame frame;
public DrawBoard drawBoard;
public Timer ticker = new Timer(20, this);
public Board() {
frame = new JFrame("Snake");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// frame.setPreferredSize(new Dimension(600, 720));
frame.getContentPane().add(drawBoard = new DrawBoard(), BorderLayout.CENTER);
frame.getContentPane().add(new BottomComponent(), BorderLayout.PAGE_END);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
ticker.start();
}
#Override
public void actionPerformed(ActionEvent e) {
}
}
class DrawBoard extends JPanel {
private static final int PREF_W = 600;
private static final int PREF_H = PREF_W;
public static Color yellow = new Color(13816442);
public DrawBoard() {
setBorder(BorderFactory.createTitledBorder("Draw Board"));
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(yellow);
g.fillRect(0, 120, 600, 600);
}
}
class BottomComponent extends JPanel {
private static final int PREF_W = 600;
private static final int PREF_H = 120;
public BottomComponent() {
setBorder(BorderFactory.createTitledBorder("Bottom Component"));
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
}
I'm developing a software which paints 2 different JPanel for my GUI: a score and a mast guitar. The score is a class which extends JPanel and has paintComponent() method like this:
public class PanelPartitura extends JPanel implements MouseListener{
public void paintComponent(Graphics comp){
super.paintComponent(comp);
comp2D = (Graphics2D)comp;
comp2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
paintBackground();
paintStave();
paintNotes();
[...]
}
}
The mast guitar is a class as well:
public class PanelGuitarra extends JPanel implements MouseListener
public void paintComponent(Graphics comp){
super.paintComponent(comp);
comp2D = (Graphics2D)comp;
comp2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
//Then I call secondary methods to paint it:
paintBackground();
PaintPoints();
}
[...]
}
It still works fine. I add the class PanelPartitura to a JScrollPane in order to scroll when it's playing:
partitura = new PanelPartitura();
JScrollPartitura = new JScrollPane(partitura, JScrollPane.VERTICAL_SCROLLBAR_NEVER, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
Both JPanels mix each others painted components when the software is playing and scrolling. I would like to ask, if somebody has a clue about what on earth is going on? In my opinion:
It could be because I separated the painting methods as we've seen above:
paintBackground();
paintStave();
paintNotes();
then, when the software starts to paint, it paints some parts of the first JPanel (paintBackground() for example) and then some parts of the mast guitar (paintBackground()), then it changes again and the result is a mixture of both.
I think this is because it mixes different parts every time, I mean it doesn't behave in the same way every time it plays.
I really don't want this to be happening, so let me ask you: how can I make atomic methods to be sure this wouldn't be the problem?
I missunderstood the scroll method. I scroll on this way:
//the note playing currently position is saved in positionBar
positionBar = 150 + 50*PGuitarra.composicion.getAcordeSeleccionado().getPosicionXAcorde();
//horizontalScrollBar is moved to this position
PGuitarra.JScrollPartitura.getHorizontalScrollBar().setValue(positionBar);
I see that your paint methods are not using the same Graphics object (at the JPanel scope). Could that be the reason? And if it is, try passing comp (the Graphics object) as a parameter to paintBackground, paintStave and paintNotes.
So I am working on a Java game using a combination of swing components, and Graphics2D.
I have an AbstractLevelView class which contains all of the elements of the game inside.
This LevelView component uses a BorderLayout to display a context bar on the left, and a JPanel containing the game grid in the center.
My context bar and gridPanel all work fine, but what I want to do is paint "messages" directly to the AbstractLevelView, but I can't get any painting to work. This is presumably because there is nowhere to paint due to the nested components.
I've included an abbreviated version of my AbstractLevelView, as well as a test paintComponent method. I have tried using paintComponents, but it doesn't even get called.
Does anyone know what I can do so I can paint on top of my nested components?
public abstract class AbstractLevelView extends AbstractGameView {
private JPanel gridPanel;
private ContextBar contextBar;
public AbstractLevelView(){
gridPanel = new JPanel();
add(gridPanel, BorderLayout.CENTER);
contextBar = new ContextBar();
add(contextBar, BorderLayout.WEST);
}
#Override
public void paintComponent(Graphics g){
System.out.println("foo");
Image img = ImageManager.getImage("img.jpg");
g.drawImage(img, 0, 0, 300, 300, this);
}
}
Thank you MadProgrammer, for suggesting JLayeredPanes. I had attempted to use those back in the earlier stages of my game, but now that I know more about them, I've successfully implemented a solution.
I'm not quite sure exactly how Graphics work in Java so I having trouble debugging my problem.
I have a Class MyFrame Extends JPanel. Now I do some drawing on MyFrame save and save me properties about that certain frame then add it an ArrayList collection of MyFrames. Later on I want to recall the properties a certain instance of MyFrame and repaint that frame on the screen. Having the the graphic of frame show up again is what I am having trouble with.
Here is a quick bit of code that will demonstrate my problem.
public class MyFrame extends JPanel{
private int property;
private int x;
private int y;
public MyFrame(int xp, int yp){
x = xp;
y = yp;
}
#override
public void paintComponent(Graphics g){
super.paintComponent(g);
g.fillRect(x,y,5,5);
}
public void setProperty(int p){
property = p;
}
}
public class MainClass() extends JPanel{
private ArrayList<MyFrame> frames = new ArrayList<MyFrame>;
private MyFrame currentFrame = new MyFrame();
public void addFrame(int x, int y){
this.remove(currentFrame);
currentFrame = new MyFrame();
this.add(currentFrame);
frames.add(currentFrame);
}
public void setFrame(int frame){
this.remove(currentFrame);
currentFrame = frames.get(frame);
this.add(currentFrame);
}
}
Summary:
I'd like the panel to display the correct frame when setFrame is called. currently when I do this set frame will be blank.
You seem to be swapping JPanels in and out of another JPanel, and when doing this, you must take into consideration the layout used by the container-JPanel and you would need to call revalidate and repaint on the container-JPanel after the swap.
But rather than mess with all of this, why not go the easy route by just swaping JPanels or perhaps JLabels with ImageIcons holding an image using a CardLayout?
You could also consider adding some states, an enum data member, to a single JPanel and ask it to switch from one state to another, then call its repaint method and, in its paintComponent overriden method, draw some graphics according to its state.
Your architecture (as enhance by #Hovercraft Full of Eels is also good, even better if the draw methods vary quite a lot and have very different purposes. However, my proposal could lay to a faster app, and could allow other fatures such as transition between states, shared double buffer, variable/code reuse in case graphics are close.
What is your app doing ?
Regards,
Stéphane
I'm Stuck with how to resize the third button to became the same size like the other two and place it on the bottom.
class ControlFrame extends JFrame
implements Runnable
{
JButton jb_inc = new JButton();
JButton jb_zero = new JButton();
JButton jb_dec = new JButton();
ControlFrame() {
super("Control Frame");
this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
ControlFrame(int x,int y, int w, int h) {
this();
this.setBounds(x, y, w, h);
this.setVisible(true);
jb_inc.setBounds(10,10,90,20);
jb_zero.setBounds(10,40,90,20);
jb_dec.setBounds(10,60,90,20);
jb_inc.setVisible(true);
jb_zero.setVisible(true);
jb_dec.setVisible(true);
this.getContentPane().add(jb_inc);
this.getContentPane().add(jb_zero);
this.getContentPane().add(jb_dec);
}
public void run() {
}
}
public class Counting_Machine
{
public static void main(String[] args) {
ControlFrame cf = new ControlFrame(0,200,80,150);
}
}
I suggest you start by reading the section from the Swing tutorial on Using Layout ManagersYour code won't work because you are trying to add 3 components to the "center" of the BorderLayout which won't work. Choose a LayoutManager that better meets your needs.
There is no need to use the setBounds(...) method when using a layout manager. The job of the layout manager is position the components based on the rules of the layout manager. The preferred size of each button should be the same since you have not added any text to the buttons.
Also, there is not need to invoke setVisible() on the buttons, all Swing components are visible by default, except the top level containers (JFrame, JDialog).
Two suggestions:
1) put common things into the instance initialization block. Personally I shudder whenever I see a call to "this()".
{
this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
ControlFrame()
{
super("Control Frame");
}
ControlFrame(int x,int y, int w, int h)
{
super("Control Frame");
...
}
2) I would get rid of the x, y, w, h constructor... personally I have a class called WindowUtils that has a "position" method that figures out the screen size and then uses the values passed in to create a window that is the relative size of the screen. Then the code that creates the window calls that. I prefer to have as few constructors as possible (very often that is zero or one, I hardly ever have two or more).
3) this.getContentPane().add(jb_inc); can now be written as add(jb_inc); - since JDK 1.5 I think.
4) never call overrideable methods (all the things you have this. before) inside the constructor. If a subclass were to override "add" you could see things break. So you can call super.add(), or do the adds in another method, or make your class final.
Now to answer your question... :-)
You need to make use of LayoutManagers to get what you want.
Depending on what you want you probably want to use a BorderLayout so you can get the button on the bottom.
Since you're setting bounds on everything, you don't need a LayoutManager at all. Set your LayoutManager to null, then you can position everything yourself, as you are in your example.
this.setLayoutManager(null)