I'm now having problems with JButton.
My JButton will not get added to the JFrame or the JPanel and it seems to be disabling paintComponent() from running.
Here's my JPanel class:
public class BLANKScreen extends JPanel implements Runnable {
Thread thread = new Thread(this);
public boolean running = false;
public static ImageIcon greenButton = new ImageIcon("resources/menubuttons/GreenButton.png");
public static JButton mainMenuPlayButton = new JButton("Play!", greenButton);
private int fps;
static BLANKWindow w;
public int scene = 0;
public void run() {
long lastFrame = System.currentTimeMillis();
int frames = 0;
running = true;
scene = 0;
while (running) {
if (scene == 0) {
addMainMenuButtonsOptions();
} else if (scene == 1) {
}
repaint();
frames++;
if (System.currentTimeMillis() - 1000 >= lastFrame) {
fps = frames;
frames = 0;
lastFrame = System.currentTimeMillis();
}
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.exit(0);
}
public BLANKScreen (BLANKWindow w) {
this.w = w;
this.w.addKeyListener(new KeyHandler(this));
this.w.addMouseListener(new MouseHandler(this));
thread.start();
}
public class KeyTyped {
public void keyESC() {
running = false;
}
public void keySpace() {
if (scene == 0) {
scene = 1;
} else if (scene == 1) {
scene = 0;
}
}
}
public class MouseClicked { }
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.clearRect(0, 0, this.w.getWidth(), this.w.getHeight());
if (scene == 0) {
int nameLength = g.getFontMetrics().stringWidth("BLANK!");
g.drawString("BLANK!", w.getWidth() / 2 - nameLength / 2, w.getHeight() / 4);
}
}
public void addMainMenuButtonsOptions() {
mainMenuPlayButton.setActionCommand("/mainMenuPlayButton");
mainMenuPlayButton.addActionListener(new ActionHandler());
this.add(mainMenuPlayButton);
System.out.println("ThingHappend");
}
}
And the this.add(mainMenuPlayButton); also doesn't work when I say w.add(mainMenuPlayButton);.
Here's my JFrame class:
public class BLANKWindow extends JFrame {
public static void main(String[] args) {
new BLANKWindow();
}
public BLANKWindow() {
new JFrame();
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
int screenHeight = screenSize.height;
int screenWidth = screenSize.width;
this.setSize(screenWidth, screenHeight);
this.setTitle("BLANK");
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setResizable(true);
this.setExtendedState(MAXIMIZED_BOTH);
this.setVisible(true);
BLANKScreen dbs = new BLANKScreen(this);
this.add(dbs);
}
}
For some reason neither JButton nor paintCompenent() has decided to load?
Basically, at the core, you have a thread violation of the Swing API and seem to have a lack of knowledge about the basic workings of Swing...Welcome to the danger zone...
Swing is single threaded and is not thread safe. This means that you should block the EDT but you should also never interact with or modify any UI components from outside the EDT
You need to change your approach.
Swing already has an Event Queue, a thread for processing those events, the Event Dispatching Thread and uses a passive painting algorithm to update the UI (and layout managers).
You need to working within those constraints in order to make it all work.
Instead of trying to switch states within the BLANKScreen class, you should start with something like a "menu panel" and a "game panel".
The "menu panel" will display the menu options to the user, the "game panel" will display the game, it will have the game loop and painting logic for the actual game.
Now, you "could" get them to work together, but I think you need to break them apart to start with...it will only complicate the issues...
You could then use CardLayout to switch between them. This will make it easier to change state the visible states.
Also take a look at:
Painting in AWT and Swing
Concurrency in Swing
Updated...
The first rule Object Oriented Programming - separation of responsibilities. An object is responsible for it's job and only it's job. If you find that your object is trying to do more (like update the frames of game AND display a menu), then you're think along the wrong lines (especially in this case).
You need to separate the menu from the game. The game has a update mechanism, the menu doesn't care, it doesn't need it, it's updated by the core framework.
The game has user interaction of it's own, so does the menu, but they have different meanings to each other (a mouse click on the menu does something, but in the game it does something else), so they should separated...
The menu and the game probably don't even care about each other, so you could use a controller of some kind, which controls the state between two and facilitates the communication
This is a crude example, I would normally spend more time building better controllers and models, but this is designed to demonstrate the point of the concept, not provide a fully, out of the box, running solution...
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestGameMenu {
public static void main(String[] args) {
new TestGameMenu();
}
public TestGameMenu() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new MainView());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class MainView extends JPanel {
private MenuPane menuPane;
private GamePane gamePane;
public MainView() {
setLayout(new CardLayout());
menuPane = new MenuPane();
gamePane = new GamePane();
add(menuPane, "menu");
add(gamePane, "game");
menuPane.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if ("play".equalsIgnoreCase(e.getActionCommand())) {
((CardLayout) getLayout()).show(MainView.this, "game");
gamePane.resume();
}
}
});
}
}
public class MenuPane extends JPanel {
private JButton playGame;
public MenuPane() {
setLayout(new GridBagLayout());
playGame = new JButton("Play My Awesome Game!");
playGame.setActionCommand("play");
add(playGame);
}
public void addActionListener(ActionListener listener) {
playGame.addActionListener(listener);
}
public void removeActionListener(ActionListener listener) {
playGame.removeActionListener(listener);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
public class GamePane extends JPanel {
private Point p;
private int xDelta;
private int yDelta;
private volatile boolean running = true;
private volatile boolean paused = true;
private ReentrantLock lckPause;
private Condition conPause;
public GamePane() {
p = new Point(100, 100);
do {
xDelta = (int)((Math.random() * 10) - 5);
} while (xDelta == 0);
do {
yDelta = (int)((Math.random() * 10) - 5);
} while (yDelta == 0);
lckPause = new ReentrantLock();
conPause = lckPause.newCondition();
Thread t = new Thread(new Runnable() {
#Override
public void run() {
while (running) {
while (paused) {
lckPause.lock();
try {
conPause.await();
} catch (InterruptedException ex) {
} finally {
lckPause.unlock();
}
}
p.x += xDelta;
p.y += yDelta;
if (p.x < 0) {
p.x = 0;
xDelta *= -1;
} else if (p.x > getWidth()) {
p.x = getWidth();
xDelta *= -1;
}
if (p.y < 0) {
p.y = 0;
yDelta *= -1;
} else if (p.y > getHeight()) {
p.y = getHeight();
yDelta *= -1;
}
repaint();
if (running && !paused) {
try {
Thread.sleep(40);
} catch (InterruptedException ex) {
}
}
}
}
});
t.start();
}
public void pause() {
if (!paused && running) {
lckPause.lock();
try {
paused = true;
} finally {
lckPause.unlock();
}
}
}
public void resume() {
if (paused && running) {
lckPause.lock();
try {
paused = false;
conPause.signalAll();
} finally {
lckPause.unlock();
}
}
}
public void stop() {
if (running) {
lckPause.lock();
try {
paused = false;
running = false;
conPause.signalAll();
} finally {
lckPause.unlock();
}
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.RED);
g2d.fillOval(p.x - 5, p.y - 5, 10, 10);
g2d.dispose();
}
}
}
#MadProgrammer 's comment has the answer. But aside from the severe threading problem, you appear to be adding the button many times to the container. As far as I can tell, addMainMenuButtonsOptions is called every 2 ms until a key is pressed. (Although you don't show the code that uses the class KeyTyped, I assume you have bound it to a key listener?).
A smaller point: Have you considered using setBackground and setOpaque in the constructor, instead of calling clearRect manually?
Also as #Tom pointed out, it looks like you are using a default layout so the (multiple?) buttons will probably float to the top - is that what you want?
Related
I'm new to Eclipse, recently swapped from Bluej which ran my codes reliably. In Eclipse, it sometimes runs and sometimes just doesn't run the paint method and I'm not sure why. The same code was running this morning and now it decides to not run and I'm not sure what to do.
Main method:
import java.awt.Container;
import java.awt.Dimension;
import javax.swing.JFrame;
import javax.swing.JPanel;
import Asset.Paddle;
import Asset.Puck;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseEvent;
import java.awt.BorderLayout;
public class setup implements KeyListener, MouseListener, MouseMotionListener, Runnable {
int width = 100;
int height = 100;
int scale = 8;
public static setup setup;
JFrame frame;
JPanel main;
Graphic graphic;
Puck puck;
Paddle paddle1,paddle2;
boolean running, up = true, up2 = true;
boolean menu = false, b1, b2, b3;
int winSize;
public setup() {
puck = new Puck((width*scale)/2,(width*scale)/2,20,20);
paddle1 = new Paddle(width*scale/8-20,height*scale/2,20,100);
paddle2 = new Paddle(width*scale/8*7,height*scale/2,20,100);
frame();
}
public void frame() { //Frame setup
frame = new JFrame("Pong");
frame.setSize(width * scale,height * scale);
frame.setLayout(new BorderLayout());
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container c1 = frame.getContentPane();
Dimension winSize = frame.getSize();
System.out.println(winSize);
graphic = new Graphic(puck,paddle1,paddle2);
graphic.addKeyListener(this);
graphic.addMouseListener(this);
graphic.addMouseMotionListener(this);
main = new JPanel();
main.setLayout(new BorderLayout());
main.setSize(width * scale,height * scale);
main.add(graphic,BorderLayout.CENTER);
start();
c1.add(main);
graphic.requestFocus();
}
public void start() { //running = true
new Thread(this).start();
running = true;
menu = true;
RENDER();
}
public void stop() {
running = false;
}
public void run() { //Game
while(running == true) {
try {
Thread.sleep(10);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
if(puck.getY() < 0 || puck.getY() > height * scale) {
puck.reverseY();
}
paddle1.run();
paddle2.run();
puck.run();
RENDER();
}
}
public void RENDER() {
graphic.UPDATEPADDLE(paddle1,paddle2);
graphic.UPDATEPUCK(puck);
graphic.repaint();
}
public void keyPressed(KeyEvent evt) {
if(evt.getKeyCode() == 81) { // Q
paddle1.setYVel(-2);
up = true;
}
if(evt.getKeyCode() == 65) { // A
paddle1.setYVel(2);
up = false;
}
if(evt.getKeyCode() == 80) { // P
paddle2.setYVel(-2);
up2 = true;
}
if(evt.getKeyCode() == 76) {
paddle2.setYVel(2);
up2 = false;
}
}
public void keyReleased(KeyEvent evt) {
if(evt.getKeyCode() == 81 && up ) { // Q
paddle1.setYVel(0);
}
if(evt.getKeyCode() == 65 && !up) { // A
paddle1.setYVel(0);
}
if(evt.getKeyCode() == 80 && up2) { // P
paddle2.setYVel(0);
}
if(evt.getKeyCode() == 76 && !up2) { // L
paddle2.setYVel(0);
}
}
public void keyTyped(KeyEvent evt) {}
public void mouseClicked(MouseEvent e) {
// if(e.getX() > 375 && e.getX() < 375 + 200 && e.getY() > 400 && e.getY() < 400 + 50) {
// menu = false;
// System.out.println("clicked");
// graphic.UPDATEBUTTON(b1,b2,b3);
// graphic.UPDATEMENU(menu);
// start();
// graphic.repaint();
// }
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
public void mousePressed(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
}
public static void main(String[] args) {
setup = new setup();
}
public void mouseDragged(MouseEvent e) {
}
public void mouseMoved(MouseEvent e) {
if(e.getX() > 375 && e.getX() < 375 + 200 && e.getY() > 400 && e.getY() < 400 + 50) {
b1 = true;
graphic.UPDATEBUTTON(b1,b2,b3);
graphic.repaint();
}
else {
b1 = false;
graphic.UPDATEBUTTON(b1,b2,b3);
graphic.repaint();
}
}
}
Paddle:
package Asset;
public class Paddle {
double x, y, yVel, h, w;
public Paddle(double xx, double yy, int width, int height) {
x = xx;
y = yy;
h = height;
w = width;
}
public int getX() {
return (int)x;
}
public int getY() {
return (int)y;
}
public int getH() {
return (int)h;
}
public int getW() {
return (int)w;
}
public void setYVel(int yVelocity) {
yVel = yVelocity;
}
public void run() {
y += yVel;
if(y < 0) {
yVel = 0;
y = 0;
}
}
}
Puck:
package Asset;
public class Puck {
double x,y,w,h;
double xVel = 0;
double yVel = 3;
public Puck(double xx, double yy,int width,int height) {
x = xx;
y = yy;
w = width;
h = height;
x++;
}
public void reverseY() {
yVel *= -1;
}
public void reverseX() {
xVel *= -1;
}
public int getX() {
return (int)x;
}
public int getY() {
return (int)y;
}
public int getW() {
return (int)w;
}
public int getH() {
return (int)h;
}
public void run() {
x += xVel;
y += yVel;
}
}
Setup:
import java.awt.*;
import javax.swing.*;
import Asset.Paddle;
import Asset.Puck;
public class Graphic extends JPanel {
private static final long serialVersionUID = 2273791975624707192L;
Puck ppuck;
Paddle ppaddle1,ppaddle2;
boolean mmenu = true;
boolean bb1,bb2,bb3;
public Graphic(Puck puck,Paddle paddle1,Paddle paddle2) {
ppuck = puck;
ppaddle1 = paddle1;
ppaddle2 = paddle2;
}
public void UPDATEMENU(boolean menu) {
mmenu = menu;
}
public void UPDATEPADDLE(Paddle paddle1, Paddle paddle2) {
ppaddle1 = paddle1;
ppaddle2 = paddle2;
}
public void UPDATEPUCK(Puck puck) {
ppuck = puck;
}
public void UPDATEBUTTON(boolean b1,boolean b2, boolean b3) {
bb1 = b1;
bb2 = b2;
bb3 = b3;
}
public void paint (Graphics g) {
super.paint(g);
g.setColor(Color.black);
g.fillRect(0, 0, 1000, 1000);
if (mmenu) { //menu
if(bb1) {
g.setColor(Color.blue);
g.setFont(new Font("TimesRoman", Font.PLAIN, 50));
g.drawString("START", 390, 440);
}
else {
g.setColor(Color.white);
g.setFont(new Font("TimesRoman", Font.PLAIN, 50));
g.drawString("START", 390, 440);
}
}
else {
g.setColor(Color.white);
g.fillOval(ppuck.getX(), ppuck.getY(), ppuck.getW(), ppuck.getH());
g.fillRect(ppaddle1.getX(), ppaddle1.getY(), ppaddle1.getW(), ppaddle1.getH());
g.fillRect(ppaddle2.getX(), ppaddle2.getY(), ppaddle2.getW(), ppaddle2.getH());
}
}
}
So, two "basic" problems...
One, if you modify the UI after the frame is visible, you must call revalidate and repaint too trigger a layout and paint pass. A simpler solution, in your case, would be to call setVisible AFTER you've established the UI
public void frame() { //Frame setup
frame = new JFrame("Pong");
frame.setSize(width * scale, height * scale);
frame.setLayout(new BorderLayout());
frame.setLocationRelativeTo(null);
//frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container c1 = frame.getContentPane();
//...
start();
c1.add(main);
graphic.requestFocus();
frame.setVisible(true);
}
Second...
This one's a little more complicated, but, you start your Thread and then update the state which it relies on to keep running
public void start() { //running = true
new Thread(this).start();
running = true;
menu = true;
RENDER();
}
While very, very unlikely, it's possible that the thread will inspect the state before you change it ... or because of the way the memory model works, won't see the change.
Better to set it before hand...
public void start() { //running = true
running = true;
new Thread(this).start();
menu = true;
RENDER();
}
You should also consider making it volatile
Having said that...
You're going around it all the wrong way.
To start with, don't try and do all the rendering for all the states in the single view, instead, use seperate views to different states (such as the start screen and the game screen).
You could then make use of CardLayout or simply overlay the containers onto of each other when you want to switch between them.
Next, you should avoid using KeyListener, it's troublesome at the best of times. Instead, make us of the key bindings API, then you won't need to post another question about why KeyListener has stopped working.
Next, Swing is single threaded and not thread safe. This means you should not be performing blocking or long running operations within the context of the Event Dispatching Thread or updating the UI or a state the UI depends on from outside of it.
Take a look at Concurrency in Swing for more details.
The simple solution in this case is probably to use a Swing Timer, see How to use Swing Timers for more details.
I would, personally, make the "game" panel responsible for setting up the input and rendering management, but that's me.
I have a list of JPanels that I want to display as a "slideshow" where one JPanel fades out and the next JPanel in the list fades in. This is the code I am fiddling with:
public float opacity = 0f;
private Timer fadeTimer;
private boolean out;
public void fadeIn()
{
out = false;
beginFade();
}
public void fadeOut ()
{
out = true;
beginFade();
}
private void beginFade()
{
fadeTimer =
new javax.swing.Timer(75,this);
fadeTimer.setInitialDelay(0);
fadeTimer.start();
}
public void actionPerformed(ActionEvent e)
{
if (out)
{
opacity -= .03;
if(opacity < 0)
{
opacity = 0;
fadeTimer.stop();
fadeTimer = null;
}
}
else
{
opacity += .03;
if(opacity > 1)
{
opacity = 1;
fadeTimer.stop();
fadeTimer = null;
}
}
repaint();
}
public void paintComponent(Graphics g)
{
((Graphics2D) g).setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, opacity));
g.setColor(getBackground());
g.fillRect(0,0,getWidth(),getHeight());
}
The problem is that it fades some times, and other times it does not, and other times the transition is very laggy. What I would prefer is that there is a fraction of a second where the screen goes white, between when one JPanel fades out and the next JPanel fades in. Does anyone know how I can solve this? Thanks in advance.
So, when dealing with these types of problems, it's generally a better idea to reduce the number of Timers you have, as each timer will be posting multiple events to the Event Dispatching Queue (there own tick updates as well as repaint events). All this activity could reduce the performance of the system.
Animation is also the illusion of change over time, to this end, rather the trying to loop from a start point to an end point, you should decide how long you want the animation to run for and calculate the progress of time and update the values accordingly (this more of a "timeline" based animation cycle). This can help reduce the appearance of "lagging"
Normally I'd use the Timing Framework to accomplish this, but you could also have a look at the Trident framework or the Universal Tween Engine which also provide complex animation support for Swing.
This example is very tightly coupled to it's goal. Personally, I'd normally have a abstract concept of an "animatiable" object, which would probably just have the update(float) method, which would then be expanded to support other objects, but I'll leave that you to nut out.
Another issue is making sure that the component is fully transparent to begin with (setOpaque(false)), this allows us to fake the translucency of the component during the animation.
Normally, I'd always encourage you to override paintComponent, but there a few times when this is not adequate, this is one of them. Basically, in order to facilitate the transition from one component to the other, we need to control the alpha level of ALL the child components within the component, this is when overriding paint will be a better choice.
nb: The code is set to run at around 25fps, but the screen capture software captures at roughly 8fps
import java.awt.AlphaComposite;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class FadeTest {
public static void main(String[] args) {
new FadeTest();
}
public FadeTest() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
BufferedImage img1 = ImageIO.read(new File("C:\\Users\\shane\\Dropbox\\Ponies\\sillydash-small.png"));
BufferedImage img2 = ImageIO.read(new File("C:\\Users\\shane\\Dropbox\\Ponies\\SmallPony.png"));
AlphaPane pane1 = new AlphaPane();
pane1.add(new JLabel(new ImageIcon(img1)));
pane1.setAlpha(1f);
AlphaPane pane2 = new AlphaPane();
pane2.add(new JLabel(new ImageIcon(img2)));
pane2.setAlpha(0f);
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 1;
gbc.gridy = 1;
gbc.weightx = 1;
gbc.weighty = 1;
gbc.fill = GridBagConstraints.BOTH;
frame.add(pane1, gbc);
frame.add(pane2, gbc);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
MouseAdapter ma = new MouseAdapter() {
private AnimationController controller;
#Override
public void mouseClicked(MouseEvent e) {
try {
if (controller != null) {
controller.stop();
}
controller = new AnimationController(4000);
boolean fadeIn = pane1.getAlpha() < pane2.getAlpha();
controller.add(controller.new AlphaRange(pane1, fadeIn));
controller.add(controller.new AlphaRange(pane2, !fadeIn));
controller.start();
} catch (InvalidStateException ex) {
ex.printStackTrace();
}
}
};
pane1.addMouseListener(ma);
pane2.addMouseListener(ma);
} catch (IOException ex) {
ex.printStackTrace();
}
}
});
}
public class AnimationController {
private List<AlphaRange> animationRanges;
private Timer timer;
private Long startTime;
private long runTime;
public AnimationController(int runTime) {
this.runTime = runTime;
animationRanges = new ArrayList<>(25);
}
public void add(AlphaRange range) {
animationRanges.add(range);
}
public void start() throws InvalidStateException {
if (timer == null || !timer.isRunning()) {
timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (startTime == null) {
startTime = System.currentTimeMillis();
}
long duration = System.currentTimeMillis() - startTime;
float progress = (float) duration / (float) runTime;
if (progress > 1f) {
progress = 1f;
stop();
}
System.out.println(NumberFormat.getPercentInstance().format(progress));
for (AlphaRange range : animationRanges) {
range.update(progress);
}
}
});
timer.start();
} else {
throw new InvalidStateException("Animation is running");
}
}
public void stop() {
if (timer != null) {
timer.stop();
}
}
public class AlphaRange {
private float from;
private float to;
private AlphaPane alphaPane;
public AlphaRange(AlphaPane alphaPane, boolean fadeIn) {
this.from = alphaPane.getAlpha();
this.to = fadeIn ? 1f : 0f;
this.alphaPane = alphaPane;
}
public float getFrom() {
return from;
}
public float getTo() {
return to;
}
public float getValueBasedOnProgress(float progress) {
float value = 0;
float distance = to - from;
value = (distance * progress);
value += from;
return value;
}
public void update(float progress) {
float alpha = getValueBasedOnProgress(progress);
alphaPane.setAlpha(alpha);
}
}
}
public class InvalidStateException extends Exception {
public InvalidStateException(String message) {
super(message);
}
public InvalidStateException(String message, Throwable cause) {
super(message, cause);
}
}
public class AlphaPane extends JPanel {
private float alpha;
public AlphaPane() {
setOpaque(false);
}
#Override
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.setComposite(AlphaComposite.SrcOver.derive(alpha));
super.paint(g2d);
g2d.dispose();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// Fake the background
g.setColor(getBackground());
g.fillRect(0, 0, getWidth(), getHeight());
}
public void setAlpha(float value) {
if (alpha != value) {
this.alpha = Math.min(1f, Math.max(0, value));
repaint();
}
}
public float getAlpha() {
return alpha;
}
}
}
I would like to make my app draw moving image a little smoother than how it is currently drawing them. I am not sure what to do to do that.
This is what my main game Thread looks like:
#Override
public void run(){
int delay = 500; //milliseconds
ActionListener taskPerformer = new ActionListener(){
#Override
public void actionPerformed(ActionEvent evt){
Car car = new Car();
int speed = (int)(3 + Math.floor(Math.random() * (6 - 3)));
car.setSpeed(speed);
MainLoop.this.gameObjects.vehicles.add(car.create("/Media/Graphics/blueCar.png", width - 20, 78));
car.driveTo(0, 78);
}
};
new Timer(delay, taskPerformer).start();
try{
while(true){
this.repaint();
for(GameObject go : this.gameObjects.vehicles){
// loops through objects to move them
Vehicle vh = (Vehicle) go;
this.moveVehicle(vh);
if(vh.getX() <= vh.getDestX()){
vh.markForDeletion(true);
}
}
this.gameObjects.destroyVehicles();
Thread.sleep(1);
}
}catch(Exception e){
e.printStackTrace();
}
}
This is a method that calculates the items next x/y position
protected void moveVehicle(Vehicle vh){
int cx = vh.getX();
int dx = vh.getDestX();
int cy = vh.getY();
int dy = vh.getDestY();
// move along x axis
// getMaxSpeed() = Number between 3 and 6
if(cx > dx && vh.movingX() == -1){
vh.setX(cx - vh.getMaxSpeed());
}else if(cx < dx && vh.movingX() == 1){
vh.setX(cx + vh.getMaxSpeed());
}else{
vh.setX(dx);
}
// move along y axis
// getMaxSpeed() = Number between 3 and 6
if(cy > dy && vh.movingY() == -1){
vh.setY(cy - vh.getMaxSpeed());
}else if(cy < dy && vh.movingY() == 1){
vh.setY(cy + vh.getMaxSpeed());
}else{
vh.setY(dy);
}
}
This is my paint method:
#Override
public void paintComponent(Graphics graphics){
super.paintComponent(graphics);
Graphics2D g = (Graphics2D) graphics;
for(GameObject go : gameObjects.vehicles){
g.drawImage(go.getSprite(), go.getX(), go.getY(), this);
}
}
This is possibly more info than needed, but I would like to know, what should I do to make items move from left -> right top -> bottom as smoothly as possible, without much of a performance loss?
Edit: Requested sscce:
package sscce;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Sscce extends JPanel implements Runnable{
ArrayList<Square> squares = new ArrayList<>();
public Sscce(){
JFrame frame = new JFrame();
frame.setSize(500, 500);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.add(this);
Thread t = new Thread(this);
t.start();
}
public static void main(String[] args){
new Sscce();
}
#Override
public void run(){
int delay = 500; //milliseconds
ActionListener taskPerformer = new ActionListener(){
#Override
public void actionPerformed(ActionEvent evt){
Square squ = new Square();
Sscce.this.squares.add(squ);
squ.moveTo(0);
}
};
new Timer(delay, taskPerformer).start();
while(true){
try{
for(Square s : this.squares){
int objX = s.getX();
int desX = s.getDestX();
if(objX <= desX){
System.out.println("removing");
this.squares.remove(s);
}else{
s.setX(s.getX() - 10);
}
}
this.repaint();
Thread.sleep(30);
}catch(Exception e){
}
}
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
for(Square s : squares){
g.setColor(Color.blue);
g.fillRect(s.getX(), s.getY(), 50, 50);
}
}
}
class Square{
public int x = 0, y = 0, destX = 0;
public Square(){
this.x = 400;
this.y = 100;
}
public void moveTo(int destX){
this.destX = destX;
}
public int getX(){
return this.x;
}
public int getDestX(){
return this.destX;
}
public void setX(int x){
this.x = x;
}
public int getY(){
return this.y;
}
}
First note, for some reason, I had issues with the JPanel implementing Runnable when running it under MacOS - I have no idea why, but that's why I moved it out.
It is possible for your squares to be update while it is been used to paint which will cause an exception (also, removing elements from a list while you're iterating it isn't a good idea either ;))
Instead, I have two lists. I have a model, which the list can modify and then the paint list which the paint method can use. This allows the thread to modifiy the model, while a paint process is underway.
To prevent any clash, I've added in a lock, which prevents one thread from modifying/accessing the paint list, while another thread has it locked.
Now. Down to the real problem. The main issue you're having isn't the amount of time between the updates, but the distance you are moving. Reduce the distance (to make it slower) and standarise the updates.
Most people won't notice anything much over 25fps, so trying to do much more then that is just wasting CPU cycles and starving the repaint manager, preventing it from actually updating the screen.
It's a balancing act to be sure...
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestAnimation11 extends JPanel {
private ArrayList<Square> squares = new ArrayList<>();
private ReentrantLock lock;
public TestAnimation11() {
lock = new ReentrantLock();
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame();
frame.setSize(500, 500);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.add(TestAnimation11.this);
Thread t = new Thread(new UpdateEngine());
t.start();
}
});
}
public static void main(String[] args) {
new TestAnimation11();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Square[] paint = null;
lock.lock();
try {
paint = squares.toArray(new Square[squares.size()]);
} finally {
lock.unlock();
}
for (Square s : paint) {
g.setColor(Color.blue);
g.fillRect(s.getX(), s.getY(), 50, 50);
}
}
public class UpdateEngine implements Runnable {
private List<Square> model = new ArrayList<>(squares);
#Override
public void run() {
int ticks = 0;
List<Square> dispose = new ArrayList<>(25);
while (true) {
ticks++;
dispose.clear();
for (Square s : model) {
int objX = s.getX();
int desX = s.getDestX();
if (objX <= desX) {
dispose.add(s);
} else {
s.setX(s.getX() - 2);
}
}
model.removeAll(dispose);
if (ticks == 11) {
Square sqr = new Square();
sqr.moveTo(0);
model.add(sqr);
} else if (ticks >= 25) {
ticks = 0;
}
lock.lock();
try {
squares.clear();
squares.addAll(model);
} finally {
lock.unlock();
}
repaint();
try {
Thread.sleep(40);
} catch (Exception e) {
}
}
}
}
class Square {
public int x = 0, y = 0, destX = 0;
public Square() {
this.x = 400;
this.y = 100;
}
public void moveTo(int destX) {
this.destX = destX;
}
public int getX() {
return this.x;
}
public int getDestX() {
return this.destX;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return this.y;
}
}
}
What I'm trying to do is make it so when I run my application, it starts the thread and the image is shown for 3 seconds (3000ms), then the thread stops running.
The path for the image is correct, the image file exists, and the thread itself runs; however, the image doesn't seem to show. What could be wrong? Here is my code:
package org.main;
import java.awt.Graphics;
import java.awt.Image;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
public class Splasher extends JPanel implements Runnable {
private static final long serialVersionUID = 1L;
Image image;
ImageIcon splash = new ImageIcon("res/splash.png");
public static Thread DrawSplash = new Thread(new Splasher());
public Splasher() {
setFocusable(true);
image = splash.getImage();
repaint();
}
boolean hasRan = false;
public void run() {
try {
System.out.println("Drawing Splash Screen");
repaint();
Thread.sleep(3000);
System.out.println("Repainted");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void paint(Graphics g) {
g.drawImage(image, 0, 0, null);
}
public Image getImage() {
return image;
}
}
There's not enough to go from you question.
You don't show how you use the splash screen, if it's attached to anything or how you start/use the Thread.
So the problem could be anything...
In addition to everything else VishalK has already pointed out, I would add public static Thread DrawSplash = new Thread(new Splasher()) is a bad idea. You shouldn't use static Threads are threads are non-reentrant, that is, you can run the same thread twice.
This is a little example demonstrating a "fading" splash screen, using a number of Swing Timers
This assumes you're using Java 7, there is away to make it work for Java 6, I've not posted that code.
public class TestSplashScreen01 {
public static void main(String[] args) {
new TestSplashScreen01();
}
public TestSplashScreen01() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
SplashScreen splash = new SplashScreen();
splash.start();
}
});
}
public class SplashScreen extends JWindow {
private SplashPane splash;
public SplashScreen() {
setBackground(new Color(0, 0, 0, 0));
splash = new SplashPane();
add(splash);
pack();
setLocationRelativeTo(null);
}
public void start() {
splash.start();
}
public class SplashPane extends JPanel {
private BufferedImage splash;
private Timer timer;
private float alpha = 0f;
private int duration = 1000;
private long startTime = -1;
public SplashPane() {
try {
splash = ImageIO.read(getClass().getResource("/res/SokahsScreen.png"));
} catch (IOException ex) {
ex.printStackTrace();
}
timer = new Timer(3000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
fadeOut();
}
});
timer.setRepeats(false);
}
protected void fadeOut() {
Timer fadeInTimer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
long now = System.currentTimeMillis();
long runTime = now - startTime;
alpha = 1f - ((float) runTime / (float) duration);
if (alpha <= 0.01f) {
alpha = 0f;
((Timer) (e.getSource())).stop();
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
dispose();
}
});
}
repaint();
}
});
startTime = System.currentTimeMillis();
fadeInTimer.setRepeats(true);
fadeInTimer.setCoalesce(true);
fadeInTimer.start();
}
protected void fadeIn() {
Timer fadeInTimer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
long now = System.currentTimeMillis();
long runTime = now - startTime;
alpha = (float) runTime / (float) duration;
if (alpha >= 1f) {
alpha = 1f;
((Timer) (e.getSource())).stop();
timer.start();
}
repaint();
}
});
startTime = System.currentTimeMillis();
fadeInTimer.setRepeats(true);
fadeInTimer.setCoalesce(true);
fadeInTimer.start();
}
public void start() {
if (!SplashScreen.this.isVisible()) {
alpha = 0f;
SplashScreen.this.setVisible(true);
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
fadeIn();
}
});
}
}
#Override
public Dimension getPreferredSize() {
return splash == null ? super.getPreferredSize() : new Dimension(splash.getWidth(), splash.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (splash != null) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.setComposite(AlphaComposite.SrcOver.derive(alpha));
int x = (getWidth() - splash.getWidth()) / 2;
int y = (getHeight() - splash.getHeight()) / 2;
g2d.drawImage(splash, x, y, this);
g2d.dispose();
}
}
}
}
}
NB:
The problem with this example is once you call start, the program will continue to execute, this will require some kind of listener to tell interested parties when the splash screen has completed.
Alternatively, you could use a undecorated modal JDialog
Many mistakes that you have done in your code...
You have overriden paint method instead of paintComponent to draw Image.
You have used Thread for drawing and removing image on Swing component (JPanel). You should use javax.swing.Timer instead.
Although you should use javax.swing.Timer , but still the basic mistake that you did is that You have created a static Thread and within that passed a new object of Splasher instead of current object.
Each Time you want to make changes in the Graphics of a Component You should call repaint explicitly. For example, If you want to make the image to disappear after 3 seconds you should call repaint method after 3 seconds laps , and should have the proper logic written inside paintComponent method to remove that image.
Here is the modified version of your code , which is doing exactly what you are looking for.Have a look on it.:
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentAdapter;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
import javax.swing.JFrame;
import javax.swing.Timer;
import javax.swing.SwingUtilities;
public class Splasher extends JPanel {
private static final long serialVersionUID = 1L;
Image image;
ImageIcon splash = new ImageIcon("apple.png");
MyComponentListener componentListener ;
Timer timer ;
public Splasher()
{
componentListener = new MyComponentListener();
setFocusable(true);
image = splash.getImage();
timer = new Timer(3000, new LoadAction());
addComponentListener(componentListener);
}
boolean hasRan = false;
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
if (image == null && timer !=null )
{
g.clearRect(0, 0, getWidth(), getHeight()) ;
timer.stop();
removeComponentListener(componentListener);
}
else
{
g.drawImage(image, 0, 0, null);
}
}
public Image getImage()
{
return image;
}
private class MyComponentListener extends ComponentAdapter
{
#Override
public void componentResized(ComponentEvent evt)
{
System.out.println("Resized..");
timer.start();
}
}
private class LoadAction implements ActionListener
{
public void actionPerformed(ActionEvent evt)
{
System.out.println("Drawing Splash Screen");
repaint();
image = null;
repaint();
System.out.println("Repainted");
}
}
public static void main(String st[])
{
SwingUtilities.invokeLater ( new Runnable()
{
#Override
public void run()
{
JFrame frame = new JFrame("Splash:");
frame.getContentPane().add(new Splasher());
frame.setSize(300,500);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
});
}
}
Also Watch the following tutorial for Paint mechanism in java. http://docs.oracle.com/javase/tutorial/uiswing/painting/closer.html
I've written this simple program which displays key presses, draws the phrase "Hello World," where one's cursor moves (with a trail mode when clicked) and also cycles through the Colors in which "Hello World," is picked out when the mouse wheel is scrolled. However there is a problem with this: When the mouse wheel scrolls the entire window goes blank (showing the default grey color from when you first make a component visible) and will then be redrawn with the color change (a very small change only to the "Hello World," which doesn't seem to require that the whole frame be redrawn.
The time for which the blankness occurs seems to correlate with the force with which the mouse wheel is scrolled, if I scroll very lightly there is only a very tiny moment where everything is not displayed, however scrolling very hard can make the window go blank for 2-3 seconds.
I've tried double buffering - thinking that this might be some sort of screen flicker - but it has made no change and I'm at a loss as to what could be causing this weird effect. It's as if the frame image is loading while the Wheel motion event is happening. (Is there perhaps a way to exit from the wheel event immediately so as to reduce loading time? (This is just my guesses as to possible solutions)).
The code is below. Any ideas would be greatly appreciated.
package keymouse;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferStrategy;
import java.util.LinkedList;
import javax.swing.JFrame;
public class KeyMouse implements KeyListener,
MouseMotionListener, MouseListener, MouseWheelListener, Runnable {
boolean trailMode = false;
boolean exists = false;
display window;
LinkedList wordList;
LinkedList trailList;
LinkedList colorList;
Point mousePoint;
int TRAIL_SIZE = 10;
boolean init = true;
int FONT_SIZE = 32;
int mouseY;
int mouseX;
int y;
int colorCount = 0;
public static void main(String[] args) {
KeyMouse k = new KeyMouse();
k.run();
}
public KeyMouse() {
window = new display();
window.addKeyListener(this);
window.addMouseMotionListener(this);
window.addMouseListener(this);
window.addMouseWheelListener(this);
window.setBackground(Color.WHITE);
window.setForeground(Color.BLACK);
wordList = new LinkedList();
trailList = new LinkedList();
colorList = new LinkedList();
colorList.add(Color.BLACK);
colorList.add(Color.BLUE);
colorList.add(Color.YELLOW);
colorList.add(Color.GREEN);
colorList.add(Color.PINK);
}
#Override
public void keyTyped(KeyEvent e) {
// do nothing
}
#Override
public void keyPressed(KeyEvent e) {
int keyCode;
if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
stop();
}
keyCode = e.getKeyCode();
addMessage("Pressed:" + e.getKeyText(keyCode));
}
#Override
public void keyReleased(KeyEvent e) {
//do nothing
}
#Override
public void mouseDragged(MouseEvent e) {
Point p = new Point(e.getX(), e.getY());
addLocation(p);
}
#Override
public void mouseMoved(MouseEvent e) {
Point p = new Point(e.getX(), e.getY());
addLocation(p);
}
#Override
public void mouseClicked(MouseEvent e) {
}
#Override
public void mousePressed(MouseEvent e) {
trailMode = true;
}
#Override
public void mouseReleased(MouseEvent e) {
trailMode = false;
}
#Override
public void mouseEntered(MouseEvent e) {
//do nothing
}
#Override
public void mouseExited(MouseEvent e) {
//do nothing
}
#Override
public void mouseWheelMoved(MouseWheelEvent e) {
System.out.println(e.getWheelRotation());
colorCount++;
if (colorCount > 4) {
colorCount = 0;
}
window.setForeground((Color) colorList.get(colorCount));
}
#Override
public void run() {
window.createBufferStrategy(2);
BufferStrategy strategy = window.getBufferStrategy();
while (true) {
draw(strategy.getDrawGraphics());
strategy.show();
try {
Thread.sleep(20);
} catch (Exception ex) {
}
}
}
public void draw(Graphics g) {
//draw background
g.setColor(window.getBackground());
g.fillRect(0, 0, window.getWidth(), window.getHeight());
//draw Text
g.setColor(window.getForeground());
g.setFont(new Font("sansserif", Font.BOLD, 32));
int count = trailList.size();
if (trailList.size() > 1 && trailMode == false) {
count = 1;
}
if (exists == true) {
for (int i = 0; i < count; i++) {
Point p = (Point) trailList.get(i);
g.drawString("Hello World", p.x, p.y);
}
}
g.setColor(Color.BLACK);
y = 56;
for (int i = 0; i < wordList.size(); i++) {
String word = (String) wordList.get(i);
g.drawString((String) wordList.get(i), 100, y);
y += 32;
}
}
public void addMessage(String message) {
if (y >= window.getHeight()) {
wordList.remove(0);
}
wordList.add(message);
}
public void addLocation(Point h) {
exists = true;
trailList.addFirst(h);
if (trailList.size() > TRAIL_SIZE) {
trailList.removeLast();
}
}
public void printMessages() {
for (int i = 0; i < wordList.size(); i++) {
System.out.println(wordList.get(i));
}
}
private void stop() {
System.exit(0);
}
Absent a complete example, I can't reproduce the effect you describe. You might compare your code to this example, which exhibits no apparent blanking.
In general,
JPanel is double buffered by default; it's unusual to need a different buffer strategy.
AnimationTest illustrates a Swing Timer and how to display your average paint period.
MouseAdapter is convenient for overriding a small number of methods.
When possible, use generic parameters for type safety.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseWheelEvent;
import java.util.LinkedList;
import java.util.Queue;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
/**
* #see https://stackoverflow.com/a/10970892/230513
*/
public class ColorWheel extends JPanel {
private static final int N = 32;
private final Queue<Color> clut = new LinkedList<Color>();
private final JLabel label = new JLabel();
public ColorWheel() {
for (int i = 0; i < N; i++) {
clut.add(Color.getHSBColor((float) i / N, 1, 1));
}
this.setBackground(clut.peek());
label.setText(getBackground().toString());
this.add(label);
this.addMouseWheelListener(new MouseAdapter() {
#Override
public void mouseWheelMoved(MouseWheelEvent e) {
setBackground(clut.peek());
label.setText(getBackground().toString());
clut.add(clut.remove());
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(320, 240);
}
private void display() {
JFrame f = new JFrame("ColorWheel");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(this);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new ColorWheel().display();
}
});
}
}