JPanel getWidth() returns Zero, after invoking pack() - java

I believe my code will describe what I wrote, But the problem is, is that When I run it, I get that infamous "Exception in thread "Animator" java.lang.IllegalArgumentException: Width (0) and height (0) cannot be <= 0" Error. I have searched for answers but have yet to find why this is happening.
package com.shparki.TowerDefense;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Toolkit;
import javax.swing.JPanel;
public class GamePanel extends JPanel implements Runnable{
//==================== PROGRAM VARIABLES ====================//
private static final int WIDTH = 300;
private static final int HEIGHT = WIDTH / 16 * 9;
private static final int SCALE = 4;
private static final int TARGET_FPS = 45;
private static final int PERIOD = 1000 / TARGET_FPS;
private boolean debug = true;
private volatile boolean running = false;
private volatile boolean gameOver = false;
private volatile boolean paused = false;
private Thread animator;
private TowerDefense frame;
private double currentFPS;
//==================== PROGRAM METHODS ====================//
public GamePanel(TowerDefense td){
frame = td;
setMinimumSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
setMaximumSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
setPreferredSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
setVisible(true);
}
public void addNotify(){
super.addNotify();
startGame();
}
public void startGame(){
if (!running && animator == null){
animator = new Thread(this, "Animator");
animator.start();
}
}
public void stopGame(){
running = false;
}
public void run(){
running = true;
Long beforeTime, timeDiff, sleepTime;
while(running){
beforeTime = System.currentTimeMillis();
update();
render();
paintScreen();
timeDiff = System.currentTimeMillis() - beforeTime;
sleepTime = PERIOD - timeDiff;
if (sleepTime <= 0){
sleepTime = 5L;
}
currentFPS = (1000 / ( timeDiff + sleepTime ));
try{
Thread.sleep(sleepTime);
} catch (InterruptedException ex) { ex.printStackTrace(); }
}
System.exit(0);
}
//==================== DRAWING METHODS ====================//
private Graphics dbg;
private Image dbImage;
public void paintComponenet(Graphics g){
super.paintComponent(g);
if (dbImage != null){
g.drawImage(dbImage, 0, 0, null);
}
}
public void paintScreen(){
Graphics g;
try{
g = this.getGraphics();
if (g != null & dbImage != null){
g.drawImage(dbImage, 0, 0, null);
}
Toolkit.getDefaultToolkit().sync();
g.dispose();
} catch (Exception ex) { System.out.println("Graphics context error: " + ex); }
}
public void doubleBuffer(){
if (dbImage == null){
dbImage = createImage(getWidth(), getHeight());
if (dbImage == null){
System.out.println("dbImage is null");
return;
} else {
dbg = dbImage.getGraphics();
}
}
}
//==================== UPDATE METHODS ====================//
public void update(){
}
//==================== RENDER METHODS ====================//
public void render(){
doubleBuffer();
Graphics2D g2d = (Graphics2D) dbg;
g2d.setColor(Color.BLACK);
g2d.fillRect(0, 0, getWidth(), getHeight());
}
}
And this is my frame class:
package com.shparki.TowerDefense;
import java.awt.BorderLayout;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import javax.swing.JFrame;
public class TowerDefense extends JFrame implements WindowListener{
public TowerDefense(String name){
super(name);
setResizable(false);
setLayout(new BorderLayout());
add(new GamePanel(this), BorderLayout.CENTER);
pack();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setVisible(true);
}
public void windowActivated(WindowEvent arg0) { }
public void windowClosed(WindowEvent arg0) { }
public void windowClosing(WindowEvent arg0) { }
public void windowDeactivated(WindowEvent arg0) { }
public void windowDeiconified(WindowEvent arg0) { }
public void windowIconified(WindowEvent arg0) { }
public void windowOpened(WindowEvent arg0) { }
public static void main(String[] args){
new TowerDefense("Tower Defense Game");
}
}

The JFrame has not been packed then the height and widths are requested here in GamePanel
dbImage = createImage(getWidth(), getHeight());
resulting in 0x0 dimension for the JPanel.
The reason is that Swing invokes addNotify (where startGame resides) almost immediately once the parent component has been set for the component.
You could invoke startGame explicitly after pack has been called.
add(gamePanel, BorderLayout.CENTER);
pack();
gamePanel.startGame();

Related

How can I make my swing graphics project have a higher FPS(frames per second for anyone who doesn't know what it means)?

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

Java cannot render Menu state (Java GUI)

Please read the latest update down below
So I am trying to make a brick breaker game and while everything works in terms of gameplay I am having trouble adding a menu system. Basically this is how my code works
public class Game extends Canvas implements Runnable{
public Graphics g;
private Menu menu;
protected Ball ball;
protected Player player;
protected BufferedImage image;
protected BufferStrategy bufferStrategy;
protected Thread thread;
protected JFrame frame;
protected volatile boolean running, gameOver;
private enum STATE{
MENU,
GAME,
ABOUT,
OPTIONS,
};
private STATE State = STATE.MENU;
public Game(){
//Set the Jframe
}
private void init()
{
image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
requestFocus();
menu = new Menu();
//init everything else aswell
}
public void update()
{
//Update every moving object
}
#Override
public void run()
{
init();
long initialTime = System.nanoTime();
double timePerFrame = 1000000000/FPS;
double delta = 0;
int ticks = 0;
long timer = 0;
while (running)
{
long currentTime = System.nanoTime();
long elapsedTime = currentTime - initialTime;
delta += elapsedTime/timePerFrame;
timer += elapsedTime;
if (delta >= 1)
{
update();
delta--;
ticks++;
}
render();
initialTime = currentTime;
if (timer >= 1000000000)
{
currentFPS = ticks;
ticks = 0;
timer = 0;
}
}
stop();
}
And when I render everything that is in the STATE GAME it works just fine but when I try to add an else if statement that does menu.draw(g) it all falls apart and I just get a blank frame
Here is how I render
public void render()
{
bufferStrategy = getBufferStrategy();
if (bufferStrategy == null)
{
createBufferStrategy(3);
return;
}
g = bufferStrategy.getDrawGraphics();
g.drawImage(image, 0, 0, getWidth(), getHeight(), this);
g.setColor(BG_COLOR);
g.fillRect(0, 0, WIDTH, HEIGHT);
if(State == STATE.GAME){
player.draw(g);
ball.draw(g);
blockController.draw(g); **THESE WORK JUST FINE**
}
else if(State == STATE.MENU){
menu.draw(g); **DOES NOT WORK**
}
bufferStrategy.show();
g.dispose();
}
And my Menu class has no difference in terms of the draw method
public class Menu implements GUI
{
#Override
public void draw(Graphics g) {
g.setFont(new Font("arial", Font.BOLD, 50));
g.setColor(Color.black);
g.drawString("MENU", Game.WIDTH / 2, 100);
}
}
Any idea why this might be happening I am doing the same render litteraly but keep getting
Exception in thread "Thread-0" java.lang.ClassCastException: class sun.java2d.NullSurfaceData cannot be cast to class sun.java2d.d3d.D3DSurfaceData error or g is null error
How can I fix this?
UPDATE ----------------------------------
The menu.draw() in the render works when I remove the lines
g.setFont(new Font("arial", Font.BOLD, 50));
g.setColor(Color.black);
g.drawString("MENU", Game.WIDTH / 2, 100);
And instead add something like
g.setColor(Color.CYAN);
g.fillRect(5, 5, 200, 200);
This does work but why the setfont, setColor and drawString don't work I don't understand I wanted to add buttons aswell but they don't work either. Is it because I set the entire frame with a rectangle in the render with the line g.fillRect(0, 0, WIDTH, HEIGHT); but then can I add objects like paddle,ball,bricks but not a string or a button?
The following example seems to work fine.
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setVisible(true);
}
});
}
public class TestPane extends Canvas {
private Thread thread;
private volatile boolean render = false;
public TestPane() {
}
#Override
public void addNotify() {
super.addNotify();
start();
}
#Override
public void removeNotify() {
super.removeNotify();
stop();
}
protected void start() {
if (thread != null) {
render = false;
try {
thread.join();
} catch (InterruptedException ex) {
}
}
render = true;
thread = new Thread(new Runnable() {
#Override
public void run() {
while (render) {
render();
try {
Thread.sleep(16);
} catch (InterruptedException ex) {
}
}
}
});
thread.start();
}
protected void stop() {
render = false;
if (thread == null) {
return;
}
try {
thread.join();
} catch (InterruptedException ex) {
}
thread = null;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
private Menu menu = new Menu();
protected void render() {
BufferStrategy strategy = getBufferStrategy();
if (strategy == null) {
createBufferStrategy(3);
strategy = getBufferStrategy();
}
if (strategy == null) {
return;
}
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
Graphics graphics = strategy.getDrawGraphics();
graphics.setColor(Color.BLACK);
graphics.fillRect(0, 0, getWidth(), getHeight());
menu.render(graphics, getSize());
graphics.dispose();
// Repeat the rendering if the drawing buffer contents
// were restored
} while (strategy.contentsRestored());
// Display the buffer
strategy.show();
// Repeat the rendering if the drawing buffer was lost
} while (strategy.contentsLost());
}
}
public class Menu {
public void render(Graphics g, Dimension bounds) {
// This is probably going to cost you a lot of
// performance and it might be better to
// pre-create the font instead
g.setFont(new Font("Arial", Font.BOLD, 50));
g.setColor(Color.WHITE);
String text = "MENU";
FontMetrics fm = g.getFontMetrics();
g.drawString("MENU", (bounds.width - fm.stringWidth(text)) / 2, (bounds.height / 2) - fm.getAscent());
}
}
}
If you continue to have issues, continue providing a runnable example which demonstrates your issue

Flickering While Resizing Transparent JWindow

I'm trying to make a transparent window with Java Swing, and I have created the window. Problem is when I resize the window, it flickers. I tried changing the background to an opaque color instead. That fixed the problem, But I want the window to be transparent. I have also tried
Toolkit.getDefaultToolkit().setDynamicLayout(true);,
Toolkit.getDefaultToolkit().getDesktopProperty("awt.dynamicLayoutSupported");,
System.setProperty("sun.awt.noerasebackground", "true");,
But with no avail. I've tried JWindow.setBounds instead of JWindow.setSize, but that also had no effect.
Here is the code I use to produce the window
import java.awt.Color;
import java.awt.Graphics;
import java.awt.MouseInfo;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JPanel;
import javax.swing.JWindow;
public class GlassWindow extends JPanel {
// RIGHT CLICK TO CLOSE
private static final long serialVersionUID = 1L;
private JWindow jwindow = new JWindow();
private boolean programCalledRender, shouldClose;
private int edges = 8;
private Color background = new Color(255, 100, 0, 100);
public GlassWindow() {
int width = 1000, height = 750;
this.setOpaque(false);
this.setSize(width, height);
jwindow.setSize(width, height);
jwindow.setBackground(new Color(0, 0, 0, 0));
jwindow.setContentPane(this);
jwindow.setLocationRelativeTo(null);
jwindow.setVisible(true);
jwindow.addMouseListener(new MouseListener() {
public void mouseClicked(MouseEvent e) {
if(e.getButton() == MouseEvent.BUTTON3) System.exit(0);
}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
public void mousePressed(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
});
new Thread(() -> run()).start();
}
public void render() {
programCalledRender = true;
this.repaint();
}
private void run() {
int setTPS = 20, setFPS = 60;
long lastTime = System.nanoTime();
double delta = 0, frameDelta = 0;
while(true) {
if(shouldClose) break;
long now = System.nanoTime();
delta += (now-lastTime)/(1000000000/(double)setTPS);
frameDelta += (now-lastTime)/(1000000000/(double)setFPS);
lastTime = now;
while(delta > 0) {
tick();
delta--;
}
while(frameDelta > 0) {
render();
frameDelta--;
}
}
}
private void tick() {
}
#Override
public void paintComponent(Graphics g) {
if(!programCalledRender) return;
super.paintComponent(g);
int newWidth = MouseInfo.getPointerInfo().getLocation().x+20-jwindow.getX();
if(newWidth != jwindow.getWidth()) {
jwindow.setSize(newWidth, jwindow.getHeight());
}
if(background != null) {
g.setColor(background);
g.fillRect(edges, edges, jwindow.getWidth()-edges*2, jwindow.getHeight()-edges*2);
}
g.setColor(new Color(0, 0, 0, 100));
for(int i = 0; i <= edges; i++) {
g.drawRect(i, i, jwindow.getWidth()-i*2, jwindow.getHeight()-i*2);
}
}
public static void main(String[] args) {
new GlassWindow();
}
}
How can I prevent flickering when the JWindow is being resized?
Any help is appreciated.
As suggested by #camickr I should not resize the window in the render method.
So to stop the flickering I remove
int newWidth = MouseInfo.getPointerInfo().getLocation().x+20-jwindow.getX();
if(newWidth != jwindow.getWidth()) {
jwindow.setSize(newWidth, jwindow.getHeight());
}
from the paintComponent(Graphics g) method. And add
jwindow.addMouseMotionListener(new MouseMotionListener() {
public void mouseDragged(MouseEvent e) {}
public void mouseMoved(MouseEvent e) {
int newWidth = MouseInfo.getPointerInfo().getLocation().x+20-jwindow.getX();
if(newWidth != jwindow.getWidth()) {
jwindow.setSize(newWidth, jwindow.getHeight());
}
}
});
to the constructor GlassWindow().

Game Applet not running in 2D game (while loop)

I'm trying to set the dimensions for this game i want to create. The error is at while(running); I went over it for an hour but i can't seem to catch the error. I'm using NetbeansIDE also setPreserredSize (new Dimension(WIDTH, HEIGHT)); gives me an error, it is asking to Create a method " setPrefferedSize(java.awt.Dimension)"in.com.francesc.game.Game
private void setPreserredSize(Dimension dimension) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
when i run it i get this error
Exception in thread "main" java.lang.RuntimeException: Uncompilable source code - illegal start of type
at com.francescstudio.game.Game.<init>(Game.java:67)
at com.francescstudio.game.Start.main(Start.java:16)
Java Result: 1
heres all the code
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import javax.swing.JPanel;
/**
*
* #author francesc
*/
public class Game extends JPanel implements KeyListener, Runnable {
public static final long SerialVersionUID = 1L;
public static final int WIDTH = 400;
public static final int HEIGHT = 630;
public static final Font main = new Font("Bebas Nue Regular", Font.PLAIN, 28);
private Thread game;
private boolean running;
private BufferedImage Image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
private long startTime;
private long elapse;
private boolean set;
public Game() {
setFocusable(true);
setPreserredSize (new Dimension(WIDTH, HEIGHT));
addKeyListener(this);
}
private void update() {
}
private void render() {
Graphics2D g = (Graphics2D) Image.getGraphics();
g.setColor(Color.white);
g.fillRect(0, 0, WIDTH, HEIGHT);
// render board
g.dispose();
Graphics2D g2d = (Graphics2D) getGraphics();
g2d.drawImage(Image, 0, 0, null);
g2d.dispose();
}
#Override
public void run() {
}
int fps = 0, update = 0;
long fpsTimer = System.currentTimeMillis();
double nsPerUpdate = 1000000000.0 / 60;
// last update time in nano seconds
double then = System.nanoTime();
double unprocessed = 0;
while (running){
boolean shouldRender = false;
double now = System.nanoTime();
unprocessed += (now - then) / nsPerUpdate;
then = now;
// update queque
while (unprocessed >= 1) {
update++;
update();
unprocessed--;
shouldRender = true;
}
// render
if (shouldRender) {
fps++;
render();
shouldRender = false;
} else {
try {
Thread.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public synchronized start(){
if(running)return;
running = true;
game = new Thread (this, "game");
game.start();
}
public synchronized stop (){
if(!running) return;
running = false;
System.exit(0);
}
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
}
}
Your problem is while (running) {} isn't inside of a method, you defined it in the class where it will never get called.
EDIT:
Your code should look something a little more like this
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import javax.swing.JPanel;
/**
*
* #author francesc
*/
public class Game extends JPanel implements KeyListener, Runnable {
public static final long SerialVersionUID = 1L;
public static final int WIDTH = 400;
public static final int HEIGHT = 630;
public static final Font main = new Font("Bebas Nue Regular", Font.PLAIN, 28);
private Thread game;
private boolean running;
private BufferedImage Image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
private long startTime;
private long elapse;
private boolean set;
public Game() {
setFocusable(true);
setPreferredSize(new Dimension(WIDTH, HEIGHT));
addKeyListener(this);
}
private void update() {}
private void render() {
Graphics2D g = (Graphics2D) Image.getGraphics();
g.setColor(Color.white);
g.fillRect(0, 0, WIDTH, HEIGHT);
// render board
g.dispose();
Graphics2D g2d = (Graphics2D) getGraphics();
g2d.drawImage(Image, 0, 0, null);
g2d.dispose();
}
#Override
public void run() {
int fps = 0, update = 0;
long fpsTimer = System.currentTimeMillis();
double nsPerUpdate = 1000000000.0 / 60;
// last update time in nano seconds
double then = System.nanoTime();
double unprocessed = 0;
while (running) {
boolean shouldRender = false;
double now = System.nanoTime();
unprocessed += (now - then) / nsPerUpdate;
then = now;
// update queque
while (unprocessed >= 1) {
update++;
update();
unprocessed--;
shouldRender = true;
}
// render
if (shouldRender) {
fps++;
render();
shouldRender = false;
} else {
try {
Thread.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public synchronized start(){
if(running)return;
running = true;
game = new Thread (this, "game");
game.start();
}
public synchronized stop (){
if(!running) return;
running = false;
System.exit(0);
}
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
}
}

JButton hover effect animation. Changing opacity by Mouse Listener

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

Categories