I am trying to make an animation with multiple thread. I want to paint n squares where this n comes from commend-line argument. Every square has their x-y coordinates, colors and speed. They are moving to the right of the frame with different speed, color and coordinates. Since I am using multi thread I assume I have to control each squares. So I have to store each square object in the ArrayList. However, I am having trouble with painting those squares. I can paint one square but when I try to paint multiple squares, it does not show. Here what I have done so far:
DrawSquare.java
import java.awt.Graphics;
import javax.swing.JPanel;
public class DrawSquare extends JPanel {
public Square square;
public DrawSquare() {
square = new Square();
}
#Override
public void paintComponents(Graphics g) {
// TODO Auto-generated method stub
super.paintComponents(g);
}
#Override
public void paint(Graphics g) {
// TODO Auto-generated method stub
super.paint(g);
g.setColor(square.getC());
g.fillRect(square.getX(), square.getY(), square.getR(), square.getR());
}
}
Square.java
import java.awt.Color;
import java.util.Random;
public class Square {
private int x,y,r,s;
private Color c;
private Random random;
public Square() {
random = new Random();
x = random.nextInt(100) + 30;
y = random.nextInt(100) + 30;
r = random.nextInt(50) + 20;
s = random.nextInt(20) + 5;
c = new Color(random.nextInt(255),random.nextInt(255),random.nextInt(255));
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getR() {
return r;
}
public int getS() {
return s;
}
public Color getC() {
return c;
}
}
Animation.java
import java.awt.BorderLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Animation extends JFrame implements Runnable {
private JPanel panel;
private DrawSquare square;
public Animation() {
}
public static void main(String[] args) {
Animation w = new Animation();
DrawSquare square = new DrawSquare();
JFrame f = new JFrame("Week 9");
int n = Integer.parseInt(args[0]);
f.setVisible(true);
f.setSize(700,700);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setResizable(false);
for(int i=0; i<n; i++) {
f.getContentPane().add(square);
}
}
#Override
public void run() {
// TODO Auto-generated method stub
}
}
So, starting with...
public class DrawSquare extends JPanel {
public Square square;
public DrawSquare() {
square = new Square();
}
#Override
public void paintComponents(Graphics g) {
// TODO Auto-generated method stub
super.paintComponents(g);
}
#Override
public void paint(Graphics g) {
// TODO Auto-generated method stub
super.paint(g);
g.setColor(square.getC());
g.fillRect(square.getX(), square.getY(), square.getR(), square.getR());
}
}
As general recommendation, it's preferred to put custom painting in the paintComponent method (note, there's no s at the end)
When paint is called, the Graphics context has already been translated to the component coordinate position. This means that 0x0 is the top/left corner of the component, this also means that...
g.fillRect(square.getX(), square.getY(), square.getR(), square.getR());
is painting the rect at x + x x y + y, which will, at the very least, paint the rect in the wrong position, at worst paint it beyond the visible scope of the component.
You're also not providing any sizing hints for the component, so it's default size will be 0x0, which prevent it from been painted.
Since I am using multi thread I assume I have to control each squares.
Well, since I can't really see what's driving the animation, I imagine that when you say "multi thread" you're suggesting that each square has it's own Thread. In this case, that's a bad idea. Let's put aside the thread synchronisation issues for a moment, more threads doesn't equate to more work you can do, at some point, it will begin to degrade the system performance.
In most cases, a single, well managed thread, is all you really need. You also have to understand that Swing is NOT thread safe. This means that you shouldn't update the UI (or states that the UI relies on) from outside the context of the Event Dispatching Thread.
So, while you're thread can update the position of the rects, you need to take care to ensure that they are not been painted while they are been update. Once you've updated the state, you then need to trigger a paint pass (which is trivial in of itself)
So I have to store each square object in the ArrayList.
Yep, good start
However, I am having trouble with painting those squares. I can paint one square but when I try to paint multiple squares, it does not show.
Okay, so instead of using multiple components, use one. Run through your ArrayList within the paintComponent method of this component and paint all the rects to it. This provides a much simpler way to manage things like bounds detection, as you have only one container to worry about.
I'd highly recommend you have a look at:
Java Bouncing Ball which demonstrates many of the concepts discussed here
Concurrency in Swing
How to use Swing Timers
Performing Custom Painting
Painting in AWT and Swing
Related
I have a class that creates a JFrame on which a simple game of Tetris will be played, I also have a class DrawSquare, which does exactly what you think it does, however when I initialise a new instance of the DrawSquare class and then try to draw that one and all the others to my JFrame things start to go wrong, the code is intended for one square to be drawn in the top left hand corner and then drop down a line at a time until it reaches the bottom of the frame (it does this), then a new square should be drawn in the second column at the top of the frame, as well as our first square in the bottom left hand corner, however once it starts dropping down the second column I get a series of squares drawn in a diagonal towards the top right hand corner. At the moment all I plan for the code to do is have a square drop from the top row of each column and stop when it reaches the bottom of the frame, am I storing the instance of the class at the wrong point in the code? Edit: In fact I'm pretty sure it's that, I'd want to store that instance when it reaches the bottom. Does every instance of the class need its own timer?
public class Tetris extends JFrame {
public static final int height = 20; //height of a square
public static final int width = 20; //width of a square
public int xPos = 0; //column number of the square
public int yPos = 0; //row number of the square
public static void main(String[] args){
Tetris tet = new Tetris();
}
public Tetris() {
DrawSquare square = new DrawSquare(xPos, yPos, width, height, false);
add(square);
DrawSquare.squares.add(square);
setSize(220,440);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
}
public class DrawSquare extends JPanel {
public static List<DrawSquare> squares = new ArrayList<>();
protected int xPos;
protected int yPos;
protected int width;
protected int height;
protected Timer timer = new Timer(200, new TimerListener());
protected boolean endFall = false;
public DrawSquare(int xPos, int yPos, int width, int height, boolean endFall) {
this.xPos = xPos;
this.yPos = yPos;
this.width = width;
this.height = height;
this.endFall = endFall;
this.timer.start();
}
class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
yPos++;
if (yPos > 19) {
yPos = 19;
endFall = true;
}
if (endFall == true) {
timer.stop();
if (xPos > 8) {
xPos = 8;
}
xPos++;
endFall = false;
yPos = 0;
DrawSquare newSqr = new DrawSquare(xPos, yPos, width, height, true);
squares.add(newSqr);
add(newSqr);
}
timer.start();
repaint();
}
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Iterator<DrawSquare> it = squares.iterator();
while (it.hasNext()) {
DrawSquare square = it.next();
g.fillRect(square.xPos * square.width, square.yPos * square.height, square.width, square.height);
}
}
}
You are giving a great example of the fundamental misunderstanding beginners have of how the swing (and many other graphics toolkits) render stuff to the screen. I will give an overview of that, as it pertains to you, then answer your immediate questions and explain how to fix your code.
It took me a (very long) while to figure out how this stuff works my self, so please bear with me. I hope that reading through this answer will help you in a much more general way than answering this one question.
Asynchronous Drawing
Swing draws windows in a totally different sequence (the event dispatching thread) than the ones that modifies the state of your program (the main thread, as well as timer and other threads). You can modify the coordinates of things you want to draw as many times as you like in the main thread, but the changes will not show up until you request them to by calling JComponent.repaint() on one of your components. This will generally trigger a nearly-immediate repaint of the component, displaying your latest state.
If you change the coordinates of a widget like a JPanel in your main thread, it will likely show up immediately. This is because the methods you use to set the position will trigger repaint requests internally.
A repaint request gets queued and eventually processed by the event dispatching thread. This is where the paintComponent method gets called. The paintComponent method should therefore only draw. It should not do any other logic. If it needs to know how to draw some specialized stuff, the information for that should be stashed somewhere accessible by one of the other threads.
In short, you make calculations and update state as you need in the main thread or the timer. Then you access that state in the event dispatching thread via the paintComponent method.
Timers
There are a bunch of ways you can use timers to run your GUI, but you only really need one for the current application. In your case, the timer only needs to do two things:
Check if a block has fallen all the way down and doesn't need to move any more.
Trigger a repaint of your panel.
You do not need to compute the updated position of the blocks in the timer if the block's position is a simple equation with respect to time. If you know the time at which a block appears on the screen and the current time, you know how far the block has moved, so you can paint it in the correct spot based purely on the elapsed time.
If you had a more complicated system with paths that you could not predict purely on the time, I would recommend sticking the movement logic into the timer events as well. In that case, you might consider having multiple timers, or switching to java.util.timer. But again, this does not apply to your current case (even with multiple blocks).
Model and View
The model of your program is the thing that holds the abstract state. In this case, the positions and other meta-data about all your blocks. The view is the part that does the rendering. It is usually a good idea to separate these two things. There is often a third component to GUIs, called the controller, which connects the model and view to the user. We will ignore it here since you are not asking about controlling the blocks yet.
In your current code, you have attempted to represent your blocks with an extension to JPanel and a static list of existing blocks. While a JPanel may be a convenient way to display rectangular blocks with some custom graphics in them (like icons), I would recommend that you start by drawing the blocks directly using the Graphics object passed to paintComponent. At least initially, it will help you to think of the drawing code and the game logic as separate entities.
Final Rant Before Code Dump
I have made rewrites to your code to encapsulate all the ranting I did before into code. Here are some additional minor points about what I did that may help explain my reasoning:
When you call JFrame.add(...) to add a component to a JFrame, you are really calling JFrame.getContentPane().add(...). The content pane is where 90% of normal swing components go in a window. Therefore, we can either set the JPanel that will do the rendering as your content pane or we can add it to the current content pane. I have chosen to do the latter so that you can add other widgets, like a score board, at a later time.
Class names should generally be nouns, while methods are often verbs. This is not an absolute rule (nothing really is), but naming things this way will often help you visualize the interactions between objects in a more meaningful way. I have renamed DrawSquare to GamePiece for this reason.
There is no longer any reason for GamePiece to be a JPanel. It just needs to know its own width, height, and time of appearance.
The other problem with trying to have DrawSquare draw itself is that a component can only really draw within its own bounding box. So you really want to override the paintComponent of whatever holds the rectangles.
The rendering class maintains a reference to two lists of GamePieces. One is for the moving objects and one is for the ones that have fallen. The logic for moving them between the lists is in the timer. This is better than say adding a flag to GamePiece because it facilitates incremental repaint. I will only partially illustrate this here, but there is a version of repaint that only requests a small region to be painted. This would be useful to speed up the movement.
Code
public class Tetris extends JFrame
{
public static final int height = 20; //height of a square
public static final int width = 20; //width of a square
public static final int x = 0;
private GamePanel gamePanel;
public static void main(String[] args)
{
Tetris tet = new Tetris();
// Normally you would tie this to a button or some other user-triggered action.
tet.gamePanel.start();
tet.gamePanel.addPiece(new GamePiece(width, height, x));
}
public Tetris()
{
getContentPane().setLayout(new BorderLayout());
gamePanel = GamePanel();
add(gamePanel, BorderLayout.CENTER);
setSize(220,440);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}
}
public class GamePanel extends JPanel
{
private List<GamePiece> moving;
private List<GamePiece> still;
private Timer timer;
public GamePanel()
{
moving = new ArrayList<>();
still = new ArrayList<>();
timer = new Timer(100, new TimerListener());
}
public addPiece(int width, int height, int x)
{
moving.add(new GamePiece(width, height, x));
}
public void start()
{
timer.start();
}
#Override
public void paintComponent(Graphics g)
{
Rectangle clip = g.getClipBounds(null);
Rectangle rectToDraw = new Rectangle();
// I prefer this, but you can make the call every
// time you call `GamePiece.getY()`
long time = System.currentTimeMillis();
for(GamePiece piece : this.moving) {
rectToDraw.setSize(piece.width, piece.height)
rectToDraw.setLocation(piece.x, piece.getY(time))
if(rectangleToDraw.intersects(clip))
((Graphics2D)g).fill(rectToDraw)
}
for(GamePiece piece : this.still) {
rectToDraw.setSize(piece.width, piece.height)
rectToDraw.setLocation(piece.x, piece.getY(time))
if(rectangleToDraw.intersects(clip))
((Graphics2D)g).fill(rectToDraw)
}
}
private class TimerListener implements ActionListener
{
#Override
public void actionPerformed(ActionEvent e)
{
long time = System.currentTimeMillis();
// Using non-iterator loop to move the pieces that
// stopped safely. Iterator would crash on in-loop move.
for(int i = 0; i < moving.size(); i++) {
piece = moving.get(i);
if(piece.getY(time) > 440 - piece.height) {
moving.remove(i);
still.add(piece);
i--;
}
}
repaint();
}
}
}
public class GamePiece
{
public final int width;
public final int height;
public final long startTime;
public int x;
public GamePiece(int width, int height, int x)
{
this.width = width;
this.height = height;
this.startTime = System.currentTimeMillis();
this.x = x;
}
public int getY(long time)
{
// This hard-codes a velocity of 10px/sec. You could
// implement a more complex relationship with time here.
return (int)((time - this.startTime) / 100.0);
}
}
Your main problem in a nutshell: you need to separate the JPanel component class from the square logical class. Right now, they are one and the same, and every time you create a new DrawSqaure, you're creating a new JPanel, starting a new Swing Timer, and thus calling code that doesn't need to be called. This is also forcing you to make the List static else you'd have a stack overflow error. Solution: separate the two out, make your List non-static, and use only one Swing Timer.
I've been working on a small "game" project for a while now and haven't been able to pass a value into my paintComponent method in any way.
Here's my program. I used a boolean test parameter, but the paintComponent method does not consider test to be true and not paint the blue square.
How can I fix this? Any help would be greatly appreciated.
Class 1 (Swing):
package swing;
import javax.swing.*;
import javax.*;
import java.awt.*;
import java.awt.event.*;
public class swing {
private static draw object = new draw();
public static void main(String[] args) {
JFrame frame = new JFrame("RogueLikeLike MVP");
frame.setVisible(true);
frame.setFocusable(true);
frame.setSize(500,500);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);;
frame.add(object);
}
static class Move implements KeyListener{
public boolean isFocusTraversable ( ) {
return true;
}
public void keyPressed(KeyEvent e) {
int x = 10;
int y = 10;
char c = e.getKeyChar();
if(c == 's')
y+=10;
if(c == 'a')
x-=10;
if(c == 'w')
y-=10;
if(c == 'd')
x+=10;
object.setAlignmentX(x);
object.setAlignmentY(y);
}
public void keyReleased(KeyEvent e) {}
public void keyTyped(KeyEvent e) {}
}
}
Class 2 (draw):
package swing;
import javax.swing.*;
import java.awt.*;
public class draw extends JPanel
{
boolean test = true;
private static final long serialVersionUID = 1L;
/*public void passMethod(draw object) {
int x = (int) object.getAlignmentX();
int y = (int) object.getAlignmentY();
}*/
public void drawing(draw object){
draw thing = new draw();
thing.setX(object.getAlignmentX());
int x = 55;
repaint();
}
private void setX(float alignmentX) {
setX((int) alignmentX);
}
private void setY(float alignmentY) {
setY((int) alignmentY);
}
public void paintComponent(Graphics g, boolean test){
super.paintComponents(g);
//int x = (int)object.getAlignmentX();
//int y = (int)object.getAlignmentY();
if(test==true)
{
g.setColor(Color.blue);
g.fillRect(getX(),getY(),100,100);
}
}
}
paintComponent can be overloaded, but the overloaded version won't be used to paint the Component. Actually paintComponent isn't called by the user, but by the painting routine. Instead of overloading the method you should consider using a variable for test and reading/updating this variable.
Like this:
private boolean test = true;
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
if(test){
//the rest of the code
}
}
Short answer, you can't. This isn't how method overriding works.
paintComponent is one of the documented methods used by the painting sub system to allow you to plugin custom painting, in order to work it MUST match a very particular method signature, otherwise, how would the API know what to call
Instead, paintComponent should paint the current known state of the component, either directly or indirectly.
What this means is, you component should either have a set of properties which the paintComponent method can access or some other object (ie a model) which it can use to determine the current state of which to paint.
You could use a delegate approach, which would allow to define a contract (maybe via a interface) which paintComponent could call, passing the information you need to these delegates, in order for them to perform the required painting.
See Painting in AWT and Swing and Performing Custom Painting for more details about how painting works
nb:
Add the #Override annotation above your paintComponent method, the compiler will error telling you that their is no matching method which supports your signature
Call super.paintComponent NOT super.paintComponent"s", this would set up a recursive call which would result in a StackOverflowError
I'm trying to use the method addBall to Paint a ball with it's own thread unto the coloredBallPanel
I'm very stuck and would appreiciate any help at all.
Btw I'm trying to make bouncing ball program in which all the balls run on their own separate threads.
public class ColoredBallPanel extends JPanel
{
Ball ball;
public ColoredBallPanel()
{
ball= new Ball();
}
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.WHITE);
g2.fillRect(0, 0, 499, 300);
// ball.paint(g2);
}
}
import java.awt.BorderLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class BallsFrame extends JFrame
{
private ColoredBallPanel coloredBallPanel;
private final int FRAME_WIDTH = 500;
private final int FRAME_HEIGHT = 500;
private int BallDimensionsx =30,BallDimensionsy=30;
private Ball ball = new Ball();
public BallsFrame()
{
//sets the size of the JFrame
setSize(FRAME_WIDTH, FRAME_HEIGHT);
coloredBallPanel = new ColoredBallPanel();//Initialize a Ball panel
add(coloredBallPanel, BorderLayout.CENTER);//white square in the centre
addBall(); // add two balls to panel(Doesn't work yet)
addBall();
}
public void addBall()
{
Ball ball = new Ball();
coloredBallPanel.add(ball);
Runnable r = new ColoredBallRunnable(ball, coloredBallPanel);
Thread t = new Thread(r);
t.start();
}
}
import java.awt.*;
import javax.swing.*;
public class Ball extends JComponent
{
private int x=(int) (Math.random()*(500 -1)),
y =(int) (Math.random()*(300-1)),
xVelocity=-10,
yVelocity=10;
private int width=30,height=30,size =30;
/**
* #param args
*/
public void update()
{
x+=xVelocity;
y+=yVelocity;
if(x<=0)
{
xVelocity =10;
}
else if(x+size>=500)
{
xVelocity = -10;
}
if(y<=0)
{
yVelocity =10;
}
else if (y+size>=300)
{
yVelocity=-10;
}
}
public void paint(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.GREEN);
g2.fillOval(x, y, width, height);
}
}
import javax.swing.JComponent;
import javax.swing.JPanel;
public class ColoredBallRunnable implements Runnable
{
private Ball ball ;
public ColoredBallRunnable(Ball ball, ColoredBallPanel coloredBallPanel)
{
// TODO Auto-generated constructor stub
ball = new Ball();
coloredBallPanel = new ColoredBallPanel();
}
public void run()
{
Ball ball = new Ball();
while(true)
{
ball.update();
ball.repaint();
try{
Thread.sleep(10);
}catch(InterruptedException e){
return;
}
}
}
}
There are so many problems...
Ball is component been added to a Container which is under the control of a LayoutManager, this means that, even if you got Ball to move, you would fighting the layout manager all the time
Ball has no "size" (or position for that matter), so when it is added to the Container, it's sized to it's default size of 0x0, making it, virtually, invisible
Ball is never painted. This is actually for two reasons, but we'll start with the obvious, it doesn't override any valid paint methods that would allow Swing to paint it...if it was larger than 0x0.
Solutions...?
Make Ball just a POJO which knows it's size and location (and can update itself if that's what you need)
Create a "model" of some kind that can be shared between the view ColoredBallPanel and the controller (the thread). This model should maintain a List of Balls currently available...
Allow the ColoredBallPanel to loop through this list of balls and paint them via it's paintComponent method
Allow the controller to loop through this list of balls and update them.
Synchronise access to the balls list, so neither the view or controller can mess with the list while the other is using it. You might consider making a read-only version of the list for the view, but that might be beyond the scope right now...
Call super.paintComponent before you do any custom painting
Perhaps, something more like Java Bouncing Ball
I'm trying to call repaint from another class. But it does not work. I have to draw on a frame.
public class Tester extends JFrame{
public static dtest d ;
public static void main(String[] args) {
Tester t = new Tester();
d = new dtest();
test tnew = new test();
}
public static class dtest extends JFrame implements MouseMotionListener{
public static int x,y;
dtest()
{
super("title");
setSize(500,500);
setVisible(true);
addMouseMotionListener(this);
}
#Override
public void mouseDragged(MouseEvent e) {
x = e.getX();
y = e.getY();
repaint();
}
#Override
public void mouseMoved(MouseEvent e) {
// TODO Auto-generated method stub
}
public void paint(Graphics g)
{
System.out.println("I am called");
}
}
public static class test {
public test()
{
for(int i = 0 ; i < 5 ; i++)
{
System.out.println("I am called from run");
d.repaint();
}
}
}
}
this prints
I am called from run
I am called from run
I am called from run
I am called from run
I am called from run
so it does not executing the paint() portion. d.repaint() is not working. why?
Take a look at this page and look at the first answer. It's a similar if not exact question to yours.
JFrame's paint() method has been deprecated. The compiler, or your IDE, should be complaining a bit, especially if you place the #Override tag directly above the method (use this to test if this method can be rewritten... aka what you're trying to do).
This means that its use has been discouraged and some functionality may have been removed. When using javax.swing, you'll want to learn the system completely about JPanels and JComponents. To paint something on a screen, you'll want to add a custom class that extends JPanel with the add(Component c) method. Then, override the paintComponent(Graphics g) method in that class. Make sure to have the first line in that method be super.paintComponent(g); so that the window can refresh itself.
For completeness:
public class MyWindow extends JFrame {
MyPanel thePanel;
public MyWindow(int x, int y) {
setSize(x, y);
thePanel = new MyPanel(x, y);
this.add(thePanel);
}
}
public class MyPanel extends JPanel {
public MyPanel(int x, int y)
setSize(x, y);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(ImageManager.getImage("Cute Puppy"), 40, 40, null); // Or whatever
}
}
So, when the repaint() or revalidate() method is called on the MyWindow, the Panel will recieve a paintComponent call.
Please let me know in the comments if you need any additional help.
Edited:
Since you need to use MouseMotionListener, and I'm still not quite understanding the context and trouble of "I need to call repaint from another class"... I will try my best.
Firstly, check out this tutorial on the Oracle pages. Also, check out the others on GUI's. You'll learn a lot about organization and displaying that will make you realize how their system can work with yours.
Now, for your questions:
i have to use MouseMotionListener.
Not quite... it is a good way for set up but you can run a Thread (something that constantly runs methods over and over) to check the Mouse coordinates. You'll want to start doing this when you get into games and other miscellaneous applications.
new Thread() {
public void run() {
Point mouse;
int mousex;
int mousey;
while (true) {
mouse = MouseInfo.getPointerInfo().getLocation();
mousex = mouse.x - theWindow.getLocationOnScreen().x - 3; // You'll need to get the
// x coordinate, subtract the window's x coordinate, and subtract 3 because of
// the blue border around a standard pc window.
mousey = mouse.y - theWindow.getLocationOnScreen().y - 29; // 29 is top bar height
SomeOtherClass.processMove(mousex, mousey);
}
}
}.start();
Next: I tried that with JPanel but i could not do that. If you read the tutorial at the top of my edit, you see they implement MouseMotionListener with ease.
Next: I prefer to do it with JFrame. If you wish to process the mouse in the JFrame, do the following: Have your JFrame the listener, but the JPanel be where the mouse data comes from. As follows:
public class MyWindow extends JFrame implements MouseMotionListener {
public MyPanel thePanel;
public int x;
public int y;
public MyWindow() {
thePanel = new MyPanel();
thePanel.addMouseMotionListener(this);
// Make this JFrame get called when the mouse
// moves across the panel.
}
#Override
public void mouseDragged(MouseEvent e) {
x = e.getX();
y = e.getY();
thePanel.repaint();
}
#Override
public void mouseMoved(MouseEvent e) {
// TODO Auto-generated method stub
}
}
public class MyPanel extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g);
// Other painting stuff
}
}
Next: Now i have to update the frame from another class. I could not find a way to update the GUI(the frame) from another class.
Simple. Since the JPanel is what needs to be updated, add the following method to the MyWindow class:
public void repaintWindow() {
thePanel.repaint();
}
And add this to whenever you need to update it:
MyWindow theWindow = new MyWindow();
theWindow.repaintWindow();
Next: all the answers here extended JPanel. So i could not find my answer.
I apologize, but you NEED a panel. It is possible to do with JFrames, but if you want to start doing things raw and low-level, you need to learn how these things work by learning to read the oracle tutorials and the oracle documentation. For now, use JPanels in any ways I've shown you.
Next: from another class I have to draw something on JFrame.Is that possible?
Yes, indeed! Whenever you want to draw something:
MyWindow theWindow = new MyWindow();
Graphics g = theWindow.thePanel.getGraphics();
BufferedImage someRandomImage = SomeRandomClass.getRandomImage();
g.drawImage(someRandomImage, 200, 481, null);
theWindow.repaintWindow();
I really hope I've helped but to program in java you need to use the tools they give you, especially when it comes to high level things like Swing. There are tutorials everywhere for this stuff. Please read them before asking for specific help in the future.
I have a custom, abstract class 'Panel' which extends JPanel. There aren't many differences with the two when painting. I have a Panel and I'm simulating an animation by updating the x value of an image. I have two animations right now, one that properly repaints and another than does not. This is for the one that does not. The one that works will be labelled A, the one that doesn't will be B.
A and B follow the same format. Update some variable on the Panel, calls update (a method in Panel which calls PaintComponent) and then calls repaint. It calls repaint after because this issue was with A before and was solved that way.
A: Updates an image variable.
B: Updates the x variable of an image.
The Problem: The repaint doesn't clear the old image location and so it's a choppy mess across the screen.
What I've tried:
I've seen the super.PaintComponent(g) mentioned a lot, but this
hasn't solved the problem.
I've tried changing the order for when the repaint/update methods are
called.
Repaint does not update the Panel at all. (Probably because the
painting is done in PaintComponent)
Any help would be appreciated.
Code:
Panel:
public Panel (boolean visible){
super();
this.setLayout(new BorderLayout(640, 416));//sets the Layout type of the panel
this.setOpaque(false);//Makes it so that the panel underneath can be seen where images aren't drawn
this.setVisible(visible);
ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
gs = ge.getDefaultScreenDevice();
gc = gs.getDefaultConfiguration();
}
public void paintComponent (Graphics g){
setUp();
drawOff();
setDown(g);
}
private void setUp(){
off_screen = gc.createCompatibleImage(getSize().width, getSize().height, Transparency.TRANSLUCENT);
buffer = off_screen.createGraphics();
}
protected abstract void drawOff();
private void setDown(Graphics g){
g.drawImage(off_screen,0,0,this);
off_screen.flush();
}
public void update(){
paintComponent(this.getGraphics());
}
Animation Methods (mg is the panel in question):
private void battleStart(User user) {
for (int i = 0; i < user.battle.length; i++) {
mg.battleStart(user.battleStart(i));
mg.update();
try {
Thread.sleep(150);
} catch (Exception e) {
}
mg.repaint();
}
}
private void animateStart(User user){
for (int i = 0; i < 10; i++){
mg.x = mg.x + 10;
mg.update();
try {
Thread.sleep(100);
} catch (Exception e) {
}
mg.repaint();
}
}
I think your design is way off and that is why things are not working. I'm not quite sure how your non-abstract JPanels work, but consider making your parent JPanel something more along these lines:
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
public class MyPanel extends JPanel {
private GraphicsEnvironment ge;
private GraphicsDevice gs;
private GraphicsConfiguration gc;
private BufferedImage offScreen;
public MyPanel(boolean visible) {
super();
this.setLayout(new BorderLayout(640, 416)); // strange constants for this layout.
this.setOpaque(false);
this.setVisible(visible);
ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
gs = ge.getDefaultScreenDevice();
gc = gs.getDefaultConfiguration();
addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
setUp();
}
});
}
#Override
// don't make this public. Keep it protected like the super's
// just draw in this method. Don't call other methods that create buffers
// or draw to buffers.
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (offScreen != null) {
g.drawImage(offScreen, 0, 0, null);
}
}
private void setUp() {
offScreen = gc.createCompatibleImage(getSize().width, getSize().height,
Transparency.TRANSLUCENT);
}
// draw to the buffer outside of the paintComponent
// and then call repaint() when done
public void upDateOffScreen() {
// ?? offScreen.flush(); // I've never used this before,
// so am not sure if you need this here
Graphics2D osGraphics = offScreen.createGraphics();
// TODO: do drawing with osGraphics object here
osGraphics.dispose();
repaint();
}
}
Also and again,
Do all long processing methods off of the EDT (Event Dispatch Thread).
Never call Thread.sleep(...) on the EDT.
Consider using Swing Timers instead of using Thread.sleep for the animations.
It's OK to call repaint on your JPanel off of the EDT, but for the most part that's about it.
All other Swing methods should be called on the EDT.
Read, re-read, and study the 2D and Swing graphics tutorials.