This is my code which would create a window that would takeover my full screen and would have a background colour of blue and would print "Hello World" in black, but when I run this application my screen freezes and the text "Hello World" comes out and after 5 seconds the thread terminates, but background colour does not changes and the screen becomes like this!
Can anyone point out what am I doing wrong?
This is my Screen class:
import java.awt.DisplayMode;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Window;
import javax.swing.JFrame;
public class Screen {
private GraphicsDevice graphic_card;
public Screen() {
GraphicsEnvironment environment = GraphicsEnvironment.getLocalGraphicsEnvironment();
graphic_card = environment.getDefaultScreenDevice();
}
public void set_full_screen(DisplayMode mode, JFrame window) {
window.setUndecorated(true);
window.setResizable(false);
graphic_card.setFullScreenWindow(window);
if ((mode != null) && (graphic_card.isDisplayChangeSupported())) {
try {
graphic_card.setDisplayMode(mode);
} catch (Exception exception) {
// No handling of exception
}
}
}
public Window getFullScreenWindow() {
return graphic_card.getFullScreenWindow();
}
public void restore_screen() {
Window window = graphic_card.getFullScreenWindow();
if (window != null) {
window.dispose();
}
graphic_card.setFullScreenWindow(null);
}
}
And this my Mayank class:
import java.awt.Color;
import java.awt.DisplayMode;
import java.awt.Font;
import java.awt.Graphics;
import javax.swing.JFrame;
public class Mayank extends JFrame {
public static void main(String[] args) {
DisplayMode display_mode = new DisplayMode(800, 600, 16, DisplayMode.REFRESH_RATE_UNKNOWN);
Mayank mayank = new Mayank();
mayank.run(display_mode);
}
public void run(DisplayMode display_mode) {
setBackground(Color.BLUE);
setForeground(Color.BLACK);
setFont(new Font("Arial", Font.PLAIN, 24));
Screen game_screen = new Screen();
try {
game_screen.set_full_screen(display_mode, this);
try {
Thread.sleep(5000);
} catch (Exception exception) {
// No handling of exception
}
} finally {
game_screen.restore_screen();
}
}
public void paint(Graphics g) {
g.drawString("Hello World!!", 200, 200);
}
}
I don't know where you got this code, but it is so wrong.
I created a GUI that displays a blue background for 5 seconds, then changes the background to white.
Here's the GUI.
All Swing applications must start with a call to the SwingUtilities invokeLater method to put the creation and execution of Swing components on the Event Dispatch thread (EDT).
Always draw on a JPanel. Never draw directly on a JFrame.
Your timer blocked the EDT. You must put timing and update events in a separate thread. When you change the GUI from a separate thread, you must execute the invokeLater method to ensure the update of the drawing panel is done on the EDT.
Here's the code.
package com.ggl.testing;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Manyak implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Manyak());
}
#Override
public void run() {
JFrame frame = new JFrame("Hello Word Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
DrawingPanel drawingPanel = new DrawingPanel();
frame.add(drawingPanel);
frame.pack();
frame.setVisible(true);
new Thread(new Delay(drawingPanel)).start();
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = -685508126882892538L;
private Color color;
public DrawingPanel() {
this.setPreferredSize(new Dimension(400, 400));
this.color = Color.BLUE;
}
public void setColor(Color color) {
this.color = color;
this.repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(color);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.BLACK);
g.setFont(new Font("Arial", Font.PLAIN, 24));
g.drawString("Hello World!", 120, 200);
}
}
public class Delay implements Runnable {
private DrawingPanel drawingPanel;
public Delay(DrawingPanel drawingPanel) {
super();
this.drawingPanel = drawingPanel;
}
#Override
public void run() {
try {
Thread.sleep(5000L);
} catch (InterruptedException e) {
}
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
drawingPanel.setColor(Color.WHITE);
}
});
}
}
}
Related
I want to make a little rain program in swing, but for some reason I cannot repaint the panel from another class. I tried using an inner class for the panel this time, but it doesn't seem to work with repainting it from another class/thread. Does someone know why?
sscce:
import javax.swing.JPanel;
import javax.swing.Timer;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
public class UI extends JFrame {
public static void main(String[] args) {
UI myProgram = new UI();
myProgram.setVisible(true);
}
public UI() {
this.setSize(new Dimension(500,300));
this.setBackground(Color.WHITE);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
UserPanel p = new UserPanel(this);
}
public class UserPanel extends JPanel implements ActionListener {
private Timer time = new Timer(1, this);
private UI myFrame;
public UserPanel(UI myFrame) {
this.myFrame = myFrame;
this.setSize(myFrame.getSize());
time.start();
}
#Override
public void actionPerformed(ActionEvent e) {
repaint();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("painting");
g.setColor(Color.BLACK);
g.fillRect(this.getWidth()/2, this.getHeight()/2, 50,50);
}
}
}
UI Class (with inner class JPanel):
package Rain;
import javax.swing.JPanel;
import javax.swing.Timer;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.JFrame;
public class UI extends JFrame {
public UI() {
this.setSize(new Dimension(500,300));
this.setBackground(Color.WHITE);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
UserPanel p = new UserPanel(this);
}
private class UserPanel extends JPanel implements ActionListener {
private Timer time = new Timer(1, this);
private UI myFrame;
private ArrayList<Raindrop> rain = new ArrayList<Raindrop>();
private static final int AMOUNT = 50;
private Random rand = new Random();
public UserPanel(UI myFrame) {
this.myFrame = myFrame;
this.setSize(myFrame.getSize());
for(int i = 0; i < AMOUNT; i++) {
createRain();
}
new Painter(this);
time.start();
}
public void createRain() {
float distance = rand.nextFloat() * 90 + 10;
int x = rand.nextInt(this.getWidth());
int y = 100;
rain.add(new Raindrop(distance,x,y));
}
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("tick");
for(Raindrop r : rain) {
r.fall();
}
}
public void paintComponent(Graphics g) {
System.out.println("painting");
g.setColor(this.getBackground());
g.fillRect(0,0,this.getWidth(),this.getHeight());
for(Raindrop r : rain) {
r.draw(g);
}
}
}
}
Painter:
package Rain;
import javax.swing.JPanel;
public class Painter extends Thread {
private JPanel p;
public Painter(JPanel p) {
this.p = p;
this.start();
}
public void run() {
while(true) {
System.out.println("trying to paint..");
p.repaint();
}
}
}
Console Output:
trying to paint..
tick
trying to paint..
tick
...
Expected Output:
trying to paint..
painting
tick
trying to paint..
...
The thread does work but it never calls the paintComponent(Graphics g) function in the panel
All Swing applications must run on their own thread, called EDT. (Hopefully, you start your application by calling SwingUtilities#invokelater method). So, repainting a component outside of Event Dispatch Thread is really bad bad (bad) idea. Instead of creating new Thread, repaint the component inside javax.swing.Timer's action listener since it will run in EDT.
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("tick");
for(Raindrop r : rain) {
r.fall();
}
repaint(); //repaint in EDT
}
Also, when you #Override paintComponent method, always start by calling super.paintComponent(g);
public void paintComponent(Graphics g) {
super.paintComponent(g);//let component get painted normally
System.out.println("painting");
g.setColor(this.getBackground());
g.fillRect(0,0,this.getWidth(),this.getHeight());
for(Raindrop r : rain) {
r.draw(g);
}
}
UPDATE after your SSCCE
In order a component to get painted, it must have a parent. You UserPanel p = new UserPanel(this); but you never add it to the frame:
UserPanel p = new UserPanel(this);
getContentPane().setLayout(new BorderLayout());
getContentPane().add(p);
The complete SSCCE:
public class UI extends JFrame {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> { //Run in EDT
UI myProgram = new UI();
myProgram.setVisible(true);
});
}
public UI() {
super("title");//call super for frame
this.setSize(new Dimension(500, 300));
this.setBackground(Color.WHITE);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
UserPanel p = new UserPanel(this);
//Use border layout to make p fit the whole frame
getContentPane().setLayout(new BorderLayout());
getContentPane().add(p, BorderLayout.CENTER);
}
public class UserPanel extends JPanel implements ActionListener {
private Timer time = new Timer(1, this);
private UI myFrame;
public UserPanel(UI myFrame) {
this.myFrame = myFrame;
this.setSize(myFrame.getSize());
time.start();
}
#Override
public void actionPerformed(ActionEvent e) {
repaint();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("painting");
g.setColor(Color.BLACK);
g.fillRect(this.getWidth() / 2, this.getHeight() / 2, 50, 50);
}
}
}
Don't ignore the SwingUtilities.invokeLater.
I am making a Game in Java, currently the set background doesn't work, but the setforeground works. Here is my code for the main class:
import java.awt.*;
import javax.swing.*;
public class Sam extends JFrame{
public static void main(String[] args) {
DisplayMode dm = new DisplayMode(1920, 1080, 32, 60);
Sam s = new Sam();
s.run(dm);
}
public void run(DisplayMode dm){
this.setBackground(Color.PINK);
this.setForeground(Color.BLACK);
setFont(new Font("Arial", Font.PLAIN, 24));
Screen s = new Screen();
try{
s.setFullScreen(dm, this);
try{
Thread.sleep(5000);
}catch(Exception ex){ex.printStackTrace();}
}finally{
s.restoreScreen();
}
}
public void paint(Graphics g){
g.drawString("THIS IS GUNNA BE AWESOME", 200, 200);
}
}
here is the code for my Screen class:
import java.awt.*;
import javax.swing.*;
public class Screen {
private GraphicsDevice vc;
public Screen(){
GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
vc = env.getDefaultScreenDevice();
}
public void setFullScreen(DisplayMode dm, JFrame window){
window.setUndecorated(true);
window.setResizable(false);
vc.setFullScreenWindow(window);
if(dm != null && vc.isDisplayChangeSupported()){
try{
vc.setDisplayMode(dm);
}catch(Exception ex){ex.printStackTrace();}
}
}
public Window getFullScreenWindow(){
return vc.getFullScreenWindow();
}
public void restoreScreen(){
Window w = vc.getFullScreenWindow();
if(w != null){
w.dispose();
}
vc.setFullScreenWindow(null);
}
}
JFrame is a composite component, it's actually made up of a number of other components
(From How to Use Root Panes)
So, when you call setBackground, you're only changing the background color of the underlying frame, not the contentPane which resides on it.
Instead, you should be using getContent().setBackground(...).
"But why does setForegound work" I here you ask?
That's because you've overriding paint of JFrame, which is pre-configured with the foreground property of the frame (and not the contentPane)
Now, having said all that...
You should avoid extending from top level containers like JFrame, for these types of reasons AND...
You shouldn't override paint of them either. You should start with a component, extending from something like JPanel and override it's paintComponent method.
Have a look at:
How can I set in the midst?
Graphics rendering in title bar
Java JFrame .setSize(x, y) not working?
How to get the EXACT middle of a screen, even when re-sized
for things that go wrong when you override paint of top level containers, and also, they aren't double buffered, so updating them causes flickering
Example...
import java.awt.Color;
import java.awt.DisplayMode;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
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 javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Sam extends JFrame {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
DisplayMode dm = new DisplayMode(1920, 1080, 32, 60);
Sam s = new Sam();
s.run(dm);
}
});
}
public Sam() {
add(new MainView());
}
public void run(DisplayMode dm) {
Screen s = new Screen();
s.setFullScreen(dm, this);
Timer timer = new Timer(5000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
s.restoreScreen();
}
});
timer.start();
}
public class MainView extends JPanel {
public MainView() {
this.setBackground(Color.PINK);
this.setForeground(Color.BLACK);
setFont(new Font("Arial", Font.PLAIN, 24));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
String text = "THIS IS GUNNA BE AWESOME";
FontMetrics fm = g.getFontMetrics();
int x = (getWidth() - fm.stringWidth(text)) / 2;
int y = ((getHeight() - fm.getHeight()) / 2) + fm.getAscent();
g.drawString(text, x, y);
}
}
public class Screen {
private GraphicsDevice vc;
public Screen() {
GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
vc = env.getDefaultScreenDevice();
}
public void setFullScreen(DisplayMode dm, JFrame window) {
window.setUndecorated(true);
window.setResizable(false);
vc.setFullScreenWindow(window);
if (dm != null && vc.isDisplayChangeSupported()) {
try {
vc.setDisplayMode(dm);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
public Window getFullScreenWindow() {
return vc.getFullScreenWindow();
}
public void restoreScreen() {
Window w = vc.getFullScreenWindow();
if (w != null) {
w.dispose();
}
vc.setFullScreenWindow(null);
}
}
}
I have this code,
import java.awt.*;
import java.applet.*;
public class FirstApplet extends Applet
{
int len;
char ch;
String msg="Hello World ";
public void init()
{
setBackground(Color.CYAN);
setForeground(Color.WHITE);
}
public void start()
{
System.out.println("Inside Start");
repaint();
}
public void paint(Graphics g)
{
System.out.println("Inside paint");
g.drawString(msg,200,200);
}
}
It outputs a CYAN coloured background with Hello World on it.And on the console(cmd),it outputs-
Inside Start
Inside paint
Now if I modify the code to this-
import java.awt.*;
import java.applet.*;
public class FirstApplet extends Applet
{
String msg="Hello World ";
int len;
char ch;
public void init()
{
setBackground(Color.CYAN);
setForeground(Color.WHITE);
}
public void start()
{
System.out.println("Inside Start");
for(;;)
{
repaint();
}
}
public void paint(Graphics g)
{
System.out.println("Inside paint");
g.drawString(msg,200,200);
}
}
It outputs a white coloured screen with no text on it,and on the console it just outputs-
Inside Start
I am unable to understand the output of second program,Although I am calling the repaint() inside the for loop every time yet why the colour of the applet window is not changing to CYAN colour and why its not printing "Inside paint" on the console?Can somebody please help me out.
You're tying up the GUI's event thread with your infinite loop, so that although repaint() is being called, the GUI's event thread is unable to act on it. Consider using a Swing Timer or a background thread instead.
For example, and continuing with your 1890's Applet example:
import java.applet.Applet;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.*;
public class PaintEg extends Applet {
String msg = "Hello World ";
int len;
char ch;
public void init() {
setBackground(Color.CYAN);
setForeground(Color.WHITE);
}
public void start() {
System.out.println("Inside Start");
new Thread(new Runnable() {
public void run() {
for (;;) {
repaint();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
public void paint(Graphics g) {
System.out.println("Inside paint");
g.drawString(msg, 10, 20);
}
}
Better maybe is a Swing example that uses a Swing Timer and performs basic animation:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class PaintEg2 extends JPanel {
private static final int PREF_W = 400;
private static final int PREF_H = PREF_W;
private static final int TIMER_DELAY = 30;
private String msg = "Hello World ";
private int msgX = 0;
private int msgY = 0;
public PaintEg2() {
setBackground(Color.CYAN);
setForeground(Color.WHITE);
setFont(new Font(Font.SANS_SERIF, Font.BOLD, 20));
new Timer(TIMER_DELAY, new TimerListener()).start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawString(msg, msgX, msgY);
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
msgX++;
msgY++;
repaint();
}
}
private static void createAndShowGui() {
PaintEg2 mainPanel = new PaintEg2();
JFrame frame = new JFrame("PaintEg2");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Note that in your code repaint() is being called and is being executed, but the paint manager is unable to act on this because it does so on the GUI's event thread. If the GUI's event thread is tied up, no painting can be done.
For more on how painting is done in Swing and AWT, please read Painting in AWT and Swing
This is a program that goes full screen for five seconds then go back to normal. What i am trying to do is change the back ground color to yellow. I tried getContentPane().setBackground(Color.YELLOW) but it still does not work.
package gamedev;
import java.awt.Color;
import java.awt.DisplayMode;
import java.awt.Font;
import java.awt.Graphics;
import java.io.PrintWriter;
import javax.swing.JFrame;
public class GameDev extends JFrame {
public static void main(String[] arg) {
DisplayMode dm = new DisplayMode(800, 600, 16, DisplayMode.REFRESH_RATE_UNKNOWN);
GameDev b = new GameDev();
b.getContentPane().setBackground(Color.yellow);
b.run(dm);
}
public void run(DisplayMode dm) {
setBackground(Color.YELLOW);
setForeground(Color.red);
setFont(new Font("Arial", Font.PLAIN, 25));
Screen s = new Screen();
try {
s.setFullScreen(dm, this);
try {
Thread.sleep(5000);
} catch (Exception ex) {
}
} finally {
s.restoreScreen();
}
}
public void paint(Graphics g) {
g.drawString("You know you love me home boy.", 200, 200);
}
}
and second class
package gamedev;
import java.awt.DisplayMode;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Window;
import javax.swing.JFrame;
public class Screen {
GraphicsDevice videoCard;
public Screen() {
GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
videoCard = env.getDefaultScreenDevice(); // acces computer screen
}
public void setFullScreen(DisplayMode dm, JFrame window) {
window.setUndecorated(true);
window.setResizable(false);
videoCard.setFullScreenWindow(window);
if (dm != null && videoCard.isDisplayChangeSupported()) {
try {
videoCard.setDisplayMode(dm);
} catch (Exception ex) {
}
}
}
public Window getFullScreenWindow() {
return videoCard.getFullScreenWindow(); //returns ur window
}
public void restoreScreen() {
Window w = videoCard.getFullScreenWindow();
if (w != null) {
w.dispose();
}
videoCard.setFullScreenWindow(null); // takes away from full screen.
}
}
Basically, you are blocking the Event Dispatching Thread, preventing from processing any new incoming events, including paint requests. Instead, you should use something like a javax.swing.Timer to wait in the background until the required time has elapsed, then restore the screen.
The javax.swing.Timer will provide notification after the specified delay within the context of the Event Dispatching Thread, making it safe to use when you need to make modifications to the UI.
Take a closer look at Concurrency in Swing for more details...
import java.awt.Color;
import java.awt.DisplayMode;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.Timer;
public class GameDev extends JFrame {
public static void main(String[] arg) {
DisplayMode dm = new DisplayMode(800, 600, 16, DisplayMode.REFRESH_RATE_UNKNOWN);
GameDev b = new GameDev();
b.getContentPane().setBackground(Color.yellow);
b.run(dm);
}
public void run(DisplayMode dm) {
setBackground(Color.YELLOW);
setForeground(Color.red);
setFont(new Font("Arial", Font.PLAIN, 25));
final Screen s = new Screen();
s.setFullScreen(dm, this);
Timer timer = new Timer(5000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
s.restoreScreen();
}
});
timer.setRepeats(false);
timer.start();
}
public void paint(Graphics g) {
super.paint(g);
g.drawString("You know you love me home boy.", 200, 200);
}
public class Screen {
GraphicsDevice videoCard;
public Screen() {
GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
videoCard = env.getDefaultScreenDevice(); // acces computer screen
}
public void setFullScreen(DisplayMode dm, JFrame window) {
window.setUndecorated(true);
window.setResizable(false);
videoCard.setFullScreenWindow(window);
if (dm != null && videoCard.isDisplayChangeSupported()) {
try {
videoCard.setDisplayMode(dm);
} catch (Exception ex) {
}
}
}
public Window getFullScreenWindow() {
return videoCard.getFullScreenWindow(); //returns ur window
}
public void restoreScreen() {
Window w = videoCard.getFullScreenWindow();
if (w != null) {
w.dispose();
}
videoCard.setFullScreenWindow(null); // takes away from full screen.
}
}
}
You should also take a look at Performing Custom Painting, as your paint method may be preventing it from actually painting the background
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.