I am drawing on an image on JPanel's graphic context. However the image does not remove itself from the graphics context when I press S. I know for sure my keyListeners are working.
If I am disposing the graphics context, shouldn't the content on the graphics context go away?
public class MainMenu extends JPanel implements KeyListener {
private JFrame frame;
private int width = 660;
private int height = 500;
private Image image;
private boolean removeImage = false;
public MainMenu()
{
frame = new JFrame();
frame.setResizable(false);
frame.setTitle("Menu Test");
setBackground(Color.BLACK);
frame.setSize(width,height);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// add main menu to the frame
frame.add(this);
// let the frame know about keyevents from this class
frame.addKeyListener(this);
}
public void setup()
{
frame.setVisible(true);
}
#Override
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
if(e.getKeyCode() == KeyEvent.VK_S)
{
removeImage = true;
}
repaint();
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
try {
image = ImageIO.read(new File("Game/menuScreen.PNG"));
g.drawImage(image, 0, 0, null);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(removeImage)
{
g.dispose();
}
}
Calling Graphics#dispose will not remove the image from the JPanel
if (removeImage) {
g.dispose();
}
instead use a boolean flag to indicate whether or not the image should be painted
if (showImage) {
g.drawImage(image, 0, 0, this);
}
update the flag to false and invoke repaint to effectively "clear" any previous images.
showImage = false;
repaint();
Notes:
It is unnecessary to invoke dispose for Graphics objects in paintComponents. This only applys to custom Graphics references.
Don't load images from paintComponent - this degrades performance. Load the image from a method at startup
When developing Swing applications use Key Bindings rather than KeyListeners. The latter use KeyEvents which require focus to work. Key Bindings use KeyStrokes which work regardless of focus.
Related
The program makes a ball glide across from top left to bottom right and works. But if I were to shift the line
frame.getContentPane().add(ball);
from its current position to after the for loop, why doesn't the ball show up on the frame.
I agree that the ball should no longer move, because all the shifting done in the for loop happens even before I add the ball to the JFrame,but I don't understand why the ball doesn't show up on the screen when I ultimately add it to the frame.
Here's the code of the working program, if you shift the line mentioned above to after the for loop, the ball no longer shows up
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class Animate
{
private JFrame frame;
private int x,y;
public static void main(String args[])
{
Animate ballRoll = new Animate();
ballRoll.go();
}
public void go()
{
frame = new JFrame();
frame.setSize(500,500);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
MyRoll ball = new MyRoll();
frame.getContentPane().add(ball);
for(x = 5;x<=350;x++)
{
y=x;
try
{
Thread.sleep(50);
}
catch(Exception e)
{
System.out.println("dsfsd");
}
ball.repaint();
}
}
class MyRoll extends JPanel
{
public void paintComponent(Graphics g)
{
g.setColor(Color.BLACK);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
g.setColor(Color.ORANGE);
g.fillOval(x, y, 100, 100);
}
}
}
Some points to remember:
Don't use Thread.sleep() that sometime hangs the whole swing application instead try with Swing Timer that is most suitable for swing application.
Read more How to Use Swing Timers
Don't forget to call super.paintComponent() in overridden paintComponent() method.
Call frame.setVisible(true) in the end after adding all the components.
Use frame.pack() instead of frame.setSize(500,500) that fits the components as per component's preferred size.
Override getPreferredSize() to set the preferred size of the JPanel in case of custom painting.
Use SwingUtilities.invokeLater() or EventQueue.invokeLater() to make sure that EDT is initialized properly.
Read more
Why to use SwingUtilities.invokeLater in main method?
SwingUtilities.invokeLater
Should we use EventQueue.invokeLater for any GUI update in a Java desktop application?
Sample code: (change it as per your custom painting)
private Timer timer;
...
timer = new javax.swing.Timer(50, new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
y = ++x;
ball.repaint();
if (x > 350) {
timer.stop();
}
}
});
timer.setRepeats(true);
timer.start();
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
Animate ballRoll = new Animate();
ballRoll.go();
}
});
}
class MyRoll extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
...
}
#Override
public Dimension getPreferredSize() {
return new Dimension(..., ...);
}
}
So I'm trying to make a Roguelike game with procedural generation etc. But I'm having trouble with the character handling.
The thing is when i want to move the character around with the keyboard, it is only changing its coordinates - which it should do of course, but i can't see it on the screen.
I read some answers on this site, and that helped me a little, but it didn't solve the problem entirely.
So, I'm note sure what I'm doing wrong, here's a bit of code (hope that it is not too long):
public class PanelTest extends JPanel implements KeyListener{
private static final long serialVersionUID = 1L;
private Game game;
private int width, height;
private int tileSize;
public PanelTest(Game game, int tileSize) {
super();
this.game = game;
this.tileSize = tileSize;
this.width = game.getMap().getWidth()*tileSize;
this.height = game.getMap().getHeight()*tileSize;
this.addKeyListener(this);
setPreferredSize(new Dimension(width, height));
setRequestFocusEnabled(true);
requestFocus();
setVisible(true);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
for(int i=0; i < game.getMap().getWidth(); i++){
for(int j=0; j < game.getMap().getHeight(); j++){
g.setColor(game.getMap().getColor(i,j));
g.fillRect(i*tileSize, j*tileSize, tileSize, tileSize);
}
}
// print the correct coordinates
System.out.println(game.getPlayer().getX()+" "+game.getPlayer().getY());
}
#Override
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
switch(e.getKeyCode()){
case(KeyEvent.VK_Z) : game.getPlayer().moveUp();break;
case(KeyEvent.VK_S) : game.getPlayer().moveDown(); break;
case(KeyEvent.VK_Q) : game.getPlayer().moveLeft(); break;
case(KeyEvent.VK_D) : game.getPlayer().moveRight(); break;
}
revalidate();
repaint();
}
public void addNotify() {
super.addNotify();
requestFocus();
}
#Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
}
#Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
public static void main(String[] args){
JFrame frame = new JFrame();
frame.setBounds(0, 0, 400, 400);
Map map = MapFactory.createNewEmptyMap();
RoomMaker rm = new RoomMaker(map.getWidth()/7,map);
TunnelMaker tm = new TunnelMaker(rm, map);
rm.carveOut();
tm.carveOut();
Room r = rm.getRandomRoom();
Player p = new Player(map, r.getxCenter(),r.getyCenter());
Game game = new Game(map,p);
frame.getContentPane().add(new PanelTest(game, 10));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
}
Hope you can help, thanks !
By default a JPanel uses a FlowLayout.
It looks like your code is changing a players position, but then you invoke revalidate() which causes the layout manager code to be invoked and the location of your component is recalculated again.
So for games where you have random movement you want to use a null layout and you don't need to invoke revalidate() and repaint() since your code is not responsible for invoking the setLocation() method directly.
See also Motion Using the Keyboard for problems with using a keyListener.
setRequestFocusEnabled(true);
requestFocus();
setVisible(true);
The above is not needed. The two properties you are setting to true default to true. You can't request focus on a component unless the component is displayed on a visible GUI, so adding that code here does nothing. Also, the proper method to use would be requestFocusInWindow(), not requestFocus().
I am making a Pong program, and I have a start button that begins to draw everything, and quite literally to get the ball rolling (you're welcome for the pun). Anyways, when I hit the start button, a Key Listener to move the paddles won't work unless I click the mouse somewhere on the canvas to give it priority. Is there some sort of code to automatically "click" on the canvas without the user being hassled to do so? Thanks in advance.
This Is running awt by the way. I realize I should learn swing, but never got around to it.
public class Pong extends Applet implements ActionListener, KeyListener
{
Canvas c1;
Graphics myG;
Button start;
ball ball;
paddle LPaddle;
paddle RPaddle;
public void init()
{
this.setSize(1300,700);
c1 = new Canvas();
add(c1);
c1.addKeyListener(this);
c1.setBackground(Color.pink);
start = new Button("Start");
add(start);
start.addActionListener(this);
ball = new ball();
LPaddle = new paddle();
RPaddle = new paddle();
myG = c1.getGraphics();
}
public void paint(Graphics g)
{
c1.setLocation(0,0);
c1.setSize(1251,700);
start.setLocation(1255,350);
start.setSize(40,20);
}
public void keyPressed(KeyEvent e)
{
if(e.getKeyCode()==KeyEvent.VK_UP)//up
{
RPaddle.erasePaddle(myG);
RPaddle.movePaddleUp();
RPaddle.drawPaddle(myG);
}
if(e.getKeyCode()==KeyEvent.VK_DOWN)//down
{
RPaddle.erasePaddle(myG);
RPaddle.movePaddleDown();
RPaddle.drawPaddle(myG);
}
if(e.getKeyCode()==KeyEvent.VK_W)
{
LPaddle.erasePaddle(myG);
LPaddle.movePaddleUp();
LPaddle.drawPaddle(myG);
}
if(e.getKeyCode()==KeyEvent.VK_S)
{
LPaddle.erasePaddle(myG);
LPaddle.movePaddleDown();
LPaddle.drawPaddle(myG);
}
if(e.getKeyCode()==KeyEvent.VK_ENTER)
{
myG.drawLine(625,0,625,700);
LPaddle.setInitial(150,0,350);
RPaddle.setInitial(150,1250,350);
LPaddle.drawPaddle(myG);
RPaddle.drawPaddle(myG);
}
}
public void keyReleased(KeyEvent e)
{
}
public void keyTyped(KeyEvent e)
{
}
public void actionPerformed(ActionEvent e)
{
if(e.getSource()==start)
{
myG.drawLine(625,0,625,700);
LPaddle.setInitial(150,0,350);
RPaddle.setInitial(150,1250,350);
LPaddle.drawPaddle(myG);
RPaddle.drawPaddle(myG);
}
}
KeyListeners are low level interfaces which have a major, significant draw back: The componet they are registered to must be focusable and be focused.
By clicking the start button, you are giving the button focus.
You could call requestFocusInWindow on the instance of the canvas, but this assumes that the the canvas is focusable in the first place.
If you can, you'd be better of using a JComponent/JPanel as the base for your game canvas and use the key bindings API.
I am trying to create a circle-shaped window that follows the mouse and pass clicks to the underlying windows.
I was doing this with Python and Qt (see Python overlay window) but then I switched to Java and Swing. However I'm not able to make the window transparent. I tried this method but it doesn't work, however I think that my system supports the transparency because if I start Screencast-O-Matic (which is in Java), the rectangle is actually transparent.
How can I achieve something like that? (I'm on Linux KDE4)
Why did the Java tutorial How to Create Translucent and Shaped Windows fail to work? Are you using the latest version of Java 6 or Java 7?
In the May/June issue of Java Magazine, there was a tutorial on shaped and transparent windows requiring java 7. You will probably need to sign up for Java magazine in order to read it. See if you can get this to run on your system:
import java.awt.*; //Graphics2D, LinearGradientPaint, Point, Window, Window.Type;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
/**
* From JavaMagazine May/June 2012
* #author josh
*/
public class ShapedAboutWindowDemo {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
//switch to the right thread
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("About box");
//turn of window decorations
frame.setUndecorated(true);
//turn off the background
frame.setBackground(new Color(0,0,0,0));
frame.setContentPane(new AboutComponent());
frame.pack();
//size the window
frame.setSize(500, 200);
frame.setVisible(true);
//center on screen
frame.setLocationRelativeTo(null);
}
}
);
}
private static class AboutComponent extends JComponent {
public void paintComponent(Graphics graphics) {
Graphics2D g = (Graphics2D) graphics;
//create a translucent gradient
Color[] colors = new Color[]{
new Color(0,0,0,0)
,new Color(0.3f,0.3f,0.3f,1f)
,new Color(0.3f,0.3f,0.3f,1f)
,new Color(0,0,0,0)};
float[] stops = new float[]{0,0.2f,0.8f,1f};
LinearGradientPaint paint = new LinearGradientPaint(
new Point(0,0), new Point(500,0),
stops,colors);
//fill a rect then paint with text
g.setPaint(paint);
g.fillRect(0, 0, 500, 200);
g.setPaint(Color.WHITE);
g.drawString("My Killer App", 200, 100);
}
}
}
If you're using Java 6, you need to make use of the private API AWTUtilities. Check out the Java SE 6 Update 10 API for more details
EXAMPLE
This is a bit of quick hack, but it gets the idea across
public class TransparentWindow {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
MyFrame frame = new MyFrame();
frame.setUndecorated(true);
String version = System.getProperty("java.version");
if (version.startsWith("1.7")) {
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice graphicsDevice = ge.getDefaultScreenDevice();
System.out.println("Transparent from under Java 7");
/* This won't run under Java 6, uncomment if you are using Java 7
System.out.println("isPerPixelAlphaTranslucent = " + graphicsDevice.isWindowTranslucencySupported(GraphicsDevice.WindowTranslucency.PERPIXEL_TRANSLUCENT));
System.out.println("isPerPixelAlphaTransparent = " + graphicsDevice.isWindowTranslucencySupported(GraphicsDevice.WindowTranslucency.PERPIXEL_TRANSPARENT));
System.out.println("isPerPixelAlphaTranslucent = " + graphicsDevice.isWindowTranslucencySupported(GraphicsDevice.WindowTranslucency.TRANSLUCENT));
*/
frame.setBackground(new Color(0, 0, 0, 0));
} else if (version.startsWith("1.6")) {
System.out.println("Transparent from under Java 6");
System.out.println("isPerPixelAlphaSupported = " + supportsPerAlphaPixel());
setOpaque(frame, false);
}
frame.setSize(400, 400);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static class MyFrame extends JFrame {
public MyFrame() throws HeadlessException {
setContentPane(new MyContentPane());
setDefaultCloseOperation(EXIT_ON_CLOSE);
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
dispose();
}
}
});
}
}
public static class MyContentPane extends JPanel {
public MyContentPane() {
setLayout(new GridBagLayout());
add(new JLabel("Hello, I'm a transparent frame under Java " + System.getProperty("java.version")));
setOpaque(false);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.BLUE);
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f));
g2d.fillRoundRect(0, 0, getWidth() - 1, getHeight() - 1, 20, 20);
}
}
public static boolean supportsPerAlphaPixel() {
boolean support = false;
try {
Class<?> awtUtilsClass = Class.forName("com.sun.awt.AWTUtilities");
support = true;
} catch (Exception exp) {
}
return support;
}
public static void setOpaque(Window window, boolean opaque) {
try {
Class<?> awtUtilsClass = Class.forName("com.sun.awt.AWTUtilities");
if (awtUtilsClass != null) {
Method method = awtUtilsClass.getMethod("setWindowOpaque", Window.class, boolean.class);
method.invoke(null, window, opaque);
// com.sun.awt.AWTUtilities.setWindowOpaque(this, opaque);
// ((JComponent) window.getContentPane()).setOpaque(opaque);
}
} catch (Exception exp) {
}
}
public static void setOpacity(Window window, float opacity) {
try {
Class<?> awtUtilsClass = Class.forName("com.sun.awt.AWTUtilities");
if (awtUtilsClass != null) {
Method method = awtUtilsClass.getMethod("setWindowOpacity", Window.class, float.class);
method.invoke(null, window, opacity);
}
} catch (Exception exp) {
exp.printStackTrace();
}
}
public static float getOpacity(Window window) {
float opacity = 1f;
try {
Class<?> awtUtilsClass = Class.forName("com.sun.awt.AWTUtilities");
if (awtUtilsClass != null) {
Method method = awtUtilsClass.getMethod("getWindowOpacity", Window.class);
Object value = method.invoke(null, window);
if (value != null && value instanceof Float) {
opacity = ((Float) value).floatValue();
}
}
} catch (Exception exp) {
exp.printStackTrace();
}
return opacity;
}
}
On Windows 7 it produces
Under Java 6
Under Java 7
i guess this will work,i already tried it..to make a JFrame or a window transparent you need to undecorate Undecorated(true) the frame first. Here is sample code :
import javax.swing.*;
import com.sun.awt.AWTUtilities;
import java.awt.Color;
class transFrame {
private JFrame f=new JFrame();
private JLabel msg=new JLabel("Hello I'm a Transparent Window");
transFrame() {
f.setBounds(400,150,500,500);
f.setLayout(null);
f.setUndecorated(true); // Undecorates the Window
f.setBackground(new Color(0,0,0,25)); // fourth index decides the opacity
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
msg.setBounds(150,250,300,25);
f.add(msg);
f.setVisible(true);
}
public static void main(String[] args) {
new transFrame();
}
}
The only problem is you need to add your own code for close and minimize using buttons.
If you want to do it on your own, without using a external lib, you could start a thread that performs :
set the transparent window invisible
make a Screenshot of the desktop
put this screenshot as background image of your window
Or you could use JavaFX
I was also facing the same problem. After hours of searching, I finally found the problem! These are the lines you must write, if you want to make a transparent JFrame:
public void enableTransparentWindow(float opacity) {
GraphicsEnvironment ge =
GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gd = ge.getDefaultScreenDevice();
f.setLocationRelativeTo(null);
f.setBackground(new Color(0, 0, 0));
//If translucent windows aren't supported, exit.
f.setUndecorated(true);
if (!gd.isWindowTranslucencySupported(TRANSLUCENT)) {
System.err.println(
"Translucency is not supported");
System.exit(0);
}
f.setOpacity(opacity);
}
Don't forget to call the setVisible() method after this code.
Happy Coding!
I've a class which extends JPanel. I overwrote protected void paintComponent(Graphics g).
There is a variable which has to be recalculated when the panel's dimensions change. How do I do that in a proper way?
Like Adam Paynter suggested, you can also add an inner class to your code, like this:
class ResizeListener extends ComponentAdapter {
public void componentResized(ComponentEvent e) {
// Recalculate the variable you mentioned
}
}
The code you have entered between the innermost brackets will be executed everytime the component get resized.
Then you add this listener to your component with
myJPanel.addComponentListener(new ResizeListener());
You can get your component by using e.getComponent(). This way you can call any method of your component from inside the inner class like
e.getComponent().getWeight();
I suppose you could override the various setSize and resize methods and perform the calculation there. However, you may not find all the places where the size can be changed. You may want to have your class implement ComponentListener and simply listen to itself for resize events.
Warning: I am not a Swing expert.
Warning: I have not compiled this code.
public class MyJPanel extends JPanel implements ComponentListener {
public MyJPanel() {
this.addComponentListener(this);
}
public void paintComponent(Graphics g) {
// Paint, paint, paint...
}
public void componentResized(ComponentEvent e) {
// Perform calculation here
}
public void componentHidden(ComponentEvent e) {}
public void componentMoved(ComponentEvent e) {}
public void componentShown(ComponentEvent e) {}
}
If I understand the question correctly then you should read the section from the Swing tutorial on How to Write a Component Listener which shows you how to listen for a change in a components size.
If the calculation isn't time consuming, I would just re-calculate the value each time in paintComponent().
Otherwise, you can save a value that is the size of the component and check it against the new size in paintComponent. If the size changed, then recalculate, otherwise don't.
private Dimension size;
protected void paintComponent(Graphics g){
if (!size.equals(getSize())){
size = getSize();
// recalculate value
}
}
Or, you can do the calculation on a resize event.
//in the constructor add the line
addComponentListener(resizeListener);
private ComponentListener resizeListener = new ComponentAdapter(){
public void componentResized(ActionEvent e){
// recalculate value
}
};
The simplest way is to implement a ComponentListener:
myjpanel.addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
//recalculate variable
}
});
Here, I have used a ComponentAdapter because I only intend on overriding componentResized().
Here's what I use (where CoordinatePlane is a JPanel):
I'm not an expert
public CoordinatePlane() {
setBackground(Color.WHITE);
this.addComponentListener(new ComponentAdapter(){
public void componentResized(ComponentEvent e) {
//YOUR CODE HERE
}
});
}
It resizes automatically if it's
inside a BorderLayout panel and
put there as BorderLayout.CENTER component.
If it doesn't work, you probably have forgotten one of these two.
This simple example is drawing a red circle in the resized frame....
import java.awt.*;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import javax.swing.*;
import java.awt.geom.*;
public class RedCircle extends JFrame implements ComponentListener {
int getWidth;
int getHeight;
public RedCircle() {
super("Red Circle");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
addComponentListener(this);
pack();
setVisible(true);
}
public void componentResized(ComponentEvent e) {
getWidth = e.getComponent().getWidth();
getHeight = e.getComponent().getHeight();
Panel pane = new Panel(getWidth,getHeight);
add(pane);
}
public static void main(String[] args) {
RedCircle rc = new RedCircle();
}
public void componentMoved(ComponentEvent e) {
}
public void componentShown(ComponentEvent e) {
}
public void componentHidden(ComponentEvent e) {
}
}
class Panel extends JPanel {
int panelWidth;
int panelHeight;
public Panel(Integer getWidth,Integer getHeight) {
panelWidth = getWidth;
panelHeight = getHeight;
}
public void paintComponent(Graphics comp) {
super.paintComponent(comp);
Graphics2D comp2D = (Graphics2D) comp;
int realWidth = panelWidth - 17;
int realHeight = panelHeight - 40;
float Height = (realHeight);
float Width = (realWidth);
// draw the Red Circle
comp2D.setColor(Color.red);
Ellipse2D.Float redCircle = new Ellipse2D.Float(0F, 0F, Width, Height);
comp2D.fill(redCircle);
}
}