I am writing a Tetris-based game, and I am using a GridLayout to display the actual shaft which the Tetris pieces fall down. The GridLayout is filled with little JLabel objects. The shaft is itself a JPanel inside another JPanel, the panel I am using to contain and control the entire game. When the game ends, I want the words "GAME OVER" to appear on top of the grid, possibly accompanied by a small image.
My shaft is an instance of ShaftPanel, which extends JPanel (so that I could override paintComponent) and is a nested private class (so it has access to private instance variables of the larger JPanel). The private instance variable it needs to access is boolean game, which is true when the game is in session and set to false when the game is over. Here is the code for my ShaftPanel class:
public class ShaftPanel extends JPanel {
public ShaftPanel(GridLayout g){
super(g);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
if(game)
return;
g.setColor(Color.WHITE);
g.setFont(new Font("Courier New", Font.BOLD, 15));
char[] c = {'G','A','M','E',' ','O','V','E','R'};
g.drawChars(c,0,c.length,45,45);
}
}
I have one method in the larger class calling the repaint() method of shaft at the appropriate time. Adding a print statement indicates that the paintComponent method is being called when I want it to, and the setColor, setFont, drawChars methods are all being called at the correct times. However, nothing shows up. I highly suspect that the text is being drawn underneath the grid of JLabels, so that it can't be seen, but I don't know how to fix this problem. The JLabels inside the grid have to stay opaque, because the program relies on them being different colors. Is there a way to tell paintComponent to draw the text on top of anything else in the panel?
See the section from the Swing tutorial on Using a Glass Pane for an example and explanation on how a glass pane works.
Another option is to use a JWindow to display a label with your message and icon.
I would probably not have made tetris this way, but one way to test this would be to remove the "if(game)" and see if game over is being written even when no JLabels are there (unless they are always there and sometimes are blank).
At any rate, I think what might be useful though I don't think I have ever used it is a GlassPanel.... This is a panel that can be overlayed on your current JFrame etx...
Look Here for more code and info: http://www.java2s.com/Code/Java/Swing-JFC/Paintonglasspane.htm
Related
I want to add JLabels to JFrame directly. I don't want use JPanel. But I have a position problem. I set layout as null.
I tryed draw line to see what's going on.
#Override
public void paint(Graphics g){
g.setColor(Color.red);
g.drawLine(0, 31, super.getWidth(), 31);
}
And the zero is actually 31.
Drawing screenshot
Why? And how can I fix that?
I want to add JLabels to JFrame directly. I don't want use JPanel.
If you're adding the JLabel "to the JFrame" then you're adding it to the contentPane which is almost always a JPanel, so 99% of the time, you're still using a JPanel, even without trying to.
But I have a position problem. I set layout as null.
Which is almost always a bad thing to do. This makes for GUI's that don't work on all platforms, fighting against the Java philosophy and structure.
And the zero is actually 31.
Why? And how can I fix that?
Because of the top part of the JFrame is taken up by the OS window's menu bar. The contentPane, starts 31 pixels below the top of the JFrame (in your case -- different for different OS's and screen resolutions).
Best to avoid drawing directly on the JFrame, which is actually composed of many sub-components -- the content pane, the root pane, the glass pane,... and instead draw within the paintComponent method of a JPanel that you either add to the contentPane or make as the contentPane. Then 0,0 is the top left of the usable portion of your main window.
Also, please elaborate more on the underlying reason why you're trying to avoid use of a JPanel. Your issue may in fact be an XY Problem type issue.
Positions in a JFrame are relative to the edge of the window, not the content pane. To get the dimensions of the content pane, use getContentPane().getWidth() and getContentPane().getHeight().
I have a rectangle which I move along the JPanel using repaint(). When the position of the rectangle reaches a position outside the JPanel it is not visible anymore. How can I make it visible outside the JPanel?
This my paint method:
public void paintComponent (Graphics g) {
g.setColor(Color.red);
g.fillRect(dist, 0, 10, 10);
dist++;
}
Update:
I have multiple JPanels in the JFrame which I positioned using the GridBagLayout. The JPanels represent Lanes in a Street and the rectangles cars. The reason to make the rectangles visible outside their JPanel is to have the cars change lanes. The JPanel seemed suitable to me, to set the first position of a car.
Is there a better solution for this problem?
You state:
I have a rectangle which I move along the JPanel using repaint(). When the position of the rectangle reaches a position outside the JPanel it is not visible anymore. How can I make it visible outside the JPanel?
If the JPanel is drawing it, the short answer is: "you can't".
The longer answer will depend on just where you're trying to draw the JPanel and how the rest of your GUI is set up.
Edit
You now state:
I have multiple JPanels in the JFrame which I positioned using the GridBagLayout. The JPanels represent Lanes in a Street and the rectangles cars. The reason to make the rectangles visible outside their JPanel is to have the cars change lanes. The JPanel seemed suitable to me, to set the first position of a car.
If I coded the way you were doing it, I wouldn't have these local JPanels draw the car but rather would have the car be its own sprite that exists on a different layer from the streets, perhaps using a JLayeredPane. It could exist in its own JPanel that encompasses your entire map, as long as this JPanel is not opaque. Then you could move the car any which way you'd like.
As said before you can't but if you want it to occupy and larger area you should either make the JPanel bigger or put the paintComponent in the parent component.
I am working on a project and I need to display an image that I have already drawn. I need a background image and several foreground images that I can move around. What is the easiest way to do this? I haven't been able to find a clear answer.
ImageIcon image = new ImageIcon("imagename.jpg");
JLabel label = new JLabel("", image, JLabel.CENTER);
JPanel panel = new JPanel(new BorderLayout());
panel.add( label, BorderLayout.CENTER );
return panel;
The answer depends. Do you want to resize the background image to meet the requirements of the client are or not? If so, do you want to "fill" or "fit" the image to the area.
Take a look at Java: maintaining aspect ratio of JPanel background image for a lengthier discussion on the topic.
The next question you need to ask, is do you want to paint the animation on the surface or use existing components and move them instead...
You could check out...
Swing animation running extremely slow
https://stackoverflow.com/questions/15858623/how-to-use-a-swing-timer-to-animate/15859932#15859932
Drawing 2 Balls to move in different direction on Java but one disappeared
Java Bouncing Ball
Multiple bouncing balls thread issue
I am trying to make ball gradually move
the images are not loading
Which uses the paintComponent or direct paint method.
This approach is relatively common and easy to control. The problem is if you want to perform sub animation (ie animate the actually element separately from the main animation...think walking or spinning), then it becomes more difficult.
Check out
Java ball object doesn't bounce off of drawn rectangles like it's supposed to.
Which uses components instead. This method is good if you want to provide sub animation, but has the complexity of requiring you to size and position the components within the container.
Just as a side note. JLabel is a container ;)
The easiest way i've found is to create an entirely new class and extend JPanel like so:
import javax.swing.*;
import javax.imageio.*;
import java.io.*;
import java.awt.*;
public class Background extends JPanel {
private Image image;
public Background(){
this.setPreferredSize(new Dimension(width,height));
image =Toolkit.getDefaultToolkit().getImage("your_image.jpg");;
}
public void paintComponent(Graphics g) {
//paints the background image
super.paintComponent(g);
do{
}while(g.drawImage(image, 0, 0, null)==false);
}
}
To instantiate this class simply call this:
Background b= new Background();
From your main program.
Remember that b now acts as a Jpanel so you can simply call b.add(element)
If you don't understand what i've talked about and shown you, view this document on extending classes:
Extending Classes
I've just started coding video games and I've heard that doing all your drawing on a JPanel and attaching that panel onto a JFrame is better than simply drawing onto the JFrame. I was just wondering why is this better?
It is better for various reasons, including:
In most Swing components, custom painting is achieved by overriding the paintComponent(Graphics) method. Top-level Swing containers (e.g. JFrame, JApplet, JWindow) have only paint(Graphics). As a result of the common method for painting, people who answer often forget about this difference between common and top-level components, and therefore suggest silly advice. ;)
A JComponent can be added to a JFrame, or a JApplet, or a JDialog, or a constraint of a layout in a JPanel, or a JInternalFrame, or a.. Just when you think your GUI is complete, the client says "Hey, wouldn't it be great to throw in a tool-bar & offer it as an applet as well?" The JComponent approach adapts easily to that.
As explained (complete with code!) by Richante, it is easier to calculate the co-ordinates required for painting the custom component, and if it has a preferred size, to size the JFrame to be 'exactly the right size' to contain the GUI (using pack()). That is especially the case when other components are added as well.
Now, two minor disagreements with the advice offered.
If the entire component is custom painted, it might be better to simply put a BufferedImage inside an ImageIcon/JLabel combo. Here is an example of painting to an image. When it comes time to update it:
Call getGraphics() for a Graphics or createGraphics() for a Graphics2D
Draw the custom painting to that graphics instance
Dispose of the graphics instance
Call repaint() on the label.
It is an easy way to do Double Buffering.
Let me elaborate a bit. In video games, to avoid the flicker caused by redrawing and display smoother graphics, people usually use double buffering. They create an offscreen surface, draw everything on that and then display the entire off-screen surface on the screen in one go.
In Java2D and Swing, the easiest way to do this, is to simply do your game sprite drawing on a JPanel, and then add the JPanel to a JFrame.
Secondly, by drawing things on a JPanel, you allow more GUI widgets and other graphical objects to be displayed on the JFrame without having to paint them manually in the game loop. For example buttons, menus, other panels, even other rendering JPanels.
Thirdly, it allows you to use automatically translated co-ordinates for your game. You can draw everything from the top-left to the bottom-right without having to worry about the window manager specific things like window border-widths, task panes, titles etc.!
Moreover, this is not just a convention only used by Game Programmers in Java. Creating a separate Game Board, Render Panel or Graphics Widget is quite popular when programming games using a library with an internal mainloop such as a GUI Toolkit. You can use a User Form in Windows Forms and a Drawing Board in GTK+.
The JFrame includes things like the menu, title bar and border. Therefore, when you refer to coordinates you have to account for these. You might also decide to add a menu bar, or some other components, to the frame. If your painting is all in a JPanel, then this won't change how you need to refer to coordinates.
For example, try:
public class Test extends JFrame {
public Test() {
super();
setVisible(true);
setBounds(100, 100, 200, 100);
}
#Override
public void paint(Graphics g) {
g.fillRect(0, 0, 50, 50);
}
public static void main(String[] args) {
new Test();
}
}
and you will see that the black square is not square! Because (0, 0) is the top left corner of the entire frame, not the corner of the visible area.
I am trying to write a code to generate a graph like this: http://www.mathgoodies.com/lessons/graphs/images/line_example1.jpg
I need more than one different line (I hope that's what they are called).
I'm just starting to learn awt and swing. After exhausting three hours of work, I couldn't manage a way to draw a line on top of any other drawing.
I'll try to explain my problem with an example.
Lets say I draw a square like this:
JFrame window = new JFrame();
window.setLayout(null);
window.setVisible(true);
Graph graph = new Graph();
window.add(graph);
//-------------------
public class Graph extends JPanel {
....
public void paintComponent (Graphics g) {
super.paintComponent(g);
g.setColor(Color.white);
g.fillRect(150, 20, x, y);
}
....
}
How do I draw another line or anything else on top of this white square whithout drawing the line in the Graphs paintComponent method? How do I add another JPanel on top of another one, so that both of them are visible? (I'm using JPanel to add some buttons)
Hopefully you can understand what I'm asking.
Thank you!
How do I draw another line or anything else on top of this white square whithout drawing the line in the Graphs paintComponent method?
All custom painting should be done in the paintComponent() method. I'm not sure why you want to add another panel that paints on line. Keep it simple and keep all the painting code in one place.
If you want to add other components (like a JPanel) to the panel then you would use layout managers to lay out the components properly. You would also need to make the components non-opaque by using the setOpaque(...) method.
Another way to layer components is to use a JLayeredPane.
Start by reading the Swing tutorial. There are sections on:
Using Layout Managers
Using Layered Panes.