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.
Related
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.
This is my first java project and I am trying to draw a simple rectangle on my JPanel inside my JFrame. Been trying to solve this issue with the help of the same topics on stackoverflow but still no success.
The exception I get when I run the program is java.lang.NullPointerException. From my understanding I can not draw on the JPanel itself? which is created in mainWindow.
Main:
public class Main {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
GameBoard game = new GameBoard();
mainWindow view = new mainWindow(game);
mainModel model = new mainModel();
mainController cont = new mainController(model, view, game);
cont.controllerInit();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
View:
public class mainWindow{
public JFrame frame;
public JPanel panel;
GameBoard game = new GameBoard();
frame = new JFrame();
frame.getContentPane().setBackground(SystemColor.control);
frame.setBounds(100, 100, 728, 435);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(game);
frame.getContentPane().setLayout(null);
panel = new JPanel();
FlowLayout flowLayout = (FlowLayout) panel.getLayout();
panel.setBounds(166, 44, 550, 349);
frame.getContentPane().add(panel);
frame.setVisible(true);
}
Game:
public class GameBoard extends JPanel{
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(Color.BLUE);
g.drawRect(200, 200, 200, 200);
}
}
Never, ever call paintComponent directly, no external source has any reason to do so. Also, what do you thing would happen if you passed it null?
You should start by having a look at Performing Custom Painting and Painting in AWT and Swing to get a better understand of how paint in Swing works.
The Swing API basically uses a delegate model, where the system delegates responsibility of the paint of each component to the component. This is achieved by the system calling the components paint method, which in-turn calls (among a few others) paintComponent.
Swing also uses a passive rendering approaching, meaning that painting occurs at the discretion of the paint system. You component is notified of the need when its paint method is called. This may occur at any time.
In order for a component to be painted, it must first be added to container which is realised on the screen (has a native peer), in most cases, this means that the component hierarchy needs to resolve to some kind of window based class, like JFrame.
So, the answer to your question is:
Read the above documentation (and get a better understanding of how the API works)
Add your GameBoard to a container which can be resolved to a window based class
Never call paint or paintComponent directly
Reflection....
private mainWindow view;
private mainModel model;
public GameBoard(mainModel m, mainWindow v)
{
view = v;
model = m;
}
To me, this makes no sense. There is no reasonable reason why GameBoard needs a reference to mainWindow. GameBoard is, in of itself, a "view". If anything, the only thing you "should" be passing to GameBoard (assuming you're trying to use a MVC) is a controller
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.
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.
I have a button that when pressed calls a changecolor() method in a different class where some drawing is done. The button listener works fine and I see from some logging that the color was in fact changed but my drawing is not updated. This is my current implementation:
(This method is called when a button is clicked)
public void changeWarningLightColor(){
System.out.println("change color method called");
if(warningLights.equals(Color.GREEN)){
warningLights=Color.RED;
System.out.println(warningLights);
repaint();
}
else{
warningLights=Color.GREEN;
repaint();
}
}
and my drawing is created in the same file in the above method as follows:
#Override
protected void paintComponent(Graphics g){
super.paintComponent(g);
g.drawSomething();
//draw a bunch of lines
}
My question is what is the proper way to call repaint() in order to actually update the drawing? Do I need to somehow call g.repaint() or do something different?
Separate class where the frame is created:
public class MainWindow extends JFrame {
public MainWindow(){
JPanel Window = new JPanel();
JPanel LeftSidePanel = new JPanel();
LeftSidePanel.setLayout(new BorderLayout());
LeftSidePanel.add(new DrawStatus(),BorderLayout.CENTER); //where the drawing is added
Window.setLayout(new BoxLayout(Window,0));
Window.add(LeftSidePanel);
add(Window);
}
public static void main(String[] args) {
//main method for showing the frame we created with the panels, and circles inside it
MainWindow frame = new MainWindow();
frame.setSize((int) (.75*Toolkit.getDefaultToolkit().getScreenSize().getWidth()),(int) (.75*Toolkit.getDefaultToolkit().getScreenSize().getHeight()));
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
frame.setTitle("DVPWS v1.0");
frame.setResizable(false);
MenuBar menu = new MenuBar();
frame.setJMenuBar(menu);
frame.setVisible(true);
}
}
If you are using a Jframe (most likely are) do
yourFrame.repaint();
Optionally
yourPanel.repaint();
In this case you could do
public MainWindow mw = new MainWindow();
mw.repaint();
If that doesnt work(i had a similar problem) than your going to have to make an instance of JFrame instead of extending it.