paintComponent and repaint methods not working for my JPanel, Graphics2D - java

I am working on trying to make a GUI with a rectangular vehicle object in the middle of the page with regards to x-coordinate and two rectangular objects on either side of the vehicle.
I am extending a JPanel, so I call repaint in the run method to call the paintComponent method, but I am not even entering the paintComponent method. Additionally, do I have to do anything differently because I am working with Graphics2D?
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Display extends JPanel implements Runnable{
public static final int frameWidth=1300;
public static final int frameHeight=800;
public double score;
public double updateTimeInterval=25;
public double prevUpdatedTime=0;
public Display(){
JFrame frame = new JFrame();
frame.setSize(frameWidth, frameHeight);
frame.setTitle("You are playing HoverTeam!!!");
frame.setVisible(true);
JPanel panel = new JPanel();
frame.getContentPane().add(panel);
System.out.println("completed constructor.");
}
public void paintComponent(Graphics g){
System.out.println("currently painting.");
Graphics2D g2 = (Graphics2D) g;
/*
* Testing with random GameState
*/
double[] pos = {28,6,Math.PI/8};
double[] vel = {5,5,0};
int[] nearList = {4,8,7,5};
GameState gs = new GameState(pos,vel,2,2,nearList,3);
//GameState gs = GameClient.getGameState();
/*
* Drawing the vehicle in the center of the screen with regards to the x-coordinate and then referencing the walls to it.
*/
Path2D.Double vehic = gs.getVehicleShapePath(frameWidth/2, gs.getPosition()[1]);
g2.draw(vehic);
int[] nearObstHeights = gs.getNearObstList();
double vehiclePast = gs.getPosition()[0]%5; //distance that the vehicle is past the second obstacle--reference to where to draw obstacles
for (int i =0; i<nearObstHeights.length;i++){
Rectangle2D.Double obstacle = new Rectangle2D.Double(frameWidth/2 -vehiclePast+5*(i-1),nearObstHeights[i],1,nearObstHeights[i]);
g2.draw(obstacle);
}
score = gs.getPosition()[0]/5;
g.drawString("Score:"+score, frameWidth/2, frameHeight-10);
}
public void run(){
/*
* No maximum score, game goes on forever.
*/
System.out.println("entereed run method.");
while (true){
long currentTime = System.currentTimeMillis();
if (currentTime-prevUpdatedTime>updateTimeInterval){
System.out.println("entered if statement");
prevUpdatedTime = currentTime;
repaint();
System.out.println("should have just repainted.");
}
}
}
public static void main(String[] args){
(new Thread(new Display())).start();
}
}
Thanks

Your code is very confusing, each object has to take care of its own responsabilities and nothing more. In particular:
Display is a JPanel... and also a Runnable? Why is that? You can have a runnable doing work with the GUI (carefully) but it shouldn't be the GUI itself.
Display is a JPanel but its constructor is doing all sorts of things like creating a frame. The constructor of Display only has to take care of constructing the JPanel. Create your own class MyApplication that does all the work, or an static method createAndShowGUI() and call it from your main.
Why are you creating a thread in the main? Or rather, why the same statment that creates the
Then you have some problems in your code:
The first thing you almost certianly do in your paintComponent is calling super.paintComponent(g).
You should learn what the Event Dispatching Thread is. Look at how to setup the initial threads in Swing. In short you must call invokeLater in your main and get rid of that new Thread(). And however you organize your code, do not execute that runnable in the EDT, it has an infinite loop and the GUI would freeze.
You never put any Display object in your frame. The code creating the GUI should be something like this:
.
//we create the GUI here, this is called by main inside the runnable of `invokeLater`.
public static creteAndShowGUI(){
JFrame frame = new JFrame();
frame.setSize(frameWidth, frameHeight);
frame.setTitle("You are playing HoverTeam!!!");
//JPanel panel = new JPanel(); //this is an empty panel, you want the Display subclass that you created.
//we use the Display panel that will use the `paintComponent` to paint itself
Display panel = new Display();
frame.getContentPane().add(panel);
System.out.println("completed constructor.");
frame.setVisible(true);
}
You don't need that runnable at all, the GUI is repainted when needed by its own. You only have to call repaint() if you are making changes in it with your code.
Edit: As per your comments I'll clarify something: Whatrepaint() does is schedule the repainting of the object in the screen, that process includes, at some point, calling paintComponent. However you are calling repaint() on an object that it's not in the screen. Your call repaint() or rather this.repaint() is repainting the object new Display() that you create in the main (and is aregument of new Thread() however you don't add that object to any frame, it's not in the GUI so there's nothing to repaint on the screen.

Related

Stuff drawn by paintComponent() disappear after calling repaint()

On Head First Java book we're seeing some little animation and I'm trying to draw an animation that traces out a diagonal line. I'm doing so by using the paintComponent() method and drawing an oval at x,y (values which are updated each time through the loop). Why do I lose the previously drawn ovals? According to the book I should be getting a smear on the screen where the previously drawn ovals are not lost. This should need fixing by adding to the paintComponent() method a white background every time repaint() is called but instead i'm not getting the 'mistake'. Why is this and how do I get to KEEP the previously drawn ovals on the panel? Using JDK 13.0.2 and Mac OSX Catalina
import javax.swing.*;
import java.awt.*;
public class SimpleAnimation {
int x = 70;
int y = 70;
public static void main(String[] args) {
SimpleAnimation gui = new SimpleAnimation();
gui.go();
}
public void go() {
JFrame frame = new JFrame();
MyDrawPanel drawPanel = new MyDrawPanel();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(drawPanel);
frame.setSize(300,300);
frame.setVisible(true);
for (int i = 0; i < 130; i++) {
x++;
y++;
drawPanel.repaint();
try {
Thread.sleep(25);
} catch (Exception ex){};
}
}
class MyDrawPanel extends JPanel {
public void paintComponent(Graphics g) {
g.setColor(Color.orange);
g.fillOval(x,y,50,50);
}
} // close inner class
} // close outer class
Is that what prevents the smear of ovals?
class MyDrawPanel extends JPanel {
public void paintComponent(Graphics g) {
g.setColor(Color.orange);
g.fillOval(x,y,50,50);
}
The code should be:
class MyDrawPanel extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g); // added
g.setColor(Color.orange);
g.fillOval(x,y,50,50);
}
You need the super.paintComponent(g) to clear the background of the panel before doing the custom painting.
Yeah, great book. The output i'm getting with the code as is, is the "corrected version"
Edit:
The book is correct. You need to understand how the EDT works. When you start an application the code invoked in the main() method executes on a separate Thread. Swing events and painting is done on the Event Dispatch Thread (EDT). So invoking sleep() in the go() method should NOT affect the painting of the circle and you should see the smear. If you only see a single oval painted after the loop is finished, then this imples your IDE or platform starts the code in the main() method on the EDT, which is not normal.
You can verify my above statement by adding:
System.out.println( SwingUtilities.isEventDispatchThread() );
to your loop to see if it is executing on the EDT or not.

Only One Thread Paints in my JFrame object, why not the other? [duplicate]

This question already has answers here:
Why does the first panel added to a frame disappear?
(2 answers)
Closed 5 years ago.
I've been trying all day long to make this happen with no success. What can be going wrong?
I want 2 threads printing simultaneously in my JFrame:
Thread-1: Prints Squares
Thread-2: Prints Circles
I'am ending up with only one thread printing on the JFrame. The other get executed but don't print in the JFrame.
Look, only squares are getting printed:
This is my main class:
public class Main {
public static void main(String[] args) {
FigurePlacer circle = new FigurePlacer("circle");
FigurePlacer square = new FigurePlacer("square");
JFrame window = new JFrame();
window.add(circle);
window.add(square);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setTitle("Task");
window.setSize(700, 700);
window.setLocationRelativeTo(null);
window.setVisible(true);
}
}
This is the Threading Class:
import java.awt.Graphics;
import javax.swing.JPanel;
import java.awt.Color;
import java.util.Random;
public class FigurePlacer extends JPanel implements Runnable{
String figure;
final int width = 700;
final int height = 700;
int x_pos = 0;
int y_pos = 0;
int x_width = 50;
int y_height = 50;
public FigurePlacer(String str){
figure = str;
randomCoord();
Thread th = new Thread (this);
th.start();
}
private void randomCoord(){ //this ramdomize x,y coord to place a new object
Random random = new Random();
x_pos = random.nextInt(width);
y_pos = random.nextInt(height);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLACK); //sets the black color in background
g.fillRect(0, 0, 700, 700);
System.out.println(figure);
switch (figure){
case "square":
g.setColor(Color.GREEN);
g.fillRect(x_pos, y_pos, x_width, y_height);
break;
case "circle":
g.setColor(Color.BLUE);
g.fillOval(x_pos, y_pos, x_width, y_height);
break;
}
}
#Override
public void run(){ //paints the objects
while (true){
randomCoord();
paintImmediately(x_pos, y_pos, x_width, y_height);
try{
Thread.sleep (50);
}
catch (InterruptedException ex){}
}
}
}
JFrame window = new JFrame();
window.add(circle);
window.add(square);
The default layout manager for a JFrame is the BorderLayout. When you add a component to a panel using the BorderLayout and don't specify a constraint then the component goes to the CENTER. However only one component can ever be displayed in the CENTER, so only the last component added is painted.
You could use the OverlayLayout, which allows you to stack two panels on top of one another. Of course you will need to make the top panel non-opaque.
The easier solution is to not attempt to use two panels, just create on panel that can display circles or squares.
Also, your painting code is wrong. You should not be using paintImmediately(...) to do painting. The paintComponent() method should paint every object every time the method is invoked. Try resizing your frame and you will see that all your object disappear since the paintComponent() method will clear all the old paintings.
See Custom Painting Approaches for the two common approaches for this kind of painting.

JPanel How to create an instance/object

I keep mixing up JFrame and JPanel. I have a code that compiles but nothings happens and the program ends.
This is a homework assignment that requires us to draw a triangle. Then add a button, when button is pressed the triangle will flip upside down. Press again to return to the first location.
Any assistance would be great. This is my countless rough draft and I'm just trying to initiate the object to view.
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.awt.geom.Line2D;
import java.awt.Color;
public class Triangle extends JPanel implements ActionListener
{
JButton button = new JButton("Just Flip It");
public Triangle()
{
add(button);
button.addActionListener(this);
}
public void actionPerformed(ActionEvent e)
{
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawLine(125,75,100,200);
g.drawLine(125,75,150,200);
g.drawLine(100,200,150,200);
}
public static void main(String[] args)
{
Triangle frame = new Triangle();
frame.setSize(400, 400);
frame.setVisible(true);
}
}
Think of a JPanel as a piece of paper and a JFrame as a book.
If you never add the piece of paper to the book, it will never be shown.
So, here are the steps to get your program running:
Create a JFrame that will hold your JPanel (Triangle) inside it
Add some logic in your ActionListener to have a boolean flag, that will be changed to true or false depending on the current state and based on it repaint the pane
Have some logic inside the paintComponent(...) method that draws the triangle upside down or upside up based on the state of the boolean flag above
So, from the above in your main(...) method you should have
JFrame frame = new JFrame("My frame");
//Initialize your Triangle here
frame.add(triangle);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
Your actionPerformed() method should look like this:
public void actionPerformed(ActionEvent e)
{
clicked = !clicked;
repaint();
}
And your paintComponent():
super.paintComponent();
if (clicked) {
//Draw upside up
} else {
//Draw upside down
}
As we're using pack() instead of setSize(...) method, we should override getPreferredSize() method of our JPanel as well:
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
Otherwise we could get a really small JFrame (and it's a good practice to do it this way)
Also don't forget to place your program on the EDT (as I show in the point #2 of this answer)
As a side note, you might also want to move to use the Shape's API as recommended by #MadProgrammer in this answer
Both links above have cool examples and tips, check them out!
You need to add your JPanel to a JFrame object in your main method in the same way you add a button to your panel. The JFrame is the window that your JPanel exists inside of.
Make sure that you set the JFrame object to visible and set its size.
Not related to your question, but I would suggest at least separating out a private class for you ActionListener rather than having the Triangle class be both. If you're feeling adventurous, you could look into using an anonymous inner class or a lambda expression.

repaint in java not working

I am trying to draw a rectangle which its position is updated every second, for that I have a class which extends JPanel and in it I have overriden the paint ( or paintComponent) function_ I have tried both _ but apparanetly this function is called only once and as it is shown in the code below when I try to call it in an infinite loop with repaint function it doesnt get called, any ideas what I can do?
public class Board extends JPanel implements KeyListener{
public void setUpBoard(){
JFrame frame = new JFrame();
Board board = new Board();
frame.setVisible(true);
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(600, 600);
frame.setLocation(350, 80);
frame.add(board);
}
public void paint(Graphics g){
g.setColor(Color.RED);
g.fillRect(food.getX(),200,20,20);
}
}
the code above is the graphic part, below is the main function, which is placed in another class :
public static void main(String[] args) throws InterruptedException {
Board board = new Board();
FoodGenerator food = new FoodGenerator();
board.setUpBoard();
while(true){
board.repaint();
food.adder();
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
in the code above food.adder is where the position of the rectangle is updated, which I have checked and doesnt have any issues.
The problem is that you're creating a new Board object in setUpBoard and adding that to your JFrame:
Board board = new Board();
// ...
frame.add(board);
So when you use repaint(), you're repainting the instance of Board that you created in the main method, and not the instance you created in setUpBoard, which is the one you add to the frame.
This can be easily fixed by using Board board = this; in setUpBoard, or, even simpler in my opinion, just using frame.add(this). Subsequent calls to repaint will then schedule a call to paint for the same Board object that you created in the main method.
Also, since you're working with Swing, don't use paint, and instead use paintComponent, making sure that super.paintComponent(g) is the first statement in the method body.
Another problem is that the repaint calls are being done on the main thread, not on the event thread.

How to Get JPanel to Fit Snug With Its Components?

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);
}
}

Categories