This question already exists:
Java Graphics not showing on Mac even with overriding the paintComponent() method of the JPanel
Closed 2 years ago.
I'm new to Java and am trying to create a program in Swing following an Youtube Tutorial. Everything works fine in Windows but in Mac, the background image doesn't show up but only the menuBar(JLabel). I hope to get background image and JLabels in a same page.
Can someone please help me to continue in the right direction? I will put all the file codes so that understading the issue is better.
Thanks a lot in advance!
[Main.java]
package dynamic_beat_4;
public class Main {
public static final int SCREEN_WIDTH = 1280;
public static final int SCREEN_HEIGHT = 720;
public static void main(String[] args) {
new DynamicBeat();
}
}
[Dynamic Beat.java]
package dynamic_beat_4;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class DynamicBeat extends JFrame {
private Image screenImage;
private Graphics screenGraphic;
private Image introBackground = new ImageIcon(Main.class.getResource("../images/introBackground(Title).jpg"))
.getImage();
private JLabel menuBar = new JLabel(new ImageIcon(Main.class.getResource("../images/menuBar.png")));
public DynamicBeat() {
setUndecorated(true);
setTitle("Dynamic Beat Game");
setSize(Main.SCREEN_WIDTH, Main.SCREEN_HEIGHT);
setResizable(false);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
setBackground(new Color(0, 0, 0, 0));
setLayout(null);
menuBar.setBounds(0, 0, 1280, 30);
add(menuBar);
Music introMusic = new Music("introMusic.mp3", true);
introMusic.start();
}
public void paint(Graphics g) {
screenImage = createImage(Main.SCREEN_WIDTH, Main.SCREEN_HEIGHT);
screenGraphic = screenImage.getGraphics();
screenDraw(screenGraphic);
g.drawImage(screenImage, 0, 0, null);
}
public void screenDraw(Graphics g) {
g.drawImage(introBackground, 0, 0, null);
paintComponents(g);
this.repaint();
}
}
Some major points.
don't call repaint inside of a paint method. This doesn't make sense.
the preferred painting technique of swing is to override paintComponent.
Don't override Window#paint and if you do, call super.paint(g). (As per the linked javadoc.)
Calling paintComponents(g) with the graphics object of a temporary image is unnecessary at best. Swing handles buffering.
I'm amazed this worked on any platform.
I think the easiest fix for this is to create a JPanel.
public DynamicBeat() {
setUndecorated(true);
setTitle("Dynamic Beat Game");
setSize(Main.SCREEN_WIDTH, Main.SCREEN_HEIGHT);
setResizable(false);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
setBackground(new Color(0, 0, 0, 0));
JPanel panel = new JPanel(){
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(introBackground, 0, 0, this);
}
};
panel.setLayout(null);
menuBar.setBounds(0, 0, 1280, 30);
panel.add(menuBar);
setContentPane(panel);
Music introMusic = new Music("introMusic.mp3", true);
introMusic.start();
}
You might want to refer to this painting in awt and swing.
Some further tips, don't extend JFrame, and do your GUI work on the EDT.
Related
import java.awt.Graphics;
import java.awt.Image;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class TowerDefence extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g);
ImageIcon EasyImage = new ImageIcon("WelcomeImage.png");
Image Image = EasyImage.getImage();
Image newimg = Image.getScaledInstance(1000, 700, java.awt.Image.SCALE_SMOOTH);
EasyImage = new ImageIcon(newimg);
EasyImage.paintIcon(this, g, 0, 0);
JButton button = new JButton("Click Button");
button.setBounds(100, 100, 100, 100);
super.add(button);
}
public static void main(String[] args) throws Exception {
TowerDefence T = new TowerDefence();
JFrame frame = new JFrame("Sam");
frame.setVisible(true);
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setBounds(150, 20, 1000, 700);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(T);
}
}
Why is this printing out 2 JButtons? I literally don't know how this works and it would be nice to know. The idea is to literally print out a picture and a button and I can't even get that to work lol. The main issue is I don't know how to use the paint component as I am quite new to java.
First don't use setBounds unless you have a null type layout.
Second don't use a null layout unless you want to handle everything about the layout.
ImageIcon easyImage;
public TowerDefense(){
EasyImage = new ImageIcon("WelcomeImage.png");
Image Image = EasyImage.getImage();
Image newimg = Image.getScaledInstance(1000, 700, java.awt.Image.SCALE_SMOOTH);
JButton button = new JButton("Click Button");
add(button);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
EasyImage.paintIcon(this, g, 0, 0);
}
I've re-arranged the code to try to separate the painting and the initialization. I strongly recommend checking out the link provided by Abra because you'll save quite a few headaches.
Hi I'm following an online tutorial to make a rhythm game in Java and I have a problem with displaying the menubar. When I execute the program, the menubar seems to cover the background image and all I see is the menu bar on the top of the screen and a black screen below it. It seems like the code works on windows but it somehow doesn't work on a mac. Anybody know how to fix this problem?
package dynamic_beat_4;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class DynamicBeat extends JFrame {
private Image screenImage;
private Graphics screenGraphic;
private Image introBackground = new ImageIcon(Main.class.getResource("../images/introBackground.jpg")).getImage();
private JLabel menuBar = new JLabel(new ImageIcon(Main.class.getResource("../images/menuBar.png")));
public DynamicBeat() {
setUndecorated(true);
setTitle("Dynamic Beat");
setSize(Main.SCREEN_WIDTH, Main.SCREEN_HEIGHT);
setResizable(false);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
setBackground(new Color(0, 0, 0, 0));
setLayout(null);
menuBar.setBounds(0, 0, 1280, 30);
add(menuBar);
Music introMusic = new Music("intromusic.mp3", true);
introMusic.start();
}
public void paint(Graphics g) {
screenImage = createImage(Main.SCREEN_WIDTH, Main.SCREEN_HEIGHT);
screenGraphic = screenImage.getGraphics();
screenDraw(screenGraphic);
g.drawImage(screenImage, 0, 0, null);
}
public void screenDraw(Graphics g) {
g.drawImage(introBackground, 0, 0, null);
paintComponents(g);
this.repaint();
}
}
Why do you call it a "menubar"? It is a JLabel. Swing has a specific component called a JMenuBar, so your terminology is very confusing trying to understand what you are really doing.
I see many inconsistencies:
You should NOT be overriding paint().
You should NOT be using a null layout, or setBounds().
You should NOT be invoking paintComponents() directly.
You should NOT be invoking repaint() in a method called from a painting method. Basically you are creating an infinite loop.
You should NOT be using getGraphics(). If you ever need to do custom painting then you just use the Graphics object passed to the painting method.
Not really sure what you are attempting to do but I would suggest that all you need to to use the defrault BorderLayout of the frame. Then the basic code is:
add(menuBar, BorderLayout.PAGE_START);
add(introBackground, BorderLayout.CENTER)
pack();
setVisible( true );
Read the section from the Swing tutorial on How to Use BorderLayout for more information and working examples to get you started.
I am currently using the Applet class to create a simple game. Because there is a flickering effect, I've added double-buffering for Graphics components by creating an off-screen buffer like so:
public class AppletTest extends Applet implements Runnable {
Thread thread;
Image img;
Graphics gfx;
public final int WIDTH = 700, HEIGHT = 500;
public void init() {
this.resize(WIDTH, HEIGHT);
thread = new Thread(this);
thread.start();
img = createImage(WIDTH, HEIGHT); // off-screen buffering
gfx = img.getGraphics();
}
public void draw(Graphics g) {
gfx.setColor(Color.BLACK);
gfx.fillRect(0, 0, WIDTH, HEIGHT);
gfx.setColor(Color.WHITE);
gfx.fillRect(50, 50, 100, 100);
gfx.setFont(new Font("Century", Font.BOLD, 30));
gfx.drawString("I feel good sometimes I don't", 200, 200);
g.drawImage(img, 0, 0, this); // draws the off-screen image
}
public void update(Graphics g) {
draw(g);
}
public void run() {
while(true) {
repaint();
try {
Thread.sleep(5);
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
}
If you run the application, all the Graphics (.fillRect, .drawString, etc.) components/methods are drawn on the off-screen buffer. However, my goal is to add a JButton to the applet - and as expected, there's no off-screen loading for the JButton component (which means flickering).
Graphics gfx;
JButton button1;
public void draw(Graphics g) {
setLayout(null);
button1.setBounds(225, 400, 250, 50);
button1.setFont(new Font("Courier", Font.PLAIN, 17));
button1.setForeground(Color.WHITE);
button1.setBackground(Color.DARK_GRAY);
add(button1); // is it possible to draw the JButton on the off-screen buffer?
}
How would you add off-screen loading to a JButton component?
Applet (and JApplet) are officially deprecated, they are no longer supported by Java, Oracle, Browsers (or the community generally)
Swing components are, by default, double buffered. If you work with the painting system correctly, you shouldn't experience any flickering, if you do, it's clear sign that you're doing something wrong.
I would recommend having a look at Performing Custom Painting and Painting in AWT and Swing for more details about how the Swing painting system works.
Swing is single threaded AND not thread safe. This means that you should not perform any long running operations within the context of the Event Dispatching Thread and you should not update the UI from outside the context of the EDT.
Have a look at Concurrency in Swing for more details.
A simple solution to these problems is to make use a Swing Timer, which can be used to schedule regular updates which are executed within the context of the EDT.
See How to Use Swing Timers for more details...
As a basic runnable example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static class TestPane extends JPanel {
public static final int WIDTH = 700, HEIGHT = 500;
public TestPane() {
setLayout(new GridBagLayout());
add(new JButton("Big fat button"));
Timer timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(WIDTH, HEIGHT);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.BLACK);
g2d.fillRect(0, 0, WIDTH, HEIGHT);
g2d.setColor(Color.WHITE);
g2d.fillRect(50, 50, 100, 100);
g2d.setFont(new Font("Century", Font.BOLD, 30));
g2d.drawString("I feel good sometimes I don't", 200, 200);
g2d.dispose();
}
}
}
Okay, "But I absolutely, must, no questions asked, use Applet ... 😓, then I feel sorry for you, but that doesn't change the fact that Swing is already double buffered. The above example could easily be applied to a J/Applet simply by creating an instance of the JPanel and adding to an Applet container
Swing makes use of "passive rendering" algorithm, if you absolutely must be complete control, then you can have a look at BufferStrategy which hands complete control of the painting system over to you, but you won't be able to use Swing components, as they are updated by the Swing sub-system
I am trying to use java swing GUI at the same time I am drawing 2Dgraphics for the game I am creating. My goal is to have a drop down over the top of the graphics. Whenever i try to make a button or drop down in swing I cannot see it. This I believe is the 2Dgrahpics covering the swing elements.
This is my class decleration and my JFrame creation:
public class Main extends JFrame{
public Main(){
setBackground(Color.LIGHT_GRAY);
setSize(1000,600);
setTitle("Scaling Tests");
setDefaultCloseOperation(3);
setVisible(true);
This is how I am double buffering and drawing the graphics:
public void paint(Graphics g){
dbImage = createImage(1000, 600);
dbg = dbImage.getGraphics();
draw(dbg);
g.drawImage(dbImage, 0, 0, this.getWidth(), this.getHeight() , this);
repaint();
}
public void draw(Graphics g){
g.setColor(Color.red);
g.fillRect(100, 100, 200, 200);
I have tried to draw a JLabel like this in the constructor and the draw method:
JLabel mylabel = new JLabel();
mylabel.setText("Hello World!");
mylabel.setBounds(0, 0, 1280, 720);
mylabel.setVisible(true); //unnecessary
this.add(mylabel);
This has not worked. I cannot see the Jlabel no matter where in the code I put it. I think there is some problem with the graphics covering it up.
Your problem is that you are breaking the painting chain because you didn't invoke super.paint() so none of the child components are painted.
However you should NOT override paint() on a JFrame.
Instead, custom painting is done by overriding paintComopnent() on a JPanel and then you add the panel to the frame. And don't forget to invoke super.paintComponent(g) as the first statement in the method.
Then you can add the JLabel to the panel.
Also, never invoked repaint() in a painting method. This will cause an infinite loop.
This works -except the label is all the background you have to use some layout manager to handle it
import java.awt.*;
import javax.swing.*;
public class Main extends JFrame{
public Main(){
setBackground(Color.LIGHT_GRAY);
setSize(1000,600);
setTitle("Scaling Tests");
setDefaultCloseOperation(3);
JLabel mylabel = new JLabel();
mylabel.setText("Hello World!");
mylabel.setBounds(0, 0, 1280, 720);
add(mylabel);
setVisible(true);
}
public static void main(String args[]) {
Main m=new Main();
}
public void paint(Graphics g){
Image dbImage = createImage(1000, 600);
Graphics dbg = dbImage.getGraphics();
draw(dbg);
g.drawImage(dbImage, 0, 0, this.getWidth(), this.getHeight() , this);
}
public void draw(Graphics g){
g.setColor(Color.red);
g.fillRect(100, 100, 200, 200);
}
}
I am working on some application designed to be not 100% opaque, so it basically darkens the desktop of the user and my Swing interface is shown on top of this "dark veil".
It seems to me that, when some Swing components are being moved over that veil, my JFrame would need to be repainted for my moving components not to leave a trail behind them. The thing is that repainting the JFrame is too slow and my application wouldn't run smoothly anymore.
For your convenience, I created a SSCCE class that illustrates my issue, here it is:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Toolkit;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
#SuppressWarnings("serial")
public class TransparentFrameSSCCE extends JFrame {
private static final Dimension SCREEN_DIMENSIONS = Toolkit.getDefaultToolkit().getScreenSize();
private final JPanel movingPanel;
private TransparentFrameSSCCE() {
super();
this.setUndecorated(true);
this.setResizable(false);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(TransparentFrameSSCCE.SCREEN_DIMENSIONS);
// This makes my JFrame transparent (its alpha component is set to 0)
this.setBackground(new Color(0, 0, 0, 0));
this.movingPanel = new JPanel();
this.movingPanel.setBounds(0, 0, 50, 50);
this.movingPanel.setBackground(Color.RED);
final JPanel contentPane = new JPanel();
// This makes my panel semi-transparent (its alpha component is set to 128)
contentPane.setBackground(new Color(0, 0, 0, 128));
contentPane.setLayout(null);
contentPane.add(this.movingPanel);
this.setContentPane(contentPane);
}
#Override
public void setVisible(final boolean isVisible) {
super.setVisible(isVisible);
new Thread(new Runnable() {
#Override
public void run() {
int x, y;
for(;;) {
x = TransparentFrameSSCCE.this.movingPanel.getLocation().x;
y = TransparentFrameSSCCE.this.movingPanel.getLocation().y;
TransparentFrameSSCCE.this.movingPanel.setLocation(x + 5, y);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
public static void main(final String args[]) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new TransparentFrameSSCCE().setVisible(true);
}
});
}
}
Would anyone know any other way to do so?
UPDATE: Following #MadProgrammer's directions about Swing components transparency behavior, this is how to deal with my "dark veil". It works perfectly. Many thanks to him :)
final JPanel contentPane = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
final Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(new Color(0, 0, 0, 128));
g2d.fill(new Area(new Rectangle(new Point(0, 0), getSize())));
g2d.dispose();
}
};
contentPane.setOpaque(false); // Instead of: contentPane.setColor(new Color(0, 0, 0, 128)
Java components don't have a concept of transparency, they are either opaque or fully transparent (alright, the new transparency support for top level windows is an exception ;))
What you need to do is create a custom component that is fully transparent and the override it's paintComponent and fill the area of the component with your translucent color.
Also, don't modify the state of Swing components outside of the context of the Event Dispatching Thread, strange things begin to happen. A better solution might be to use a javax.swing.Timer
For example
Create rectangle with mouse drag, not draw
Java Swing: Transparent PNG permanently captures original background
How to make a transparent JFrame but keep everything else the same?
You may also want to take a look at Concurrency in Swing
Check out Backgrounds With Transparency for a simple explanation of the problem. Basically, you need to make sure your custom component paints the background.
Or instead of doing the custom painting you can take advantage of the AlphaContainer class which will do the painting for you:
//this.setContentPane( contentPane);
this.setContentPane( new AlphaContainer(contentPane) );