Timer of swing not performing as expected - java

I was suggested not to use sleep for pausing purpose and instead use swing timer but still its not working.The animation i want to achieve is a ball travelling from the top left corner diagonally towards the bottom.
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
class Show_starter {
int x, y;
JFrame window = new JFrame("Graphic_show");
Graphic_panel jp = new Graphic_panel();
public static void main(String[] args) {
Show_starter start = new Show_starter();
start.go();
}
private void go() {
window.getContentPane().add(BorderLayout.CENTER, jp);
window.setSize(600,800);
window.setVisible(true);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
class Graphic_panel extends JPanel {
public void paintComponent(Graphics g) {
for ( int i = 0; i < 100; ++i) {
g.setColor(Color.white);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
g.setColor(Color.green);
g.fillOval(x, y, 40, 40);
x++;
y++;
try {
Timer tmr = new Timer(1000, new TimerListener()); //This fires an event after every 1 sec after it has started by start().
//But The ball travels too much fast and stops at a point and again travels very fast.
tmr.serRepeats(false); // even this is not working.
tmr.start();
//should i use repaint here or in Listener for this timer?
} catch (Exception e){}
}
}
class TimerListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
jp.repaint();
}
}
}
}
The behaviour it is showing is very odd ball first goes at very high speed and stops momentarily at a point and again moves at same speed.
I have also tried to change the time for timer event to fire but the same thing happens.
Even inside loop where I have started timer the follow function call is not working
setRepeats(false);

don't create new timers always one timer is enough .decrees timing interval bit more .1 second is too slow for a animation.you don't need a loop and don't increment x and y inside paintcomponet() method because this method get called for some reason for example when you resize,minimize,maximize.
the unexpected behavior is due to your loop and creating new timers(). for example in x y start at zero but in first second x y increased to 100,100 in next paint you see them in a 100,100 position .that's look like quick move and then stop ...
example code (edited)
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
public class Show_starter {
private Timer tmr;
int x, y;
JFrame window = new JFrame("Graphic_show");
Graphic_panel jp = new Graphic_panel();
public static void main(String[] args) {
Show_starter start = new Show_starter();
start.go();
}
private void go() {
window.getContentPane().add(BorderLayout.CENTER, jp);
window.setSize(600, 800);
window.setVisible(true);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
tmr = new Timer(10, new ActionListener() { // time gap in millisecond
#Override
public void actionPerformed(ActionEvent ae) {
jp.increse();
jp.repaint();
}
});
tmr.start();
}
class Graphic_panel extends JPanel {
public void increse() {
x++;
y++;
if (x > 100) { // stop animation at x>100
tmr.stop();
}
}
public void paintComponent(Graphics g) {
g.setColor(Color.white);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
g.setColor(Color.green);
g.fillOval(x, y, 40, 40);
}
}
}
output (smoother than that)

Firstly you should not use a loop inside paint component.
public void paintComponent(Graphics g){
g.setColor(Color.white);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
g.setColor(Color.green);
g.fillOval(x, y, 40, 40);
}
Just draw it and make x and y public in the class so that you can easily access from outside
Now run a runnable (consult invokelater and swingUtilities) or create a new Thread
inside public void run() do whatever animations you want to do . You can use a loop but add Thread.sleep(//in miliseconds) and repaint() in each iteration .
note : you need to change the x and y and repaint() so that it is drawn in a new position. And the sleep() is needed to slow down the execution otherwise it can't be visible to viewer.

Related

Need help positioning in java

I created this java program and I wanted an output of which if int x and int y are above 100, it would draw a rectangle. But it doesn't. How can I make it work?Is there another line of code I need to add?
Here's my code:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
#SuppressWarnings("serial")
public class GameSetup extends JPanel implements MouseMotionListener{
public static JFrame njf = new JFrame("Test");
public static int x, y;
public static void main(String[] args){
GameSetup gs = new GameSetup();
njf.add(gs);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
this.setBackground(Color.BLACK);
g.setColor(Color.GREEN);
g.fillRect(150, 75, 200, 100);
g.setColor(Color.ORANGE);
g.drawString("Play", 239, 123);
njf.addMouseListener(new MouseAdapter() {
public void mouseMoved(MouseEvent e) {
x = e.getX();
y = e.getY();
}
});
if(x > 100 && y > 100){
g.drawRect(10, 10, 100, 100);
}
}
public GameSetup(){
njf.setSize(500,500);
njf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
njf.setResizable(false);
njf.setLocationRelativeTo(null);
njf.setVisible(true);
}
#Override
public void mouseDragged(MouseEvent arg0) {
}
#Override
public void mouseMoved(MouseEvent e) {
}
}
Okay, there are several things wrong with the code that you included above.
The first that stood out to me was the way that you are adding the mouse action listener to frame. There are multiple things that are wrong with this.
First of all, you are doing this in the paintComponent method, which, if it works, is still considered to be bad practice because the paintComponent method may be called multiple times. Do that, as pointed out by the comments, in the constructor of the panel.
The second is that you are adding the mouse listener to the frame, not the panel, which doesn't work because the panel is "above" the frame, so the mouse event will only be recognized within the panel. Your best bet here is to add a MouseMotionListener directly to the panel itself.
The third is that you are implementing the MouseMotionListenerinterface in the GameSetup class, but never actually doing anything with this implementation. So what I did was that I got rid of the inner class, and just had the panel be its own MouseMotionListnener
The second thing that is wrong with the code is that the paintComponent method is only called at certain points in time (see this). That means that even though the mouse might have moved within the zone, your paintComponent method isn't being called to update the screen accordingly. For this, you need to call the repaint method for your panel.
The third is that you didn't set a size for your panel, and the default one is 0x0, so you need to set a size of your panel (which should be the same as the frame itself) (if you want to keep the default layout).
All of that being said, here is your code that I fixed up. I added a variable called enteredZone to keep track of if the mouse had previously entered the zone, so that the rectangle would stay up even if the mouse left the zone after entering it (its your choice if you want to keep it). Note that there are other things with this code that might be considered bad practice, but this is enough to get you started:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
#SuppressWarnings("serial")
public class GameSetup extends JPanel implements MouseMotionListener {
public static JFrame njf = new JFrame("Test");
public static int x = 0, y = 0;
public static boolean enteredZone = false;
public static void main(String[] args) {
GameSetup gs = new GameSetup();
gs.addMouseMotionListener(gs);
njf.add(gs);
njf.setVisible(true);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
this.setBackground(Color.BLACK);
g.setColor(Color.GREEN);
g.fillRect(150, 75, 200, 100);
g.setColor(Color.ORANGE);
g.drawString("Play", 239, 123);
if (x > 100 && y > 100 || enteredZone){
g.drawRect(10, 10, 100, 100);
enteredZone = true;
}
}
public GameSetup() {
super();
setSize(500, 500);
njf.setSize(500,500);
njf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
njf.setResizable(false);
njf.setLocationRelativeTo(null);
}
#Override
public void mouseDragged(MouseEvent arg0) {
}
#Override
public void mouseMoved(MouseEvent e) {
x = e.getX();
y = e.getY();
if (x > 100 && y > 100) repaint();
}
}

Why does my loop run twice?

I'm trying to run basic animations, and all of the problems in my program is rooted in one of my loops running twice instead of once. I've isolated the problem here:
import java.awt.*;
import java.applet.*;
public class PrinnyTest extends Applet{
public void init()
{
setSize(600, 550);
}
public void paint (Graphics g)
{
Image img = getImage(getDocumentBase(), "http://www.clipartqueen.com/image-files/cats-head.png");
for(int y = 600;y>=20; y--)
{
g.setColor(Color.white);
g.fillRect(0, 0, 600, 550);
g.drawImage(img, 40, y, null);
try
{
Thread.sleep(20);
}
catch(InterruptedException ie)
{}
}
}
}
As you can see, it moves the image to the top but then resets the position and starts the loop over again. I feel like the answer is staring me right in the face, but for some reason I can't figure it out. Any help would be greatly appreciated.

Getting the font metrics before the paint method id called

Hi I am creating a news ticker/ text scroller.
I am using the following method:
import java.awt.Color;
import java.awt.FontMetrics;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Scroll1 extends JPanel{
private int x;
private int x2;
private int y;
private String text;
final int startX=-100;
public Scroll1(int startX)
{
x2=-650;
x = 20;
y=150;
text= "Some Words and others, and now this must be a longer text that takes up the whole panel/ frame for this test to work ";
}
#Override
public void paint(Graphics g)
{
g.setColor(Color.white);
g.fillRect(0, 0, 400, 300);
g.setColor(Color.black);
g.drawString(text, x, y);
g.drawString(text, x2, y);
FontMetrics fm= g.getFontMetrics();
System.out.println(fm.stringWidth(text));;
}
public void start() throws InterruptedException{
while(true){
while(x<= 650){
x++;
x2++;
y = getHeight()/2;
repaint();
Thread.sleep(10);
if(x2>650)
x2=-650;
}
if(x>=0)
{
x=-650;
}
}
}
public static void main(String[] args) {
JFrame frame = new JFrame("Scrolling Panel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Scroll1 scrolling = new Scroll1(-100);
frame.getContentPane().add(scrolling);
frame.setSize(400, 300);
frame.setVisible(true);
try {
scrolling.start();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Basically it has two strings that are being drawn. One starts at the 0 position and the other starts at -650. I got the -650 number by using the font metrics inside of the paint method. The problem is that I had to hard code that number, and if I did a different string that has different metrics, it would not work. I tried making a instance variable called width that stores the font metrics, but it seems that the width is not inputted until the paint method is called. Is there anyway I can get the metrics before it starts drawing it?
Is there anyway I can get the metrics before it starts drawing it?
Just initialize the variable in the first call to paint (or better yet, paintComponent - see below) - you can do this using a boolean flag, or initialize it's value to an extreme and do a check on the value.
int x = Integer.MIN_VALUE;
...
protected void paintComponent(Graphics g){
super.paintComponent(g);
if ( x == Integer.MIN_VALUE ){
x = -g.getFontMetrics().stringWidth(text);
}
...
}
Some other tips:
Use a Swing Timer to perform animation, or be sure to dispatch Swing specific calls to the EDT using SwingUtilities.
Don't override paint, rather override paintComponent (and be sure to call the parent method super.paintComponent(g))

Circle not moving

There's no errors, but when I press any of the buttons, my oval/circle doesn't move at all? Can anyone help? I've been looking up and down the code for about 20 minutes seeing if I typed anything wrong or put something in the wrong place. I can't tell if this is with the way I'm moving it or my thread.
package com.badfitz66.mainpackage;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.JFrame;
public class Main extends JFrame implements Runnable
{
int x, y, xDirection, yDirection;
private Image dbImage;
private Graphics dbG;
Font font = new Font("Black Caps", Font.ITALIC | Font.BOLD, 30);
public void run()
{
try
{
while(true)
{
Move();
Thread.sleep(5);
}
}
catch(Exception e){
System.out.println("Error");
}
}
public void Move()
{
x += xDirection;
y += yDirection;
if (x <= 0)
x = 0;
if(x >= 500)
x = 500;
if (y <= 50)
y = 50;
if (y >= 250)
y = 250;
}
public void setXDir(int xdir)
{
xDirection = xdir;
}
public void setYDir(int ydir)
{
yDirection = ydir;
}
public class AL extends KeyAdapter
{
public void keyPressed(KeyEvent e)
{
int keyCode = e.getKeyCode();
if(keyCode == e.VK_D)
{
setXDir(+1);
}
if(keyCode == e.VK_A)
{
setXDir(-1);
}
if(keyCode == e.VK_W)
{
setYDir(-1);
}
if(keyCode == e.VK_S)
{
setYDir(+1);
}
}
public void keyReleased(KeyEvent e)
{
int keyCode = e.getKeyCode();
if(keyCode == e.VK_D)
{
setXDir(0);
}
if(keyCode == e.VK_A)
{
setXDir(0);
}
if(keyCode == e.VK_W)
{
setYDir(0);
}
if(keyCode == e.VK_S)
{
setYDir(0);
}
}
}
public Main()
{
addKeyListener(new AL());
setTitle("Java game testing");
setResizable(false);
setVisible(true);
setSize(500, 500);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBackground(Color.green);
x = 150;
y = 150;
}
public void paint(Graphics g)
{
dbImage = createImage(getWidth(),getHeight());
dbG = dbImage.getGraphics();
paintComponent(dbG);
g.drawImage(dbImage, 0, 0, this);
}
public void paintComponent(Graphics g)
{
g.setFont(font);
g.drawString("Hello world", 125, 50);
g.setColor(Color.cyan);
g.fillOval(x, y, 15, 15);
repaint();
}
public static void main(String[] args)
{
Main jg = new Main();
//Threads
Thread t1 = new Thread();
t1.start();
}
}
You never call repaint from within your Move method
Thread t1 = new Thread(); won't do much, as it will never call any runnable code, in fact it will start and terminate almost immediately, how ever...
Swing is not thread safe and you should never modify the UI or anything the UI relies on from outside the Event Dispatching Thread, this especially important, as a paint cycle could occur at any time. See Concurrency in Swing for more details
You override the paint method of a top level container (JFrame) and then break the paint chain...paint is complex series of method calls chained together to generate the final result, you should always call super.paint first, but as you probably know, JFrame is not double buffered. So instead, you should create another class that extends from JPanel and override it's paintComponent method to perform the actual painting (in fact, for the most part, it should pretty much replace the functionality that the current JFrame is doing)...Swing components are double buffered by default...
Calling repaint from within a paint method...this is bad news and this will immediately schedule another paint cycle, this becomes so fast that it consume all your CPU cycles till you computer stands still
Using KeyListener. KeyListener is notorious for having issues, in particular, it will only ever trigger a key event if the component it is registered to IS FOCUSABLE and HAS FOCUS. A JFrame is made up of the physical window, the JRootPane, which holds the content pane (and a few other components), all of which can get in the way of the frame actually getting focus. Instead, using the previously mentioned JPanel, use the key bindings API, which will allow to control the level of focus required for the key events to be triggered. See How to Use Key Bindings for more details
You should also have a look at...
Performing Custom Painting
Painting in AWT and Swing
How to use Swing Timers

Java making a ball bounce, its left a trail behind?

Im newish to java, and im writing a pong game.
Ive got the ball to move (Thats my first priority) and make it apear on screen.
But for some Reason its leaving a black line behind it, I dont know if im supposed to errase this or something, but heres the code of my 2 classes (The Ball Class is just a class to save the info of the ball)
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
import javax.swing.JFrame;
public class Main extends Canvas implements Runnable{
private static final long serialVersionUID = 1L;
public static int Width=650;
public static int Height=600;
public boolean Running=false;
public Thread thread;
public Ball ball = new Ball();
public static void main(String[] args){
Main game = new Main();
JFrame frame = new JFrame();
frame.setSize(Width+25, Height+49);
frame.setTitle("Pong By Poo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.setVisible(true);
frame.setLocationRelativeTo(null);
frame.add(game);
game.start();
}
public void start(){
if(Running==true){
return;
} else {
Running=true;
thread = new Thread(this);
thread.start();
}
}
public void run(){
while(Running==true){
try{
Thread.sleep(5);
Draw();
Update();
ball.YVelocity();
ball.XVelocity();
} catch(Exception e){}
}
}
public void Draw(){
BufferStrategy bs = this.getBufferStrategy();
if(bs==null){
createBufferStrategy(2);
} else {
Graphics g = bs.getDrawGraphics();
g.setColor(Color.BLACK);
g.fillOval(ball.BallLocationX, ball.BallLocationY, 20, 20);
g.dispose();
bs.show();
}
}
public void Update(){
if(ball.BallLocationX==0) {
ball.BallMovementX=true;
System.out.println("Ball has hit the Left");
}
if(ball.BallLocationX==Width) {
ball.BallMovementX=false;
System.out.println("Ball has hit the Right");
}
if(ball.BallLocationY==0){
ball.BallMovementY=true;
System.out.println("Ball has hit the Top");
}
if(ball.BallLocationY==Height){
ball.BallMovementY=false;
System.out.println("Ball has hit the bottom");
}
}
}
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
public class Ball extends JPanel{
public int BallLocationX;
public int BallLocationY;
public boolean BallMovementY; //true makes the ball go up, false makes it go down
public boolean BallMovementX; //True makes the ball go right, false makes it go left.
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(Color.BLACK);
g.fillOval(BallLocationX, BallLocationY, 10, 10);
}
//moves the ball left to right
public int YVelocity(){
if(BallMovementY==true){
BallLocationY++;
} else {
BallLocationY--;
}
return BallLocationY;
}
//Moves the ball up and down
public int XVelocity(){
if(BallMovementX==true){
BallLocationX+=2;
} else {
BallLocationX-=2;
}
return BallLocationX;
}
}
Please help!
Essentially, everywhere your ball moves stays permanently Color.BLACK on your Canvas. If you want to get rid of that, you need to refresh every time the ball moves and repaint the Canvas to Color.WHITE (or whatever) before painting the ball's position again.
Specifically, look at your code here:
public void Draw(){
BufferStrategy bs = this.getBufferStrategy();
if(bs==null){
createBufferStrategy(2);
} else {
Graphics g = bs.getDrawGraphics();
g.setColor(Color.BLACK);
g.fillOval(ball.BallLocationX, ball.BallLocationY, 20, 20);
g.dispose();
bs.show();
}
}
There is no logic here that overwrites your previous changes to the Canvas indicating the ball's location.
Also, as a side nitpick, Java standards are to have method names in camelCase.
And to answer the question in the comments: there is nothing in the Canvas API which automatically understands what you want the default background of the Canvas to be and can reset all other graphics attributes to that. To get this functionality, however, all you'll need to do is repaint your default layout (whether it's all one color, or a basic background image, or anything else) before painting the ball's position on top of it.

Categories