BufferStrategy cleans Frame background - java

I have tried to make a simple GUI in Java Using graphics2D and JFrame.
I have added a background-color on JFrame typing this.setBackground(new Color(54, 71, 99)) inside initWindow() method. It turned out that backBuffer was clearing that background and not repainting the line that causes this is in render() method, last line backBuffer.show().
How Do I make it NOT clear the main background?
package asteroids;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferStrategy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
public class Main extends Canvas implements KeyListener {
private boolean gameOver;
private BufferStrategy backBuffer;
private Dimension dimension = new Dimension(Config.WINDOW_WH[0], Config.WINDOW_WH[1]);
private List<Star> stars = new ArrayList<Star>();
private HashMap<Integer,Boolean> keyDownMap = new HashMap<Integer, Boolean>();
public Main() {
// Initialize Window
initWindow();
addKeyListener(this);
this.setBackground(new Color(54, 71, 99));
this.createBufferStrategy(2);
backBuffer = this.getBufferStrategy();
// init variables
gameOver = false;
// Generating stars
generateStars();
// Init loop
gameLoop();
}
public void initWindow(){
JFrame window = new JFrame("Asteroids");
setPreferredSize(dimension);
window.add(this);
window.pack();
window.setResizable(false);
window.setVisible(true);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setBackground(new Color(54, 71, 99));
window.requestFocus();
}
public void update() {
if(keyDownMap.containsKey(KeyEvent.VK_ESCAPE)){
gameOver = false;
System.exit(0);
}
}
public void render(){
Graphics2D g = (Graphics2D) backBuffer.getDrawGraphics();
g.setColor(Color.WHITE);
for(Star s: stars) {
g.fillOval(s.posx - (s.width/2), s.posy - (s.height/2), s.width, s.height);
}
g.dispose();
backBuffer.show();
}
public void generateStars() {
for(int i = 0;i < 20;i++) {
int starX = new Random().nextInt(Config.WINDOW_WH[0]-10)+5;
int starY = new Random().nextInt(Config.WINDOW_WH[1]-10)+5;
stars.add(new Star(starX, starY));
}
}
public void gameLoop(){
while(!gameOver){
update();
render();
try{ Thread.sleep(20);}catch(Exception e){};
}
}
public static void main(String[] args) {
new Main();
}
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
keyDownMap.put(e.getKeyCode(), true);
}
#Override
public void keyReleased(KeyEvent e) {
keyDownMap.remove(e.getKeyCode());
}
}

How Do I make it NOT clear the main background?
You can't. Apart from Canvas been non-transparent (which can't be changed), BufferStrategy also has more then one page onto which it paints it's content (thus allowing to perform page flipping). Combined, this would make it impossible to maintain the background of the parent container.
Instead, you should (in fact, you must) clear the Graphics context of the buffer you painting to do, every time render is called, otherwise you will be painting onto what ever was previously painted on to it.
One technique might be to generate a BufferedImage with the "static" content and simply paint that to the buffer first

Related

Why is the repaint() method not calling my paintComponent() method?

I was trying to copy a repaint() and a paintComponent() method from a tutorial. After I copied the two methods my paintComponent did not get called and so the rectangle is not being showed. Here is my code:
public class Main {
GameWindow gw;
Main() {
gw = new GameWindow();
}
void start() {
gw.setWindow();
}
public static void main(String[] args) {
new Main().start();
}
}
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class GameWindow extends JPanel implements Runnable {
final int ORIGINAL_TILE_SIZE = 16;
final int SCALE = 3;
final int TILE_SIZE = ORIGINAL_TILE_SIZE * SCALE;
final int MAX_SCREEN_COLUMNS = 16;
final int MAX_SCREEN_ROWS = 12;
final int SCREEN_WIDTH = TILE_SIZE * MAX_SCREEN_COLUMNS;
final int SCREEN_HEIGHT = TILE_SIZE * MAX_SCREEN_ROWS;
Thread animation;
void setWindow() {
JFrame window = new JFrame();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setResizable(false);
window.setTitle("Avontuur");
window.setPreferredSize(new Dimension(SCREEN_WIDTH, SCREEN_HEIGHT));
window.getContentPane().setBackground(Color.black);
ImageIcon icon = new ImageIcon("C:\\Users\\Rick\\Desktop\\Star.png");
window.setIconImage(icon.getImage());
window.pack();
window.setLocationRelativeTo(null);
window.setVisible(true);
startAnimation();
}
void startAnimation() {
animation = new Thread(this);
animation.start();
}
#Override
public void run() {
while (animation != null) {
update();
repaint();
}
}
public void update() {
}
public void paintComponent(final Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setColor(Color.white);
g2.fillRect(100, 100, TILE_SIZE, TILE_SIZE);
g2.dispose();
}
}
I already tried some solutions from stackOverflow, but they did not work or they were not relevant to my problem. Now the code above is what I tried myself using the video, but after using a println in the method I saw it was not getting called. I expected it to work after watching the tutorial, but it didn't. Does anyone know how I can fix this? Thanks in advance!
Swing is single threaded - never block the Event Dispatching Thread with long running or blocking operations
Swing is NOT thread safe - never update the UI or any state the UI relies on from outside the context of the Event Dispatching Thread.
See Concurrency in Swing for more details.
Swing makes use of a passive rendering engine - you don't control the painting process and you need to work within in it's intended design/workflow, see Painting in AWT and Swing and Performing Custom Painting for more details.
So, what's the solution? In it's simplest form, use a Swing Timer, for example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
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 GamePane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class GamePane extends JPanel {
final int ORIGINAL_TILE_SIZE = 16;
final int SCALE = 3;
final int TILE_SIZE = ORIGINAL_TILE_SIZE * SCALE;
final int MAX_SCREEN_COLUMNS = 16;
final int MAX_SCREEN_ROWS = 12;
final int SCREEN_WIDTH = TILE_SIZE * MAX_SCREEN_COLUMNS;
final int SCREEN_HEIGHT = TILE_SIZE * MAX_SCREEN_ROWS;
private Timer timer;
#Override
public Dimension getPreferredSize() {
return new Dimension(SCREEN_WIDTH, SCREEN_HEIGHT);
}
#Override
public void addNotify() {
super.addNotify();
if (timer != null) {
timer.stop();
}
timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
update();
}
});
timer.start();
}
#Override
public void removeNotify() {
super.removeNotify();
if (timer != null) {
timer.stop();
}
}
public void update() {
System.out.println("Updatey update");
repaint();
}
public void paintComponent(final Graphics g) {
super.paintComponent(g);
System.out.println("Painty paint paint");
Graphics2D g2 = (Graphics2D) g.create();
g2.setColor(Color.white);
g2.fillRect(100, 100, TILE_SIZE, TILE_SIZE);
g2.dispose();
}
}
}
You should now see a bunch of text been printed to the console.
Also - you might not, I've made some structural changes to your code, there is not reason for a JPanel based class to create it's own window (or if you really wanted to do this, I'd create a static method to do it, but then I'd be questioning why).
Oh, and also what DontKnowMuchBut Getting Better said in the comments!
Here is the specific rectangle problem with your code, I will not mention other things because is not in the question, you are not adding GameWindow itself as component of your JFrame, and you are not calling your paintComponent method anywhere, I put it on update and it worked fine, but I'm afraid that your code will block and you will not able to move the window and interact with it since you are always calling update/repaint over and over again inside your while statement.
edit.: As the guy in the comment said, we must not call paintcomponent directly, so I fixed.
void setWindow() {
JFrame window = new JFrame();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setResizable(false);
window.setTitle("Avontuur");
window.add(GameWindow.this);
window.setPreferredSize(new Dimension(SCREEN_WIDTH, SCREEN_HEIGHT));
window.getContentPane().setBackground(Color.black);
ImageIcon icon = new ImageIcon("C:\\Users\\Rick\\Desktop\\Star.png");
window.setIconImage(icon.getImage());
window.pack();
window.setLocationRelativeTo(null);
window.setVisible(true);
startAnimation();
}

How to refresh graphics of a Window class in Java

I'm trying to draw over a vlcj (java binding of the VLC library) panel so that I can play a video and draw over it. And I have encounter some issues. Here is the full base code:
Code-listing 1: AppOverlay.java
package app;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.HeadlessException;
import java.awt.Window;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import com.sun.jna.platform.WindowUtils;
#SuppressWarnings("serial")
public class AppOverlay extends Window implements Runnable {
private final boolean isRunning;
private final int fps;
private BufferedImage graphics;
private BufferedImage img;
private int x, y;
private boolean ltr;
public AppOverlay(Window owner) {
super(owner, WindowUtils.getAlphaCompatibleGraphicsConfiguration());
setBackground(new Color(0,0,0,0));
graphics = new BufferedImage(1280,800, BufferedImage.TYPE_INT_ARGB);
isRunning = true;
img = null;
ltr = true;
fps = 60;
x = 0;
y = 0;
}
#Override
public void run(){
while(isRunning){
try{
Thread.sleep(1000/fps);
} catch(InterruptedException e){
e.printStackTrace();
}
if(ltr) {
if(x < 1280) x++;
else ltr = false;
} else {
if(x < 0) ltr = true;
else x--;
}
repaint();
}
}
public void createAndShowGUI() {
setVisible(true);
Thread thread = new Thread(this);
thread.start();
String path = "Drive:\\path\\to\\image.png";
try {
img = ImageIO.read(new java.io.FileInputStream(path));
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D)g;
Graphics2D gfx = graphics.createGraphics();
gfx.setColor(new Color(255,255,255,0));
gfx.clearRect(0, 0, 1280, 800);
if(img != null) gfx.drawImage(img, x, y, null);
gfx.dispose();
g2d.drawImage(graphics, 0, 0, null);
}
}
Code-listing 2: AppPlayer.java
package app;
import uk.co.caprica.vlcj.player.component.EmbeddedMediaPlayerComponent;
#SuppressWarnings("serial")
public class AppPlayer extends EmbeddedMediaPlayerComponent {
}
Code-listing 3: AppFrame.java
package app;
import java.awt.Dimension;
import javax.swing.JFrame;
#SuppressWarnings("serial")
public class AppFrame extends JFrame {
private AppPlayer appPlayer;
private AppOverlay overlay;
public AppFrame(){
super();
}
public void createAndShowGUI() {
appPlayer = new AppPlayer();
appPlayer.setPreferredSize(new Dimension(1280,800));
getContentPane().add(appPlayer);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setTitle("App");
setVisible(true);
pack();
overlay = new AppOverlay(this);
appPlayer.mediaPlayer().overlay().set(overlay);
appPlayer.mediaPlayer().overlay().enable(true);
overlay.createAndShowGUI();
}
}
Code-listing 4: Main.java
package main;
import javax.swing.SwingUtilities;
import app.AppFrame;
public class Main {
public static void main(String[] args) {
final AppFrame app = new AppFrame();
SwingUtilities.invokeLater( new Runnable() {
#Override
public void run() {
app.createAndShowGUI();
}
});
}
}
with that and the vlcj-4 library you should be able to test my code yourself. My issue is that the Overlay (AppOverlay class that extends the Window class) doesn't display or refresh the animation unless I deselect the window (I click on another window or on the desktop or the OS toolbar) so that the window (application) is inactive then select the window (the application) again. It will only load one frame and that's it. I have to deselect and reselect the window again for it to load another frame (this is only the case for the Overlay i.e. if I play a video in the AppPlayer class the video will be playing just fine.
What I want is to be able to draw some animated graphics on the overlay. I know that with the JPanel class there is the paintComponent() method but the Window class doesn't have that method (only the paint() and repaint() methods are available).
What should I do to fix this?
EDIT:
I tried adding a JPanel on which I draw instead of drawing directly on the AppOverlay
Code-listing 5: AppPanel.java
package app;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
public class AppPanel extends JPanel implements Runnable {
private int x, y;
private boolean ltr;
public AppPanel() {
x = 0;
y = 0;
ltr = true;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(new Color(0,0,0,0));
g.clearRect(0, 0, 1280, 800);
g.setColor(Color.RED);
g.fillRect(x, y, 100, 100);
}
#Override
public void run() {
while(true){
try{
Thread.sleep(1000/60);
} catch(InterruptedException e){
e.printStackTrace();
}
if(ltr) {
if(x < 1280) x++;
else ltr = false;
} else {
if(x < 0) ltr = true;
else x--;
}
repaint();
}
}
}
then adding it to the AppOverlay.
Code-listing 6: AppOverlay.java with partial modification
public class AppOverlay extends Window implements Runnable {
//previous field declaration above ...
AppPanel panel;
AppPlayer player = null;
public AppOverlay(Window owner) {
//previous constructor instructions above...
panel = new AppPanel();
add(panel);
}
public void createAndShowGUI(AppPlayer player) {
setVisible(true);
/*
Thread thread = new Thread(this);
thread.start();
String path = "Drive:\\path\\to\\image.png";
try {
img = ImageIO.read(new java.io.FileInputStream(path));
} catch (IOException e) {
e.printStackTrace();
}
*/
Thread panelThread = new Thread(panel);
panelThread.start();
}
}
Doing this will display the graphics of the JPanel and animate them as needed.
If you know a way to make the JPanel background transparent (so that we can see through it) while still letting it display its graphics. That would solve the issue for sure.
I played around a bit with your example and came up with something working, but I wouldn't call it a nice solution.
The main issue seems to be that there is no way to tell the overlay to refresh (or I just have not found it). Just repainting the overlay does not update it on screen, so the workaround I used is to hide and show it again.
For the timeing of the update interval I used a javax.swing.Timer.
(In a real version you probably want to start and stop the timer via the MediaPlayerEventListener).
As a side effect the repaint method is called and the x coordinate is adjusted to move the image around the screen.
In the simplified example below (use your main to run it), I moved a red rectangle with the x coordinate instead of some unknown image.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.HeadlessException;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.Timer;
import com.sun.jna.platform.WindowUtils;
import uk.co.caprica.vlcj.player.component.EmbeddedMediaPlayerComponent;
import uk.co.caprica.vlcj.player.embedded.OverlayApi;
public class AppFrame extends JFrame {
private static final long serialVersionUID = -1569823648323129877L;
public class Overlay extends Window {
private static final long serialVersionUID = 8337750467830040964L;
private int x, y;
private boolean ltr = true;
public Overlay(Window owner) throws HeadlessException {
super(owner, WindowUtils.getAlphaCompatibleGraphicsConfiguration());
setBackground(new Color(0,0,0,0));
}
#Override
public void paint(Graphics g) {
super.paint(g);
if (ltr) {
if (x < 1180)
x += 1;
else
ltr = false;
} else {
if (x < 0)
ltr = true;
else
x -= 1;
}
g.setColor(Color.RED);
g.fillRect(x, y, 100, 100);
String s = Integer.toString(x);
g.setColor(Color.WHITE);
g.drawChars(s.toCharArray(), 0, s.length(), x+10, y+50);
}
}
private EmbeddedMediaPlayerComponent appPlayer;
public void createAndShowGUI() {
appPlayer = new EmbeddedMediaPlayerComponent();
appPlayer.setPreferredSize(new Dimension(1280, 800));
getContentPane().add(appPlayer);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setTitle("App");
setVisible(true);
pack();
Overlay overlay = new Overlay(this);
OverlayApi api = appPlayer.mediaPlayer().overlay();
api.set(overlay);
api.enable(true);
//appPlayer.mediaPlayer().media().play(" ... ");
Timer timer = new Timer(0, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
api.enable(false);
api.enable(true);
}
});
timer.setRepeats(true);
timer.setDelay(200);
timer.start();
}
}
If that is an option for you, it might be far easier to use an animated gif instead. At least that is working on its own (no need for the Timer).
Update:
As you figured out using a JPanel seems to work better.
Just use setOpaque(false) to make it transparent.
Here an adjusted example.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.HeadlessException;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import uk.co.caprica.vlcj.player.component.EmbeddedMediaPlayerComponent;
import uk.co.caprica.vlcj.player.embedded.OverlayApi;
public class AppFrame2 extends JFrame {
private static final long serialVersionUID = -1569823648323129877L;
public class OverlayPanel extends JPanel {
private static final long serialVersionUID = 8070414617530302145L;
private int x, y;
private boolean ltr = true;
public OverlayPanel() {
this.setOpaque(false);
}
#Override
public void paint(Graphics g) {
super.paint(g);
if (ltr) {
if (x < 1180)
x += 1;
else
ltr = false;
} else {
if (x < 0)
ltr = true;
else
x -= 1;
}
g.setColor(Color.RED);
g.fillRect(x, y, 100, 100);
String s = Integer.toString(x);
g.setColor(Color.WHITE);
g.drawChars(s.toCharArray(), 0, s.length(), x+10, y+50);
}
}
public class Overlay extends Window {
private static final long serialVersionUID = 8337750467830040964L;
OverlayPanel panel;
public Overlay(Window owner) throws HeadlessException {
super(owner);
setBackground(new Color(0,0,0,0));
panel = new OverlayPanel();
this.add(panel);
}
}
private EmbeddedMediaPlayerComponent appPlayer;
public void createAndShowGUI() {
appPlayer = new EmbeddedMediaPlayerComponent();
appPlayer.setPreferredSize(new Dimension(1280, 800));
getContentPane().add(appPlayer);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setTitle("App");
setVisible(true);
pack();
Overlay overlay = new Overlay(this);
OverlayApi api = appPlayer.mediaPlayer().overlay();
api.set(overlay);
api.enable(true);
//appPlayer.mediaPlayer().media().play(" ... ");
Timer timer = new Timer(0, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
overlay.panel.repaint();
}
});
timer.setRepeats(true);
timer.setDelay(17);
timer.start();
}
}
You have already done the bulk of the work. Simply repaint the frame every time you draw over it by calling app.repaint();
You can use the following methods from JComponent: ( http://download.oracle.com/javase/6/docs/api/javax/swing/JComponent.html )
void repaint(long tm, int x, int y, int width, int height)
//**Adds the specified region to the dirty region list if the component is showing.*//
void repaint(Rectangle r)
/**Adds the specified region to the dirty region list if the component is showing.*//
You can call those before redraw()

java pick random images

English is not my native language, sorry for any mistakes. I have to make a Bubble Shooter game in Java. I want to use images for the bubbles and I want the images to be picked randomly. I used Random and ImageIcon classes. My program doesn't show anything when I compile it and I don't know where the problem is. I'm a beginner in Java.
This is the code for my Game class:
import java.awt.Graphics;
import java.awt.Image;
import java.util.Vector;
import javax.swing.JPanel;
public class Game extends JPanel{
private static final long serialVersionUID = 1L;
//what the balls are like
public final static int START_BALLS=40;
public static Vector<Ball> balls = new Vector<Ball>();
private Image img;
private Graphics graphics;
public Game() {
for(int i=0; i<START_BALLS; i++) {
balls.add(new Ball());
}
}
public void paint(Graphics g) {
img = createImage(null);
graphics = img.getGraphics();
paintComponent(graphics);
g.drawImage(img, 0, 0, null);
repaint();
}
public void paintComponet(Graphics g) {
for(int i=0; i<balls.size(); i++) {
Ball b=(Ball)balls.get(i);
b.draw(g);
}
}
public static void main(String [] args) {
new Frame();
Game game = new Game();
new Game();
Window.window.add(game);
}
}
and the class for the bubbles:
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import java.util.Random;
import javax.swing.ImageIcon;
public class Ball {
Random random = new Random();
final String[] image_paths = new String[] {"balls/peg_0.png",
"balls/peg_1.png","balls/peg_2.png","balls/peg_3.png",
"balls/peg_4.png","balls/peg_5.png"};
String randomBalls;
public Image image;
public Ball(){
randomBalls = image_paths[random.nextInt(image_paths.length)];
ImageIcon poza = new ImageIcon(randomBalls);
image=poza.getImage();
}
public void draw(Graphics g){
g.drawImage(image, 0, 0, null, null);
}
}
What is wrong with my program?
Look over my comments, look carefully at the comments in the code, see how I rearranged the organization of the classes.
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.util.Random;
import java.util.Vector;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.imageio.ImageIO;
import java.net.URL;
public class Game extends JPanel {
Random random = new Random();
final String[] image_path = new String[]{
"http://i.stack.imgur.com/gJmeJ.png",
"http://i.stack.imgur.com/IHARa.png",
"http://i.stack.imgur.com/wCF8S.png",
"http://i.stack.imgur.com/T5uTa.png"
};
private static final long serialVersionUID = 1L;
//what the balls are like
public final static int START_BALLS = 40;
public static Vector<Ball> balls = new Vector<Ball>();
private Image img;
// A Graphics instance is typically transient.
// There is rarely, if ever, a need to store them
//private Graphics graphics;
public Game() {
for (int i = 0; i < image_path.length; i++) {
balls.add(new Ball(image_path[i]));
}
//I have no idea what you were trying to achieve here, but it fails horribly
// img = createImage(null);
img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB);
// graphics = img.getGraphics();
ActionListener al = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
repaint();
}
};
Timer timer = new Timer(400, al);
timer.start();
}
#Override // very handy!
public void paintComponent(Graphics g) {
super.paintComponent(g);
// g.drawImage(img, 0, 0, null); A JPanel IS A ImageObserver
g.drawImage(img, 0, 0, this);
Ball b = (Ball) balls.get(random.nextInt(4));
b.draw(g);
}
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
JFrame f = new JFrame("Game");
f.add(new Game());
// Ensures JVM closes after frame(s) closed and
// all non-daemon threads are finished
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
// See https://stackoverflow.com/a/7143398/418556 for demo.
f.setLocationByPlatform(true);
// ensures the frame is the minimum size it needs to be
// in order display the components within it
f.pack();
// should be done last, to avoid flickering, moving,
// resizing artifacts.
f.setVisible(true);
}
};
// Swing GUIs should be created and updated on the EDT
// http://docs.oracle.com/javase/tutorial/uiswing/concurrency
SwingUtilities.invokeLater(r);
}
}
class Ball {
String randomBalls;
public Image image;
public Ball(String url) {
try {
image = ImageIO.read(new URL(url));
} catch (Exception e) {
e.printStackTrace();
}
}
public void draw(Graphics g) {
g.drawImage(image, 0, 0, null, null);
}
}
Tips
Exception in thread "AWT-EventQueue-0" java.lang.UnsupportedOperationException: getGraphics() not valid for images created with createImage(producer) at img = createImage(null); I have no idea what you thought that code statement does, but ..nothing good.
When doing custom painting in a JPanel, we should override only paintComponent(Graphics) and leave the paint(Graphics) method as it is. When overriding the former, immediately call the super method.
Adding a call to repaint() inside paint(Graphics) will cause an infinite loop.. If the code needs to loop, establish a Swing Timer to call repaint()
paintComponet(Graphics g) should be paintComponent(Graphics g) Use #Override notation when appropriate. It would have warned you of the incorrectly spelled method name.
More general tips
For better help sooner, post an MCVE.
One way to get image(s) for an example is to hot-link to the images seen in this answer.
By the time of deployment, those images will likely become an embedded-resource. That being the case, they must be accessed by URL instead of File. See the info page for the tag, for a way to form an URL.

Cannot draw transparent Component backgrounds

I have tried several tutorials and searches to figure out how to accomplish what I am trying to do. Basically I have a JLayeredPane with two Jpanels inside it. One for my game's drawing surface and one for my gui, like a pause menu. I have a png file with transparencies that I want to be the background of my gui panel that popups when the user hits escape. No matter what I do, the background of the panel (even tried making it just a component) is always grey with my png file drawn over it.
I have tried what others have recommended such as the following.
setBackground(new Color(0,0,0,0));
and
setOpaque(false);
Neither of these has seemed to help and perhaps I am failing to do something else after these. I have traditionally done them after the constructor or within the constructor of a class that extends jpanel.
I am almost to the point where I am going to have one panel and draw everything myself but I would much rather use the built in java functions like boxlayouts, etc.
Edit Adding Working Example:
import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.DisplayMode;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
public class Example {
private MyWindow gWindow;
public static void main(String argv[]) {
Example g = new Example();
g.gameLoop();
}
public Example() {
gWindow = new MyWindow();
// Initialize the keyboard listener
gWindow.frame().addKeyListener(new KeyListener() {
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_ESCAPE) // escape key, show menu
{
System.exit(0);
}
}
#Override
public void keyReleased(KeyEvent e) {
}
#Override
public void keyTyped(KeyEvent e) {
}
});
}
public void gameLoop() {
long lastLoopTime = System.currentTimeMillis();
while(true) {
// Used to calculate movement of sprites
long delta = System.currentTimeMillis() - lastLoopTime;
lastLoopTime = System.currentTimeMillis();
// Clear the canvas
Graphics2D g = (Graphics2D) gWindow.getBufferStrategy().getDrawGraphics();
g.setColor(Color.black);
g.fillRect(0,0,gWindow.frame().getWidth(), gWindow.frame().getHeight());
// Clean up graphics and flip buffer
g.dispose();
gWindow.getBufferStrategy().show();
// Small delay before next cycle
try { Thread.sleep(10); } catch (Exception e) {}
}
}
public class MyWindow {
private JFrame frame;
private JLayeredPane container;
private MyPanel gui;
private JPanel surface;
private Canvas canvas;
private GraphicsDevice vc;
private Dimension dm;
BufferedImage menuImg = null;
BufferedImage menuImgHighlight = null;
BufferedImage gSettings = null;
Font font = null;
public MyWindow() {
frame = new JFrame("Jumper");
vc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
DisplayMode display = vc.getDisplayMode();
dm = new Dimension(display.getWidth(), display.getHeight());
container = new JLayeredPane();
gui = new MyPanel();
gui.setLayout(new BoxLayout(gui, BoxLayout.Y_AXIS));
surface = new JPanel(new BorderLayout(0,0));
frame.add(container, BorderLayout.CENTER);
container.add(surface, new Integer(0), 0);
container.add(gui, new Integer(1), 0);
init_resources();
canvas = new Canvas();
surface.add(canvas);
gui.setBackground(new Color(0,0,0,0));
gui.setVisible(true);
gui.setOpaque(false);
surface.setVisible(true);
setFullScreen(display);
frame.setResizable(false);
frame.setVisible(true);
frame.pack();
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
frame.addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
setScreen(new Dimension(frame.getWidth(), frame.getHeight()));
frame.repaint();
}
});
canvas.setIgnoreRepaint(true);
canvas.createBufferStrategy(2);
canvas.setFocusable(false);
}
public JFrame frame() {
return frame;
}
public BufferStrategy getBufferStrategy () {
return canvas.getBufferStrategy();
}
public void setScreen(Dimension dim) {
int width = (int) dim.getWidth();
int height = (int) dim.getHeight();
this.dm = dim;
container.setPreferredSize(dm);
gui.setPreferredSize(dm);
surface.setPreferredSize(dm);
canvas.setBounds(0,0,width,height);
if(gSettings == null) {
gui.setBounds((int) ((dm.getWidth() - 200) / 2),
(int) ((dm.getHeight() - 200) / 2),
200,
200);
}
else {
gui.setBounds((int) ((dm.getWidth() - gSettings.getWidth()) / 2),
(int) ((dm.getHeight() - gSettings.getHeight()) / 2),
gSettings.getWidth(),
gSettings.getHeight());
}
gui.setBackground(gSettings);
surface.setBounds(0,0,width,height);
container.setBounds(0,0,width,height);
frame.validate();
}
public void setFullScreen(DisplayMode display) {
setScreen( Toolkit.getDefaultToolkit().getScreenSize());
frame.setUndecorated(true);
vc.setFullScreenWindow(frame);
if(dm != null && vc.isDisplayChangeSupported()) {
try {
vc.setDisplayMode(display);
}
catch(Exception e) {}
}
frame.validate();
}
private void init_resources() {
try {
gSettings = ImageIO.read(getClass().getResourceAsStream("/gui/settingsWindow.png"));
}
catch(Exception e)
{
System.out.print("Failed to load resources");
System.out.println();
}
}
}
public class MyPanel extends JPanel {
BufferedImage img = null;
public MyPanel() {
super();
setOpaque(false);
}
public void setBackground(BufferedImage img) {
this.img = img;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if(img != null) {
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(img, 0, 0, null);
}
}
}
}
I've not tested this, but, instead of calling super.paintComponent at the end of you paint method, try calling at the start....
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if(img != null) {
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(img, 0, 0, null);
}
}
The reasoning for this, is one of the jobs of paintComponent is clear the graphics context and ready it to be painted on. Event if the component is transparent, it must still clear/wipe the graphics context of anything that has previously been painted on it. The graphics context is a shared resource, meaning that all the components within a given window may share the same graphics context, so it gets a little dirty if it's not "wiped" first ;)
You may also have issues with mixing heavy and light weight components, but seen as you adding the light weight components to the heavy weight component, it may not be an issue, but it's worth putting in the back of your mind... ;)
JComponent is transparent by default ;)
Try to apply some Physics over here...
The visible white color is combination of RGB max values...
If you are keeping RGB values to Minimum it will give you dark color (Black) and not the transparent one..
try to implement below methods..
(your component).setOpaque(false);
(your component).setContentAreaFilled(false);
(your component).setBorderPainted(false);
Hope so this will help you...

JButton isn't in the right place when a BufferedImage is used in it's container

I'm trying to create a simple game using AWT but I want to have some JButtons aswell to exit/reset the game. The problem is, I want the BufferedImage to be drawn inside the visible frame like so in my container I have this at the end:
g.drawImage(bf,getParent().getInsets().left,getParent().getInsets().top,null);
My problem is, when I add a JButton to that frame, it only detects rollover in space that doesn't take into account the offsetting, but is drawn in a space that does. This is the relevant code (con is the container).
private void addButtons()
{
reset = new JButton("reset");
reset.setBounds(180,460, 75,30);
reset.addActionListener( this );
con.add(reset);
exit = new JButton("exit");
exit.setBounds(290,460, 60,30);
exit.addActionListener( this );
con.add(exit);
con.repaint();
}
The paint method in the Container
public void paint(Graphics g)
{
bf = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics b = bf.getGraphics();
b.setColor(Color.GRAY);
b.fillRect(0, 0, this.getWidth(), this.getHeight());
b.setColor(Color.BLACK);
b.drawRect(0,0,420,420);
super.paint(b);
g.drawImage(bf,getParent().getInsets().left,getParent().getInsets().top,null);
}
How can I make the button be drawn and detected in the same spot?
here is a screenshot of the problem
As requested:
import java.awt.*;
import javax.swing.*;
import java.util.*;
import java.awt.image.*;
import java.awt.event.*;
import java.awt.Color;
public class Draw implements ActionListener{
private SnakeFrame frame;
private SnakeCon con;
JButton reset, exit;
private boolean res;
public Draw()
{
frame = new SnakeFrame("Snake");
frame.setResizable(false);
frame.setLayout(null);
frame.setSize(600,600);
frame.setVisible(true);
con = new SnakeCon();
con.setBounds(0,0,600,600);
frame.add(con);
}
private void addButtons()
{
reset = new JButton("reset");
reset.setBounds(180,460, 75,30);
reset.addActionListener( this );
con.add(reset);
exit = new JButton("exit");
exit.setBounds(290,460, 60,30);
exit.addActionListener( this );
con.add(exit);
con.repaint();
}
public void run()
{
addButtons();
res = false;
boolean dead = false;
while(!dead)
{
if( (res) )
dead = true;
if (!dead)
{
try{
Thread.sleep(100);
}
catch (Exception e)
{}
frame.repaint();
}
}
con.removeAll();
}
public void actionPerformed(ActionEvent e)
{
if (e.getSource() == reset)
res = true;
else if (e.getSource() == exit)
System.exit(0);
}
}
--
import java.awt.*;
import javax.swing.*;
import java.util.*;
import java.awt.image.*;
import java.awt.event.*;
import java.awt.Color;
public class SnakeCon extends Container{
private BufferedImage bf;
public SnakeCon()
{
super();
setBounds(0,0,600,600);
}
public void paint(Graphics g)
{
bf = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics b = bf.getGraphics();
b.setColor(Color.GRAY);
b.fillRect(0, 0, this.getWidth(), this.getHeight());
b.setColor(Color.BLACK);
b.drawRect(0,0,420,420);
super.paint(b);
g.drawImage(bf,getParent().getInsets().left,getParent().getInsets().top,null);
}
public void update(Graphics g)
{
paint(g);
}
}
--
import java.awt.*;
import javax.swing.*;
import java.util.*;
import java.awt.image.*;
import java.awt.event.*;
import java.awt.Color;
public class SnakeFrame extends Frame implements WindowListener{
private BufferedImage bf;
public SnakeFrame(String s)
{
super(s);
addWindowListener( this );
}
public void paint(Graphics g)
{
bf = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics b = bf.getGraphics();
super.paint(b);
g.drawImage(bf,0,0,null);
}
public void update(Graphics g)
{
paint(g);
}
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
public void windowClosed(WindowEvent e) { }
public void windowOpened(WindowEvent e) { }
public void windowIconified(WindowEvent e) { }
public void windowDeiconified(WindowEvent e) { }
public void windowActivated(WindowEvent e) { }
public void windowDeactivated(WindowEvent e) { }
}
--
public class Main {
public static void main(String[] args)
{
boolean never = false;
Draw d = new Draw();
while(!never)
{
d.run();
}
System.exit(0);
}
}
Im not sure exactly what is wrong/what you want (why do you have a loop to constantly drawing buttons and invoking removeAll()? etc but i cant shake the feeling it might be implemented in a more readable/efficient way)...
But here are some suggestions that can only help your code get better:
Dont use null/Absolute Layout choose an appropriate LayoutManager.
Do not override JFrame paint(..), rather add JPanel to JFrame and override paintComponent(Graphics g) of JPanel and do drawing there.(Do not forget to have super.paintComponent(..) as 1st call in overriden paintComponent method. See here for more: Performing Custom Painting
Do not set JFrame visible before adding all components to JFrame
Always create and manipulate Swing components on Event Dispatch Thread like so:
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
//create Swing components
}
});
Do not do long running tasks on Event Dispatch Thread rather use Swing Timer/Swing Worker
Do not call setSize(..) on JFrame rather override getPreferredSize() of JPanel and return Dimensions which fit all components (see here for reasoning), than call pack() on JFrame before setting it visible
Dont extend JFrame unnecessarily or Container!
Adding WindowListener for detecting JFrame exit is not worth the lines rather use:
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
to exit Swing application when X is pressed.

Categories