How to load two extended JPanel class to one JFrame? - java

I'm going to make a game with Java, my game will have a menu. the menu is having a background, and 2 JLabel objects. I've make them on separate class, which is passed to one JFrame. And my problem is, I've load 2 of them on a single frame, but one of them always hidden by another.
this is the code:
JFrame class
#SuppressWarnings("serial")
public class Sistem extends JFrame{
private final int lebar=954;
private final int tinggi=540;
private Image bg;
File gbr=new File("res/a.jpg");
public Sistem(){
this.setTitle("Unknown man Unkown power");
this.setSize(new Dimension(lebar,tinggi));
this.setFocusable(true);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setResizable(false);
this.setContentPane(new Ngrep());
//this.setContentPane(new Menu());
this.setVisible(true);
//loadfont();
//loadbg();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
new Sistem();
}
});
}
}
background class
#SuppressWarnings("serial")
public class Ngrep extends JPanel{
private int l=954;
private int t=540;
private BufferedImage bg;
File gbr=new File("res/a.jpg");
public Ngrep(){
loadbg();
}
private void loadbg() {
// TODO Auto-generated method stub
try {
bg=ImageIO.read(gbr);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
#Override
public void paint(Graphics g) {
// TODO Auto-generated method stub
super.paint(g);
g.drawImage(bg, 0, 0, l, t, null);
}
}
menu class
#SuppressWarnings("serial")
public class Menu extends JPanel implements Runnable,KeyListener{
private int l=954;
private int t=540;
JLabel menu1=new JLabel("MULAI BARU");
JLabel menu2=new JLabel("KELUARRR");
private File fo=new File("res/Mawns.ttf");
JLayeredPane p=new JLayeredPane();
public Menu(){
loadfont();
this.add(menu1);
this.add(menu2);
}
public void loadfont(){
try {
FileInputStream fi=new FileInputStream(fo);
Font f=Font.createFont(Font.TRUETYPE_FONT, fi).deriveFont(Font.TRUETYPE_FONT, 30);
GraphicsEnvironment ge=GraphicsEnvironment.getLocalGraphicsEnvironment();
ge.registerFont(f);
menu1.setFont(f);
menu2.setFont(f);
} catch (Exception ex) {
// TODO Auto-generated catch block
ex.printStackTrace();
}
p.setLayout(new GridLayout(2, 3));
menu1.setBounds(0, 0, getWidth(), getHeight());
menu2.setBounds(0, 0+menu1.getHeight(), getWidth(), getHeight());
p.add(menu1, 2);
p.add(menu2, 2);
}
}
What I want is the menu is in front of background but background still can be seen. and how to arrange the JLabel that I've created to center down of the screen.
How can I achieve the required layout?

public class Ngrep extends JPanel{
Note that since Ngrep is a JPanel you can add components directly to it, making the Menu class redundant.
Something like seen in this SSCCE.
Note that I ended up making so many changes so fast I could not be bothered explicitly documenting most of them. Look over the code carefully, check it against your original code, check the Java Docs, and if there is any change you do not understand, ask me.
import java.awt.*;
import java.awt.image.*;
import java.awt.event.*;
import javax.swing.*;
import java.net.URL;
import javax.imageio.ImageIO;
#SuppressWarnings("serial")
public class Sistem extends JFrame {
public Sistem() {
this.setTitle("Unknown man Unkown power");
this.setFocusable(true);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setContentPane(new Ngrep());
this.setResizable(false);
this.pack();
this.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Sistem();
}
});
}
}
#SuppressWarnings("serial")
class Ngrep extends JPanel {
private int l = 375;
private int t = 150;
private BufferedImage bg;
JLabel menu1 = new JLabel("MULAI BARU");
JLabel menu2 = new JLabel("KELUARRR");
public Ngrep() {
this.add(menu1);
this.add(menu2);
try {
Font f = new Font(Font.MONOSPACED, Font.ITALIC, 30);
menu1.setFont(f);
menu1.setForeground(Color.RED);
menu2.setFont(f);
menu2.setForeground(Color.RED);
URL url = new URL("http://i.stack.imgur.com/OVOg3.jpg");
bg = ImageIO.read(url);
} catch (Exception ex) {
ex.printStackTrace();
}
setLayout(new GridLayout(2, 3));
add(menu1);
add(menu2);
}
public Dimension getPreferredSize() {
return new Dimension(l, t);
}
/*
* For a JComponent, override paintComponent rather than paint
*/
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
// a JPanel IS AN ImageObserver
g.drawImage(bg, 0, 0, getWidth(), getHeight(), this);
}
}

Start by using JFrame#add instead of JFrame#setContentPane, unless you intend to add more components to that (content) pane.
By default JFrame uses a BorderLayout for its LayoutManager. You will need to either change it to something you prefer to use OR add each component to an appropriate position within the BorderLayout
See Laying Out Components Within a Container for more details

solved, because I've found that i use paint() instead of paintComponent().
like this:
#Override
public void paint(Graphics g) {
// TODO Auto-generated method stub
super.paint(g);
g.drawImage(bg, 0, 0, l, t, this);
}
to this :
#Override
public void paintComponent(Graphics g) {
// TODO Auto-generated method stub
super.paintComponents(g);
g.drawImage(bg, 0, 0, l, t, this);
}

Related

Is there a way to repaint graphics after using super.paint(g)?

so I have a question, lets say I create a rectangle in Java using the paint method, after a 100 ms delay I do super.paint(g), this clears the rectangle previously shown, is there a way to make it re appear?
Thanks!
An example of what I'm talking about is down below, what this program is meant to do is whenever I hold down mouse button 1, it creates a rectangle that goes down and than disapears after mouse button 1 is off. The problem is whenever I hold down mouse button 1 again, the rectangle doesn't appear.
First class:
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import javax.swing.JFrame;
import javax.swing.Timer;
public class RecoilHelper extends JFrame {
static Timer rs;
static int recoil = 540;
static boolean clearRectangle = false;
/**
* Launch the application.
*/
public static void main(String[] args) {
JNativehookRecoilHelp.main(null);
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
RecoilHelper frame = new RecoilHelper();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public RecoilHelper() {
initialize();
}
/**
* Initialize the contents of the frame.
*/
private void initialize() {
setBounds(0, 0, 1920, 1080);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setUndecorated(true);
setBackground(new Color(1.0f,1.0f,1.0f,0.0f));
setAlwaysOnTop(true);
rs = new Timer(10,(ActionEvent e)->{
repaint();
recoil += 12;
if (recoil>600) {
rs.stop();
}
});
}
public void paint(Graphics g) {
Rectangle r = new Rectangle(960, recoil, 4, 4);
System.out.println(recoil);
super.paintComponents(g);
g.fillRect(
(int)r.getX(),
(int)r.getY(),
(int)r.getWidth(),
(int)r.getHeight()
);
if (clearRectangle) {
super.paint(g);
}
}
}
Second class(tracks mouse button 1 events using JNativehook):
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;
import org.jnativehook.GlobalScreen;
import org.jnativehook.NativeHookException;
import org.jnativehook.mouse.NativeMouseEvent;
import org.jnativehook.mouse.NativeMouseInputListener;
public class JNativehookRecoilHelp implements NativeMouseInputListener {
#Override
public void nativeMouseClicked(NativeMouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void nativeMousePressed(NativeMouseEvent arg0) {
// TODO Auto-generated method stub
System.out.println("Pressed");
RecoilHelper.recoil = 540;
RecoilHelper.rs.start();
}
#Override
public void nativeMouseReleased(NativeMouseEvent arg0) {
// TODO Auto-generated method stub
System.out.println("Released");
RecoilHelper.clearRectangle=true;
}
#Override
public void nativeMouseDragged(NativeMouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void nativeMouseMoved(NativeMouseEvent arg0) {
// TODO Auto-generated method stub
}
public static void main(String[] args) {
GlobalScreen.addNativeMouseListener(new JNativehookRecoilHelp());
LogManager.getLogManager().reset();
// Get the logger for "org.jnativehook" and set the level to off.
Logger logger = Logger.getLogger(GlobalScreen.class.getPackage().getName());
logger.setLevel(Level.OFF);
try {
GlobalScreen.registerNativeHook();
}
catch (NativeHookException ex) {
System.exit(1);
}
}
}
Currently you are attempting to override the paint method of the JFrame, this presents two issues, this first is that a JFrame is a heavyweight component (it has a title bar and a number of associated things that you need to consider) so you may have endless issues, the second issue is that you need to override the paintComponent method of the component that you wish to perform custom painting on.
The solution here is to instead place a JPanel inside the JFrame, and override the paintComponent method of the JPanel.
Below is a working example that creates a new rectangle and adds it to the frame every half second but also keeps the existing rectangles by adding them to a list and drawing each one in the list every time it is repainted.
The main class is simple, and simply adds our CustomJpanel to the JFrame:
public class PaintExample extends JFrame
{
private CustomJPanel customJpanel;
public PaintExample()
{
//Create and add the custom panel to the JFrame
setPreferredSize(new Dimension(400, 300));
customJpanel = new CustomJPanel();
getContentPane().add(customJpanel, java.awt.BorderLayout.CENTER);
pack();
}
public static void main(String args[])
{
//Show the JFrame:
java.awt.EventQueue.invokeLater(new Runnable()
{
public void run()
{
new PaintExample().setVisible(true);
}
});
}
}
And the custom panel class that does our painting:
public class CustomJPanel extends JPanel
{
int x = 0;
int y = 0;
boolean clearRectangle = true;
//This is a list that we use to keep track of all the rectangles to draw/update
ArrayList<Rectangle> rectangleList = new ArrayList<>();
//Start a timer when the panel is created that will update the rectangle location every half second (500ms).
//In your case you would use your recoil timer instead
public CustomJPanel(){
//Create event action
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
add a new rectangle in a different location
x+= 5;
y+= 5;
rectangleList.add(new Rectangle(x,y,10,10));
//Update the panel
repaint();
}
};
//Create and start a repeating event
Timer timer = new Timer(500, taskPerformer);
timer.setRepeats(true);
timer.start();
}
//Here is where it all happens:
#Override
protected void paintComponent(Graphics g)
{
//Call super first to perform normal component painting
super.paintComponent(g);
//Now do your custom painting
if (clearRectangle)
{
//Draw each rectangle in the list
for (Iterator<Rectangle> iterator = rectangleList.iterator(); iterator.hasNext();)
{
Rectangle r = iterator.next();
g.drawRect(r.x, r.y, r.width, r.height);
}
}
}
}
And the window looks like this after a couple so seconds, note how it keeps all previous rectangles:

How to draw a circle on top of an image?

I'm learning how to make java applets, and I'm trying to take an image of an intersection and run a circle across it. I'm able to import the image and have it displayed in the applet, but the circle won't show on top of it. I'm just having it start in the top left and go towards the left of the screen. Any ideas?
import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.TextField;
public class App extends Applet implements Runnable{
int x,y;
boolean running = true;
Image background;
/**
*
*/
private static final long serialVersionUID = -8667920279388305018L;
public void init() {
x = 0;
y = 0;
background = getImage(getCodeBase(), "street.png");
BackGroundPanel bgp = new BackGroundPanel();
bgp.setLayout(new FlowLayout());
bgp.setBackGroundImage(background);
setSize(500,500);
// set the layout of the applet to Border Layout
setLayout(new BorderLayout());
// now adding the panel, adds to the center
// (by default in Border Layout) of the applet
add(bgp);
}
#Override
public void start() {
new Thread(this).start();
}
#Override
public void paint(Graphics g) {
g.setColor(Color.BLACK);
g.fillOval(x, y, 100, 100);
}
#Override
public void run() {
while(running) {
++x;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
repaint();
}
}
````}
class BackGroundPanel extends Panel implements ImageObserver {
/**
*
*/
private static final long serialVersionUID = 1L;
Image backGround;
BackGroundPanel() {
super();
}
public void paint(Graphics g) {
// get the size of this panel (which is the size of the applet),
// and draw the image
g.drawImage(getBackGroundImage(), 0, 0,
(int)getBounds().getWidth(), (int)getBounds().getHeight(), this);
}
public void setBackGroundImage(Image backGround) {
this.backGround = backGround;
}
private Image getBackGroundImage() {
return backGround;
}
}
You can draw everything in paint and not use the panel:
public class App extends Applet implements Runnable{
int x,y;
boolean running = true;
Image background;
/**
*
*/
private static final long serialVersionUID = -8667920279388305018L;
public void init() {
x = 0;
y = 0;
background = getImage(getCodeBase(), "street.png");
setSize(500,500);
// set the layout of the applet to Border Layout
setLayout(new BorderLayout());
}
#Override
public void start() {
new Thread(this).start();
}
#Override
public void paint(Graphics g) {
g.drawImage(background, 0, 0,
(int)getBounds().getWidth(), (int)getBounds().getHeight(), this);
g.setColor(Color.BLACK);
g.fillOval(x, y, 100, 100);
}
#Override
public void run() {
while(running) {
++x;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
repaint();
}
}
Or if you want the panel you add it and draw everything there - you have to move some stuff to the panel though such as the x, y and the image - and you repaint the panel:
public class App extends Applet implements Runnable{
boolean running = true;
BackGroundPanel bgp = new BackGroundPanel();
/**
*
*/
private static final long serialVersionUID = -8667920279388305018L;
public void init() {
bgp.setLayout(new FlowLayout());
bgp.setImage(getImage(getCodeBase(), "street.png"));
setSize(500,500);
// set the layout of the applet to Border Layout
setLayout(new BorderLayout());
// now adding the panel, adds to the center
// (by default in Border Layout) of the applet
add(bgp);
}
#Override
public void start() {
new Thread(this).start();
}
#Override
public void paint(Graphics g) {
}
#Override
public void run() {
while(running) {
++bgp.x;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
bgp.repaint();
}
}
````}
class BackGroundPanel extends Panel implements ImageObserver {
int x,y;
/**
*
*/
private static final long serialVersionUID = 1L;
Image background;
BackGroundPanel() {
super();
x = 0;
y = 0;
}
public void setImage (Image i) {
background=i;
}
public void paint(Graphics g) {
// get the size of this panel (which is the size of the applet),
// and draw the image
g.drawImage(background, 0, 0,
(int)getBounds().getWidth(), (int)getBounds().getHeight(), this);
g.setColor(Color.BLACK);
g.fillOval(x, y, 100, 100);
}
public void setBackGroundImage(Image backGround) {
this.backGround = backGround;
}
private Image getBackGroundImage() {
return backGround;
}
}

JPanel doesn't display all custom made components

I have got a strange problem with displaying components in JFrame.
I have to write my own GUI engine (buttons, textboxes, etc...) without using Swing. Only JFrame/JPanel is allowed to be used.
Let's say I want to place 3 buttons.
My button class:
public class Button extends JPanel implements MouseListener {
Rectangle r = new Rectangle();
String text;
int X,Y,W,H;
public Button(int x, int y, int w, int h, String t)
{
X=x;
Y=y;
W=w;
H=h;
this.setBackground(Color.CYAN);
addMouseListener(this);
r.setSize(w, h);
r.setLocation(x, y);
this.text = t;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
g2d.draw(r);
g2d.drawString(text, X+W/2, Y+H/2);
}
#Override
public void mouseClicked(MouseEvent arg0) {
// TODO Auto-generated method stub
if((arg0.getButton()==1) && r.contains(arg0.getPoint()))
System.out.println(arg0.getPoint().toString());
}
#Override
public void mouseEntered(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mousePressed(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mouseReleased(MouseEvent arg0) {
// TODO Auto-generated method stub
}
}
And in main class I create a JFrame and JPanel. I add to JPanel 3 buttons, and finally JPanel to JFrame, but only the last declared button shows up.
public static void main(String[] args) {
// TODO Auto-generated method stub
JFrame f = new JFrame("Demo");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setBackground(Color.cyan);
JPanel j = new JPanel(new BorderLayout());
j.add(new Button(10,10,100,50,"text"));
j.add(new Button(10,100,100,50,"text2"));
j.add(new Button(300,10,100,50,"text3"));
f.add(j);
f.pack();
f.setSize(640, 400);;
f.setVisible(true);
}
What am I doing wrong?
Your code does not respect the BorderLayout rules. When adding 3 components to the BorderLayout using container without specifying BorderLayout location, they all get added to the default BorderLayout.CENTER spot, and the last one added covers the other 3. Consider using BorderLayout constants when adding components or using another layout manager(s).
Having said this, I think that you're better off completely changing route. Instead consider ...
making your Button class a logical and non-GUI class (i.e., by not having it extend JPanel),
having one single non-Button JPanel hold a List<Button>,
having this single JPanel draw all the Buttons by iterating through the list within its paintComponent method, calling a draw(Graphics g) method that each Button has
have the JPanel interact with the Buttons via a single MouseListener.
This will greatly simplify things.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
public class FooGui extends JPanel {
private static final int PREF_W = 640;
private static final int PREF_H = 400;
private List<MyButton> btnList = new ArrayList<>();
public FooGui() {
addMouseListener(new MyMouse());
}
public void addButton(MyButton btn) {
btnList.add(btn);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (MyButton myButton : btnList) {
myButton.draw(g);
}
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private class MyMouse extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
for (MyButton myButton : btnList) {
if (myButton.getRect().contains(e.getPoint())) {
System.out.println("Text: " + myButton.getText());
}
}
}
}
private static void createAndShowGui() {
FooGui fooGui = new FooGui();
fooGui.addButton(new MyButton(10,10,100,50,"text"));
fooGui.addButton(new MyButton(10,100,100,50,"text2"));
fooGui.addButton(new MyButton(300,10,100,50,"text3"));
JFrame frame = new JFrame("FooGui");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(fooGui);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
class MyButton {
private static final Color BK = Color.CYAN;
private static final Color TEXT_COLOR = Color.BLACK;
private int x;
private int y;
private int w;
private int h;
private String text;
private Rectangle rect;
public MyButton(int x, int y, int w, int h, String t) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.text = t;
rect = new Rectangle(x, y, w, h);
}
public void draw(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setColor(BK);
g2.fill(rect);
g2.setColor(TEXT_COLOR);
FontMetrics metrics = g2.getFontMetrics();
Rectangle2D bounds = metrics.getStringBounds(text, g2);
int textX = (int) (x + (w - bounds.getWidth()) / 2);
int textY = (int) (y + (h + bounds.getHeight()) / 2);
g2.drawString(text, textX, textY);
}
public Rectangle getRect() {
return rect;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getW() {
return w;
}
public int getH() {
return h;
}
public String getText() {
return text;
}
}
The purpose of this assigment is to create a GUI, which can be used when for example, there is no implemented JButton, JLabel etc. in a certain device (such as old mobile phones)
Well, that doesn't really answer my question. You addressed the issue of custom painting of buttons and labels, but there is more to a GUI then just painting things.
I also asked if you can use other features of AWT, like MouseListeners, KeyListeners, tabbing, layout managers etc. Because if you can use these features, then there is no reason to completely reinvent the wheel as has been done in the answer by hovercraft.
If all you need to do is extend JPanel and do custom painting for a button or label, then the problem with your code is that you are NOT using the layout managers properly. That is the default layout of a frame is a BorderLayout and you can't add multiple components to the CENTER of the BorderLayout.
The second problem with your posted code is you don't override the getPreferredSize() method of your components. Therefore the size will be 0 and the layout managers can't do their job properly.

Java-Image not drawing to screen

I am trying to draw an image to the screen using Java. The problem is that it does not appear and no errors occur.
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
public class GameClass extends JPanel implements ActionListener, KeyListener{
private BufferedImage image;
public GameClass(){
Timer time = new Timer(15, this);
time.start();
this.addKeyListener(this);
this.setFocusable(true);
}
public void openImage(){
try {
image = ImageIO.read(this.getClass().getResource("spaceship.png"));
} catch (IOException e) {
System.out.println("An error occurred!");
}
}
public void paintComponent(Graphics g){
g.setColor(Color.BLACK);
g.fillRect(0, 0, Main.WW, Main.WH);
g.drawImage(image,Main.WW/2,Main.WH/2,null);
}
public void actionPerformed(ActionEvent e){
repaint();
}
public void keyPressed(KeyEvent e){
}
public void keyReleased(KeyEvent e){
}
public void keyTyped(KeyEvent e){
}
}
You need to call you openImage() method in your constructor.
I renamed this to loadImages() so that this method can handle loading multiple images. I also created a static image loading function.
Note: If you have not already, create a resources/ folder in your projects' src/ folder. This folder will contain your application's assets i.e. text, image, and other data files.
import java.awt.*;
import java.awt.event.*;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*
public class GameClass extends JPanel
implements ActionListener, KeyListener {
private static final long serialVersionUID = -2508183917768834794L;
private Image image;
// Added this, because you did not include it.
private class Main {
static final int WW = 256;
static final int WH = 256;
}
public GameClass() {
Timer time = new Timer(15, this);
time.start();
this.addKeyListener(this);
this.setFocusable(true);
this.loadImages();
}
// Load all required images into instance variables.
public void loadImages() {
image = loadImage("spaceship.png");
}
// Draw the image to the panel.
public void paintComponent(Graphics g) {
g.setColor(Color.BLACK);
g.fillRect(0, 0, Main.WW, Main.WH);
// Dimensions of spaceship
int imgW = image.getWidth(null);
int imgH = image.getHeight(null);
// Dimensions of panel
int pnlW = this.getWidth();
int pnlH = this.getHeight();
// Draw the spaceship in the center of the window.
g.drawImage(image, pnlW/2 - imgW/2, pnlH/2 - imgH/2, null);
}
public void actionPerformed(ActionEvent e) {
repaint();
}
public void keyPressed(KeyEvent e) { }
public void keyReleased(KeyEvent e) { }
public void keyTyped(KeyEvent e) { }
// Static image loader method which utilizes the `ClassLoader`.
public static Image loadImage(String filename) {
try {
return ImageIO.read(GameClass.class.getClassLoader().getResource("resources/" + filename));
} catch (IOException e) {
System.out.println("Error loading image: " + filename);
}
return null;
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
Container panel = new GameClass();
frame.setSize(Main.WW, Main.WH);
frame.setTitle("Spaceship Game");
frame.setContentPane(panel);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}

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