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
Related
I started remaking my graphics engine and i have a problem with repainting the VolatileImage on the JFrame, i added an FPS counter and when it is in full-screen it only gets to around 250 or 280 FPS even though i have the loop in a new thread and I'm only drawing 2 dots on the screen.
Here is the code for the init and render functions in the Window class, might be a bit unorganized:
`
public int render() {
frame.getGraphics().drawImage(vImg, 0, 0, frame.getWidth(), frame.getHeight(), null);
totalFrames++;
if (System.nanoTime() > lastFPScheck + 1000000000) {
lastFPScheck = System.nanoTime();
currentFPS = totalFrames;
totalFrames = 0;
}
if (vImg.validate(gc) == VolatileImage.IMAGE_OK) {
vImg = gc.createCompatibleVolatileImage(frame.getWidth(), frame.getHeight());
}
if (debugging()) {
frame.setTitle(title + "[FPS: " + currentFPS + "]");
}
graphics = (Graphics2D)vImg.getGraphics();
graphics.setColor(bg_color);
graphics.fillRect(0, 0, frame.getWidth(), frame.getHeight());
return currentFPS;
}
public void repaint_buffer() {
frame.getContentPane().getGraphics().drawImage(vImg, 0, 0, canvasWidth, canvasHeight, null);
}
public void init(int width,
int height,
String title,
Color bg_color,
Consumer<Void> onDestroy) {
this.canvasWidth = width;
this.canvasHeight = height;
this.is_open = true;
this.title = title;
this.bg_color = bg_color;
this.frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
onDestroy.accept(null);
}
});
this.frame.addWindowListener(new WindowAdapter() {
public void windowResized(WindowEvent e) {
vImg = gc.createCompatibleVolatileImage(frame.getWidth(), frame.getHeight());
}
});
this.frame.setLayout(null);
this.frame.setSize(canvasWidth, canvasHeight);
this.frame.addKeyListener(keyInput);
this.frame.setResizable(true);
this.frame.setLocation(-7, 0);
this.frame.setTitle(title);
this.frame.setVisible(true);
gc = this.frame.getGraphicsConfiguration();
vImg = gc.createCompatibleVolatileImage(this.frame.getWidth(), this.frame.getHeight());
this.graphics = (Graphics2D) vImg.getGraphics();
}
`
and here is the code from the Main class, the GraphicsManager object only contains the set_pixel function which sets the color and draws a rectangle at the given position with a size of 1x1 pixels:
package obsidian.core;
import obsidian.core.Window;
import java.awt.*;
public class Main {
private static void on_destroy() {
System.exit(0);
}
public static void main(String[] args) {
Window window = new Window();
window.init(Window.get_max_size().width, Window.get_max_size().height, "title", new Color(0, 0, 0), destroy -> on_destroy());
GraphicsManager gm = new GraphicsManager(window);
Thread t = new Thread(new Runnable() {
#Override
public void run() {
while(window.is_open()) {
window.render();
gm.draw_pixel(0.5, 0.5, new Color(255, 0, 0));
gm.draw_pixel(0, 0, new Color(255, 255, 255));
System.out.println(window.get_current_fps());
}
}
});
t.start();
}
}
I tried removing the VolatileImage and directly drawing on the frame but the filling of the image makes it glitchy but it got to about 2000 FPS and IntelliJ IDEA couldn't keep up. The only solution I could find to make it run faster is removing the VolatileImage and draw directly to the frame. Didn't work ;-;
Can anyone help me with this? Thank you
Swing "passive" painting
This makes use of a Swing Timer
~170fps
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.time.Duration;
import java.time.Instant;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private Timer timer;
private Instant lastTick;
private int fps = 0;
public TestPane() {
}
#Override
public void addNotify() {
super.addNotify();
if (timer != null) {
timer.stop();
}
timer = new Timer(5, new ActionListener() {
private int frameCount = 0;
#Override
public void actionPerformed(ActionEvent e) {
if (lastTick != null) {
Duration duration = Duration.between(lastTick, Instant.now());
if (duration.toMillis() >= 1000) {
lastTick = Instant.now();
fps = frameCount;
frameCount = 0;
} else {
frameCount++;
}
} else {
lastTick = Instant.now();
}
repaint();
}
});
timer.start();
}
#Override
public void removeNotify() {
if (timer != null) {
timer.stop();
}
timer = null;
super.removeNotify();
}
#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(20, 20, 20, 20);
g2d.fillOval(40, 40, 20, 20);
FontMetrics fm = g2d.getFontMetrics();
String text = Integer.toString(fps);
g2d.drawString(text, 10, fm.getAscent());
g2d.dispose();
}
}
}
"Active" painting via a BufferStrategy
~680fps
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Frame;
import java.awt.Graphics2D;
import java.awt.image.BufferStrategy;
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.atomic.AtomicBoolean;
public class Other {
public static void main(String[] args) {
new Other();
}
public Other() {
Frame frame = new Frame();
MainCanvas canvas = new MainCanvas();
frame.setBackground(Color.BLUE);
frame.add(canvas);
frame.setLocationRelativeTo(null);
frame.pack();
frame.setVisible(true);
}
public class MainCanvas extends Canvas {
private Thread thread;
private AtomicBoolean running = new AtomicBoolean(false);
private int fps;
public MainCanvas() {
}
#Override
public void addNotify() {
super.addNotify();
createBufferStrategy(3);
start();
}
#Override
public void removeNotify() {
stop();
super.removeNotify();
}
public void start() {
stop();
run();
}
protected void stop() {
if (running.get()) {
running.set(false);
}
thread = null;
}
protected void run() {
stop();
running.set(true);
thread = new Thread(new Runnable() {
private Instant lastTick;
private int frameCount;
#Override
public void run() {
while (running.get()) {
if (lastTick != null) {
Duration duration = Duration.between(lastTick, Instant.now());
if (duration.toMillis() >= 1000) {
lastTick = Instant.now();
fps = frameCount;
frameCount = 0;
} else {
frameCount++;
}
} else {
lastTick = Instant.now();
}
render();
// Comment out these lines and see where it takes you
try {
Thread.sleep(1);
} catch (InterruptedException ex) {
java.util.logging.Logger.getLogger(Other.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
}
}
});
thread.start();
}
protected void render() {
BufferStrategy bs = getBufferStrategy();
while (bs == null) {
bs = getBufferStrategy();
}
do {
// The following loop ensures that the contents of the drawing buffer
// are consistent in case the underlying surface was recreated
do {
// Get a new graphics context every time through the loop
// to make sure the strategy is validated
Graphics2D g2d = (Graphics2D) bs.getDrawGraphics();
g2d.setColor(Color.GREEN);
g2d.fillRect(0, 0, getWidth(), getHeight());
// Render to graphics
// ...
g2d.setColor(Color.RED);
g2d.fillOval(20, 20, 20, 20);
g2d.fillOval(40, 40, 20, 20);
FontMetrics fm = g2d.getFontMetrics();
String text = Integer.toString(fps);
g2d.drawString(text, 10, fm.getAscent());
// Dispose the graphics
g2d.dispose();
// Repeat the rendering if the drawing buffer contents
// were restored
} while (bs.contentsRestored());
// Display the buffer
bs.show();
// Repeat the rendering if the drawing buffer was lost
} while (bs.contentsLost());
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
}
}
Disclaimer
This is by far a "scientific" example and is intended simply as a demonstration between passive and active rendering workflows
i have an application containing a jframe, this jframe then adds a jpanel which constains an image. the jpanel is displayed for a given time, then removed from the jframe and another jpanel is added.
I want to fade in and out between the images, and ive done this using a timer
private void fadeOut() {
ActionListener fadeOutAc = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
opacity += 10;
if (opacity >= 255) {
opacity = 255;
fadeOutT.stop();
}
repaint();
}
};
fadeOutT = new Timer(20, fadeOutAc);
fadeOutT.start();
}
private void fadeIn() {
ActionListener fadeInAc = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
opacity -= 10;
if (opacity <= 0) {
opacity = 0;
fadeInT.stop();
}
repaint();
}
};
fadeInT = new Timer(10, fadeInAc);
fadeInT.setInitialDelay(200);
fadeInT.start();
}
public void paint(Graphics g) {
super.paintComponent(g);
g.setColor(new Color(picColor.getRed(), picColor.getGreen(), picColor.getBlue(), opacity));
g.fillRect(0, 0, presWin.getWidth(), presWin.getHeight());
}
i recently moved the fading in/out from the jpanel to the jframe instead. The problem is, that in the jpanel, the repaint only had to draw an image, now it has to repaint the entire jpanel each time. Is there a way to call repaint without having the paint the components, only the rectangel?
To me, it seems a bit silly to put the functionality in the JFrame when what you seem to want is a container which can fade it's content in and out. This way you can isolate the responsibility to a single container/class which can be placed or used in what ever way you want in isolation to the rest of the UI.
Basically, this example uses a FadingPane (based on a JPanel) to control the fading process, but onto which I place JLabel which holds the actual images.
Fading is controlled through the use of a AlphaComposite, meaning that this panel will actually physically fade in and out, not just change fill color ;)
There is also a FadingListener which provides additional notifications about the fading process, really only interested in fadeOutDidComplete, so you can switch the images and fade the panel back in, but you never know...
import java.awt.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
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 Test {
public static void main(String[] args) {
new Test();
}
public Test() {
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 TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JLabel label;
private FadingPane fadingPane;
private File[] pictures;
private int index;
public TestPane() {
// Just for show
setBackground(Color.RED);
fadingPane = new FadingPane(new FadeListener() {
#Override
public void fadeDidStart(FadingPane panel) {
}
#Override
public void fadeDidStop(FadingPane panel) {
}
#Override
public void fadeOutDidComplete(FadingPane panel) {
nextPicture();
fadingPane.fadeIn();
}
#Override
public void fadeInDidComplete(FadingPane panel) {
}
});
setLayout(new BorderLayout());
fadingPane.setLayout(new BorderLayout());
label = new JLabel();
fadingPane.add(label);
add(fadingPane);
JButton next = new JButton("Next");
add(next, BorderLayout.SOUTH);
next.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
fadingPane.fadeOut();
}
});
pictures = new File("/Volumes/Disk02/Dropbox/MegaTokyo/thumnails").listFiles(new FileFilter() {
#Override
public boolean accept(File pathname) {
String name = pathname.getName().toLowerCase();
return name.endsWith(".jpg") || name.endsWith(".png");
}
});
nextPicture();
}
protected void nextPicture() {
index++;
if (index >= pictures.length) {
index = 0;
}
try {
BufferedImage img = ImageIO.read(pictures[index]);
label.setIcon(new ImageIcon(img));
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
public interface FadeListener {
public void fadeDidStart(FadingPane panel);
public void fadeDidStop(FadingPane panel);
public void fadeOutDidComplete(FadingPane panel);
public void fadeInDidComplete(FadingPane panel);
}
public class FadingPane extends JPanel {
private float delta;
private float alpha = 1f;
private Timer timer;
private FadeListener fadeListener;
public FadingPane(FadeListener fadeListener) {
this.fadeListener = fadeListener;
// This is important, as we may not always be opaque
// and we don't want to stuff up the painting process
setOpaque(false);
timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
float alpha = getAlpha() + delta;
if (alpha < 0.001f) {
alpha = 0f;
timer.stop();
fadeListener.fadeOutDidComplete(FadingPane.this);
} else if (alpha >= 1.0f) {
alpha = 1.0f;
timer.stop();
fadeListener.fadeInDidComplete(FadingPane.this);
}
setAlpha(alpha);
}
});
}
public float getAlpha() {
return alpha;
}
public void setAlpha(float value) {
if (alpha != value) {
this.alpha = Math.min(1.0f, Math.max(0.0f, value));
repaint();
}
}
#Override
public void paint(Graphics g) {
// I don't normally recomamned overriding paint, but in this case,
// I want to affect EVERYTHING that might be added to this panel
Graphics2D g2d = (Graphics2D) g.create();
g2d.setComposite(AlphaComposite.SrcOver.derive(getAlpha()));
super.paint(g2d);
g2d.dispose();
}
public void fadeIn() {
timer.stop();
fadeListener.fadeDidStop(FadingPane.this);
delta = 0.05f;
timer.restart();
fadeListener.fadeDidStart(FadingPane.this);
}
public void fadeOut() {
timer.stop();
fadeListener.fadeDidStop(FadingPane.this);
delta = -0.05f;
timer.restart();
fadeListener.fadeDidStart(FadingPane.this);
}
}
}
Thats totaly normal, moving your function to the JFrame and calling repaint function would actualy call repaint of your JFrame.
I think the best solution would be to pass panel as an argument to your fadeIn and fadeOut function and call its repaint methode for example fadeIn :
private void fadeIn(JPanel panelParam) {
ActionListener fadeInAc = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
opacity -= 10;
if (opacity <= 0) {
opacity = 0;
fadeInT.stop();
}
panelParam.repaint(); // here call repaint of the panel.
}
};
fadeInT = new Timer(10, fadeInAc);
fadeInT.setInitialDelay(200);
fadeInT.start();
}
With that you can apply your effect on any other panel.
Hope it helped.
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?
I'd like to do JButton with nice transition effect. I write a class which extend by JButton and add to it custom MouseAdapter. It almost works, but if opacity should have 0 my one BufferedImage don't vanish.
Here my all source code:
public class ImageHoverButton extends JButton {
public class MouseListener extends MouseAdapter
{
public void mouseExited(MouseEvent me)
{
new Thread(new Runnable()
{
public void run()
{
for (float i = 1f; i >= 0f; i -= .03f)
{
setOpacity(i);
try
{
Thread.sleep(10);
}
catch (Exception e)
{
}
}
}
}).start();
}
public void mouseEntered(MouseEvent me)
{
new Thread(new Runnable()
{
public void run()
{
for (float i = 0f; i <= 1f; i += .03f)
{
setOpacity(i);
try
{
Thread.sleep(10);
}
catch (Exception e)
{
}
}
}
}).start();
}
public void mousePressed(MouseEvent me)
{
new Thread(new Runnable()
{
public void run()
{
for (float i = 1f; i >= 0.6f; i -= .1f)
{
setOpacity(i);
try
{
Thread.sleep(1);
}
catch (Exception e)
{
}
}
}
}).start();
}
}
private static final long serialVersionUID = 1L;
private BufferedImage imgBottom;
private BufferedImage imgHover;
private BufferedImage imgHoverRGB;
// filter to imgInActive
float[] scales = { 1f, 1f, 1f, 0f};
float[] offsets = new float[4];
RescaleOp rop = new RescaleOp(scales, offsets, null);
/**
* Constructor for image path
* #param img
* #param x
* #param y
*/
public ImageHoverButton(String imgBottomPath, String imgHoverPath, int x, int y) {
try {
this.imgBottom = ImageIO.read(new File(imgBottomPath));
this.imgHover = ImageIO.read(new File(imgHoverPath));
imgHoverRGB = new BufferedImage(imgHover.getWidth(null),
imgHover.getHeight(null),
BufferedImage.TYPE_INT_ARGB);
Graphics g = imgHoverRGB.getGraphics();
g.drawImage(imgHover, 0, 0, null);
} catch (IOException e) {
}
this.setBounds(x, y, imgBottom.getWidth() + 40 , imgBottom.getHeight() + 50);
addMouseListener(new MouseListener());
setOpacity(0f);
setOpaque(false);
setBorderPainted(false);
setRolloverEnabled(false);
setCursor(new Cursor(Cursor.HAND_CURSOR));
setLayout(null);
}
public void setOpacity(float opacity) {
scales[3] = opacity;
rop = new RescaleOp(scales, offsets, null);
repaint();
}
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D)g;
g2d.drawImage(imgBottom, 50, 50, null);
g2d.drawImage(imgHoverRGB, rop, 0, 0);
}
}
Have any idea how to improve this?
I'm not so familiar with RescaleOp, and can't remember having used this before. But it seems like the results of applying it in this case are somewhat unexpected.
As an alternative, you might consider an AlphaComposite. The minimum modification that is necessary to achieve the desired effect would then be to change the line
g2d.drawImage(imgHoverRGB, rop, 0, 0);
to
g2d.setComposite(AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, scales[3]));
g2d.drawImage(imgHoverRGB, 0, 0, null);
However, there are several other issues with the code:
don't override paint. Instead, override paintComponent
don't call setBounds on a component (particlularly not in a constructor). The placement should be done by a layout manager
don't swallow Exceptions silently
don't load the images in the constructor of the button
implement getPreferredSize properly
don't spawn hundreds of threads due to mouse movement. (When you quickly move the mouse in and out, you'll have several threads running - some of them increasing the opacity, and some of them decreasing the opacity)
I created an example showing one possible approach: It contains an OpacityAnimator that allows a transition between two opacities, with a predefined delay in milliseconds. This animator is used to increase the opacity of the foreground image when the button is hovered with the mouse, and to decrease it when the mouse leaves the button.
(Note that this could be generalized further, and there are many possible "configuration settings" (like the transition delay) that could be exposed, but this is just intended as an example)
import java.awt.AlphaComposite;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
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 javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class HoverButtonTest
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
try
{
createAndShowGUI();
}
catch (IOException e)
{
e.printStackTrace();
}
}
});
}
private static void createAndShowGUI() throws IOException
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
BufferedImage backgroundImage = loadImage("background.png");
BufferedImage foregroundImage = loadImage("foreground.png");
f.getContentPane().setLayout(new FlowLayout());
f.getContentPane().add(
new ImageHoverButton(backgroundImage, foregroundImage));
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
private static BufferedImage loadImage(String path) throws IOException
{
return convertToARGB(ImageIO.read(new File(path)));
}
public static BufferedImage convertToARGB(BufferedImage image)
{
BufferedImage newImage = new BufferedImage(image.getWidth(),
image.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g = newImage.createGraphics();
g.drawImage(image, 0, 0, null);
g.dispose();
return newImage;
}
}
class ImageHoverButton extends JButton
{
private class MouseHoverListener extends MouseAdapter
{
#Override
public void mouseExited(MouseEvent me)
{
opacityAnimator.changeOpacity(0.0f, 250);
}
#Override
public void mouseEntered(MouseEvent me)
{
opacityAnimator.changeOpacity(1.0f, 1000);
}
#Override
public void mousePressed(MouseEvent me)
{
opacityAnimator.changeOpacity(0.5f, 50);
}
}
private class OpacityAnimator
{
private final int DELAY_MS = 10;
private final Timer timer;
private float targetOpacity;
private float currentOpacity;
private float opacityStep;
OpacityAnimator()
{
timer = new Timer(DELAY_MS, new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
if (currentOpacity > targetOpacity)
{
currentOpacity += opacityStep;
currentOpacity = Math.max(
currentOpacity, targetOpacity);
}
else if (currentOpacity < targetOpacity)
{
currentOpacity += opacityStep;
currentOpacity = Math.min(
currentOpacity, targetOpacity);
}
if (currentOpacity == targetOpacity)
{
timer.stop();
}
setOpacity(currentOpacity);
}
});
}
void changeOpacity(float targetOpacity, int durationMs)
{
timer.stop();
this.targetOpacity = targetOpacity;
float delta = targetOpacity - currentOpacity;
if (durationMs > 0)
{
opacityStep = (delta / durationMs) * DELAY_MS;
}
else
{
opacityStep = delta;
}
timer.start();
}
}
private final OpacityAnimator opacityAnimator;
private final BufferedImage backgroundImage;
private final BufferedImage foregroundImage;
private float opacity = 0.0f;
public ImageHoverButton(BufferedImage backgroundImage,
BufferedImage foregroundImage)
{
this.backgroundImage = backgroundImage;
this.foregroundImage = foregroundImage;
this.opacityAnimator = new OpacityAnimator();
addMouseListener(new MouseHoverListener());
setOpaque(false);
setBorderPainted(false);
setRolloverEnabled(false);
setCursor(new Cursor(Cursor.HAND_CURSOR));
}
#Override
public Dimension getPreferredSize()
{
if (super.isPreferredSizeSet())
{
return super.getPreferredSize();
}
int w = Math
.max(backgroundImage.getWidth(), foregroundImage.getWidth());
int h = Math.max(backgroundImage.getHeight(),
foregroundImage.getHeight());
return new Dimension(w, h);
}
public void setOpacity(float opacity)
{
this.opacity = opacity;
repaint();
}
#Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D) gr;
g.drawImage(backgroundImage, 0, 0, null);
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
opacity));
g.drawImage(foregroundImage, 0, 0, null);
}
}
Don't access Swing components from other threads. Use a swing Timer instead.
See How to use swing timers
I can make a single JLabel fade in, but how do I make multiple labels fade in one after another? Here is my current code.
public void fadeIn(final JLabel jLabel) {
jLabelList.add(jLabel);
jLabelBackPanel.add(jLabelList.get(jLabelList.size() - 1));
new SwingWorker<Void, Void>() {
#Override
protected Void doInBackground() throws Exception {
int c = 0;
for(int i = 0; i < 255; i++) {
Thread.sleep(1000);
jLabel.setForeground(new Color(
jLabel.getForeground().getRed(),
jLabel.getForeground().getGreen(),
jLabel.getForeground().getBlue(),
c++));
}
return null;
}
}.execute();
}
The above code will fade in about 9 labels at one time, then 9 more, etc. I can't figure out how to make one label wait until the last one is done fading in.
Now, there's probably a few ways this might be done, but this is basically what I've come up with...
First, I created a custom label, which uses a javax.swing.Timer to progress from a starting alpha value to a target alpha value (ie 0-1 to fade in).
To this label, I added a simply waitFor method, which waits until the target value has been reached. This is achieved through a simple object monitor. Very important, NEVER call this method while you're on the Event Dispatching Thread...
Next, I created a series of these labels with the text I wanted displayed and added each one to to the output.
I then started a separate Thread and iterated the list, fading each label in and using waitFor to wait for it to be displayed...
import java.awt.AlphaComposite;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.Icon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class FadingLabels {
public static void main(String[] args) {
new FadingLabels();
}
public FadingLabels() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
final List<FadingLabel> labels = new ArrayList<>(25);
labels.add(new FadingLabel("A "));
labels.add(new FadingLabel("long "));
labels.add(new FadingLabel("time "));
labels.add(new FadingLabel("ago "));
labels.add(new FadingLabel("in "));
labels.add(new FadingLabel("a "));
labels.add(new FadingLabel("galaxy "));
labels.add(new FadingLabel("far, "));
labels.add(new FadingLabel("far, "));
labels.add(new FadingLabel("away"));
labels.add(new FadingLabel("..."));
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridBagLayout());
for (FadingLabel label : labels) {
frame.add(label);
}
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
new Thread(new Runnable() {
#Override
public void run() {
for (FadingLabel label : labels) {
label.fadeIn();
label.waitFor();
}
}
}).start();
}
});
}
public class FadingLabel extends JLabel {
protected static final int TIME = 1000;
protected final Object fadeLock = new Object();
private float targetAlpha;
private float alpha = 0;
private Timer timer;
private long startTime;
private float fromAlpha;
public FadingLabel() {
init();
}
public FadingLabel(String text, Icon icon, int horizontalAlignment) {
super(text, icon, horizontalAlignment);
init();
}
public FadingLabel(String text, int horizontalAlignment) {
super(text, horizontalAlignment);
init();
}
public FadingLabel(String text) {
super(text);
init();
}
public FadingLabel(Icon image, int horizontalAlignment) {
super(image, horizontalAlignment);
init();
}
public FadingLabel(Icon image) {
super(image);
init();
}
protected void init() {
timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (alpha < 1f) {
long now = System.currentTimeMillis();
long diff = now - startTime;
float progress = (float) diff / (float) TIME;
float distance = targetAlpha - fromAlpha;
alpha = (float) (distance * progress);
alpha += fromAlpha;
if (alpha > 1f) {
timer.stop();
alpha = 1f;
}
} else {
alpha = 1f;
timer.stop();
}
repaint();
if (!timer.isRunning()) {
synchronized (fadeLock) {
fadeLock.notifyAll();
}
}
}
});
timer.setInitialDelay(0);
}
protected void fadeTo(float target) {
Runnable run = new Runnable() {
#Override
public void run() {
timer.stop();
fromAlpha = alpha;
targetAlpha = target;
if (targetAlpha != alpha) {
startTime = System.currentTimeMillis();
timer.start();
} else {
repaint();
}
}
};
if (EventQueue.isDispatchThread()) {
run.run();
} else {
EventQueue.invokeLater(run);
}
}
public void fadeIn() {
fadeTo(1f);
}
public void fadeOut() {
fadeTo(0f);
}
public void waitFor() {
if (EventQueue.isDispatchThread()) {
throw new IllegalStateException("Calling waitFor while within the EDT!");
}
synchronized (fadeLock) {
try {
fadeLock.wait();
} catch (InterruptedException ex) {
}
}
}
#Override
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.setComposite(AlphaComposite.SrcOver.derive(alpha));
super.paint(g2d);
g2d.dispose();
}
}
}
The animation portion uses a fixed time progression, rather then a fixed delta. This allows the animation to be more variable depending on the over heads that might be occurred from the OS. Basically what this means is that the animation will progress over a fixed period of time each time