So I'm trying to change the background on a full screen game I'm making from a tutorial, I'm trying to change the background to green, but it stays black, what's wrong with the code?
Screen.java
package debels;
import java.awt.*;
import javax.swing.JFrame;
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 e){}
}
}
public Window getFullScreen(){
return vc.getFullScreenWindow();
}
public void restoreScreen(){
Window w = vc.getFullScreenWindow();
if (w != null){
w.dispose();
}
vc.setFullScreenWindow(null);
}
}
Main.java
package debels;
import java.awt.*;
import javax.swing.*;
public class Main extends JFrame{
private static final long serialVersionUID = -7498470134154894036L;
public static void main(String[] args){
DisplayMode dm = new DisplayMode(800, 600, 16, DisplayMode.REFRESH_RATE_UNKNOWN);
Main m = new Main();
m.run(dm);
}
public void run(DisplayMode dm){
setBackground(Color.GREEN);
setForeground(Color.BLUE);
setFont(new Font("Arial", Font.PLAIN, 24));
Screen s = new Screen();
try{
s.setFullScreen(dm, this);
try{
Thread.sleep(5000);
}catch(Exception e){}
}finally{
s.restoreScreen();
}
}
public void paint(Graphics g){
g.drawString("Hello", 200, 200);
}
}
Your first problem is going to be your paint method...
public void paint(Graphics g){
g.drawString("Hello", 200, 200);
}
Part of the responsibility of the paint method is to paint...the background. But since you're not calling super.paint this can't happen.
You should avoid overriding paint on top level containers, like JFrame and instead use a custom component, like JPanel for example, and override their paintComponent method (not forgetting to call super.paintComponent).
Another area of potential issue is the use of Thread.sleep, this could be causing the Event Dispatching Thread to be halted, preventing from processing new paint requests. A better solution might be to use a javax.swing.Timer, which will pause in the background and provide notification (via a ActionListener) within the context of the EDT...
For example
import java.awt.BorderLayout;
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.Graphics2D;
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 TestFullScreen {
public static void main(String[] args) {
new TestFullScreen();
}
public TestFullScreen() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
final Screen s = new Screen();
DisplayMode dm = new DisplayMode(800, 600, 16, DisplayMode.REFRESH_RATE_UNKNOWN);
s.setFullScreen(dm, frame);
Timer timer = new Timer(5000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
s.restoreScreen();
}
});
timer.start();
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setBackground(Color.GREEN);
setForeground(Color.WHITE);
setFont(new Font("Arial", Font.PLAIN, 24));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int width = getWidth();
int height = getHeight();
FontMetrics fm = g2d.getFontMetrics();
String text = "It's not easy to be green";
int x = (width - fm.stringWidth(text)) / 2;
int y = ((height - fm.getHeight()) / 2) + fm.getAscent();
System.out.println(width + "x" + height);
System.out.println(x + "x" + y);
g2d.drawString(text, x, y);
g2d.dispose();
}
}
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 e) {
}
}
}
public Window getFullScreen() {
return vc.getFullScreenWindow();
}
public void restoreScreen() {
Window w = vc.getFullScreenWindow();
if (w != null) {
w.dispose();
}
vc.setFullScreenWindow(null);
}
}
}
This is what I did to overcome the challenge. I removed the setBackground, setForeground, and setFont from the run method in the Main class and added them to the setFullScreen method in the Screen class:
window.getContentPane().setBackground(Color.PINK);
window.setForeground(Color.BLUE);
window.setFont(new Font ("Arial", Font.PLAIN, 24));
Thereafter, I added "super.paint(g);" as the first line of the paint method in the Main class
You just need to make 3 little changes and it will work. Follow these steps:
Step-1:Add getContentPane() to setBackgroud() which is present in the run() method
getContentPane().setBackground(Color.GREEN);
Step-2: Cut & paste below lines from run() method to paint() method.
getContentPane().setBackground(Color.GREEN);
setForeground(Color.BLUE);
setFont(new Font("Arial", Font.PLAIN, 24));
step-3: Add super.paint(g) in the starting of paint() method. the whole paint() method will look like this;
public void paint(Graphics g){
super.paint(g);
getContentPane().setBackground(Color.GREEN);
setForeground(Color.BLUE);
setFont(new Font("Arial", Font.PLAIN, 24));
g.drawString("Hello", 200, 200);
}
if it still doesn't work let me know in the comment! #YNWA
Related
I have a JSlider that specifies the thickness of a line drawn on a JPanel. I added a ChangeListener to it, so when the slider is changed, it should also change the thickness of the line. But when I do that, the thickness doesn't change and instead, I'm given a faded out line. When I don't have the ChangeListener, the initial color is a purple line. I don't understand what's going on.
This is in my main GUI class, it includes the JSlider and the ChangeListener:
final JSlider slider = new JSlider(SwingConstants.HORIZONTAL, 0, 20, 1);
slider.setMajorTickSpacing(5);
slider.setMinorTickSpacing(1);
slider.setPaintTicks(true);
slider.setPaintLabels(true);
slider.addChangeListener(new ChangeListener() {
public void stateChanged(final ChangeEvent theEvent) {
final int stroke = slider.getValue();
myDrawPanel.setStroke(stroke);
myDrawPanel.repaint();
}
});
And this is in another class where the line is being drawn:
public void setStroke(final int theStroke) {
myStroke = theStroke;
}
public void paintComponent(final Graphics theGraphics) {
super.paintComponent(theGraphics);
final Graphics2D g2d = (Graphics2D) theGraphics;
// for better graphics display
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setPaint(new Color(51, 0, 111));
g2d.setStroke(new BasicStroke(myStroke));
g2d.draw(new Line2D.Double(myX, myY, myXEnd, myYEnd));
for (Line2D l : myLines) {
g2d.draw(l);
}
}
And this is the outcome:
And this is what the line originally looked like when I hardcoded a line thickness number in my paintComponent method:
Seems to work okay for me, I guess the problem is somewhere else in the code you're not showing us
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class Example {
public static void main(String[] args) {
new Example();
}
public Example() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new ControlPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class ControlPane extends JPanel {
public ControlPane() {
setLayout(new BorderLayout());
DrawPane pane = new DrawPane();
add(pane);
JSlider slider = new JSlider(0, 20, 1);
add(slider, BorderLayout.SOUTH);
slider.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
pane.setStroke(slider.getValue());
}
});
slider.setValue(1);
}
}
public class DrawPane extends JPanel {
private int myStroke = 1;
public DrawPane() {
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
public void setStroke(int myStroke) {
this.myStroke = myStroke;
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setStroke(new BasicStroke(myStroke));
g2d.drawLine(0, 0, getWidth(), getHeight());
g2d.dispose();
}
}
}
Consider providing a runnable example which demonstrates your problem. This is not a code dump, but an example of what you are doing which highlights the problem you are having. This will result in less confusion and better responses
I want to create a fully transparent background for a Frame (or JFrame) and have it show a transparent animation. I managed to get it working in Windows 7 x64 but the same code does not run on my Linux (Lubuntu x64 15.04).
The code below shows what I'm trying to achieve--just copy & paste it. I just want the little rectangle to move across the screen without leaving a trail.
static int a = 0;
public static void main(String[] args) {
JFrame f = new JFrame();
f.setUndecorated(true);
f.setBackground(new Color(0, 0, 0, 0));
f.setVisible(true);
f.setSize(512, 512);
f.add(new JPanel() {
#Override
public void paintComponent(Graphics gr) {
Graphics2D g = (Graphics2D)gr;
g.setBackground(new Color(0, 0, 0, 0));
g.clearRect(0, 0, 512, 512);
g.drawRect(a, a++, 2, 2);
}
});
while(true) {
try {
Thread.sleep(30);
} catch(InterruptedException e) {
e.printStackTrace();
}
f.repaint();
}
}
What I want to achieve (as shown in Windows) and what I get with Lubuntu 15.04:
I just want to see the little square move just like what's shown on Windows 7--I don't want to see a trail.
Please don't give me the link of Oracle's transparency and window documentation--I've gone over it all thrice.
What I've tried:
Graphics2D's 'copyArea()' of a transparent space. (This used to work AFAIK but no longer does)
GlassPane
AlphaComposite
setPaint()
Please please just test out your thoughts/code first. A lot of the "this should work" stuff I have already tried and does not seem to... All help is greatly appreciated.
For reference, here's a minimal complete example, suitable for cross-platform testing. Note that
On some platforms, e.g. Ubuntu, a completely transparent background is not seen as opaque; a small, non-zero alpha value is a typical work-around.
Swing GUI objects should be constructed and manipulated only on the event dispatch thread.
Use java.swing.Timer, which runs on the event dispatch thread, to pace the animation.
Don't use setPreferredSize() when you really mean to override getPreferredSize().
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
/**
* #see https://stackoverflow.com/a/31328464/230513
*/
public class TransparentAnimation {
private static final Color tranparentBlack = new Color(0, 0, 0, 1);
private void display() {
JFrame f = new JFrame("Test");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setUndecorated(true);
f.setBackground(tranparentBlack);
f.add(new JPanel() {
int x, y;
Timer t = new Timer(10, (ActionEvent e) -> {
x = (x + 1) % getWidth();
y = (y + 1) % getHeight();
repaint();
});
{
setBackground(tranparentBlack);
t.start();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
g.fillOval(x, y, 16, 16);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(320, 240);
}
});
f.add(new JLabel(System.getProperty("os.name") + "; v"
+ System.getProperty("os.version")), BorderLayout.SOUTH);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new TransparentAnimation()::display);
}
}
Basically this issue is OS-related. What works for Windows will not work for Linux and vice versa.
For some reason, Linux only allows animated per-pixel-transparency when setting up a BufferStrategy. However, this solution fails on Windows. As a result I have come up with the following code which picks the correct algorithm based on the OS:
static int a = 0;
public static void main(String[] args) {
JFrame f = new JFrame();
JPanel p = new JPanel() {
#Override
public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setBackground(new Color(255, 255, 255, 0));
g2d.clearRect(0, 0, f.getWidth(), f.getHeight());
g2d.drawRect(a, a++, 2, 2);
}
};
f.add(p);
f.setUndecorated(true);
f.setBackground(new Color(255, 255, 255, 0));
f.setSize(512, 512);
f.setVisible(true);
f.createBufferStrategy(2);
BufferStrategy bs = f.getBufferStrategy();
while (true) {
try {
Thread.sleep(33);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (System.getProperty("os.name").contains("indows ")) {
p.repaint();
} else {
Graphics g = null;
do {
try {
g = bs.getDrawGraphics();
p.update(g);
} finally {
g.dispose();
}
bs.show();
} while (bs.contentsLost());
Toolkit.getDefaultToolkit().sync();
}
}
}
This code works for my Windows 7 x64 and my Lubuntu 15.04 x64. Please try out this code out yourself and see if it works for you. I myself don't own a Mac so if someone would please test it for me I would be very grateful. If it does not work for anyone, please let me know.
This is what you're supposed to see:
If we extend JFrame, set undecorated to true, and override paint with, we can make a transparent JFrame.
try this,
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class TestTransparentFrame {
private class PaintPanel extends JPanel {
private List<Point> points = new ArrayList<Point>();
public PaintPanel() {
setOpaque(false);
MouseAdapter adapter = new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
points.clear();
repaint();
}
#Override
public void mouseMoved(MouseEvent e) {
points.add(e.getPoint());
repaint();
}
};
addMouseListener(adapter);
addMouseMotionListener(adapter);
setBorder(BorderFactory.createLineBorder(Color.GREEN));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (points.size() > 1) {
g.setColor(Color.RED);
Point p1 = points.get(0);
for (int i = 1; i < points.size(); i++) {
Point p2 = points.get(i);
g.drawLine(p1.x, p1.y, p2.x, p2.y);
p1 = p2;
}
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(700, 500);
}
}
protected void createAndShowGUI() throws MalformedURLException, IOException {
JFrame frame = new JFrame("Test transparent painting");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setUndecorated(true);
frame.setBackground(new Color(0, 0, 0, 50));
frame.add(new PaintPanel());
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
try {
new TestTransparentFrame().createAndShowGUI();
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
}
I have the problem of this textarea not showing up when I run it. Is there any way to make it show up. BTW its getting called through the gameloop on a class that extends canvas.
public void render(Graphics g){
Graphics2D g2d = (Graphics2D) g;
if(!initialized)
init();
try {
test.requestFocus();
test.paintAll(g);
test.setText("hi");
test.setBounds(getBounds());
test.printAll(g);
} catch (Exception e) {
e.printStackTrace();
}
g2d.draw(getBounds());
g.drawRect(0, 0, 100, 100);
}
private void init(){
frame.setVisible(false);
initialized = true;
test = new TextArea();
test.setEditable(true);
test.setBounds(getBounds());
test.setBackground(test.getBackground());
test.setForeground(test.getForeground());
frame.add(test);
frame.repaint();
frame.setVisible(true);
System.out.println(test.isVisible());
}
private Rectangle getBounds(){
return new Rectangle(100, 100, 100, 100);
}
I have tried using JTextArea but it takes up the full screen and won't bind to a rect. Thanks for help in advance!
You don't need to manually paint existing Components you add to your program. Here is a simple example how to display a TextArea over a frame/canvas you paint yourself: Check the comments for further details.
package main;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.TextArea;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.Timer;
public class Test extends JFrame {
private static Test frame;
private static double t;
private static int x;
private static int y;
private static TextArea test;
public static void main(String[] args) {
frame = new Test();
frame.setVisible(true);
// set layout to null so that you can freely position your components
// without them "filling up the whole screen"
frame.setLayout(null);
frame.setSize(500, 500);
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent we) {
System.exit(0);
}
});
// game loop
new Timer(10, (e) -> {
t += 10.0 / 1000;
x = (int) (100 + 50 * Math.sin(t));
y = (int) (100 + 50 * Math.cos(t));
// calling repaint will cause Test.paint() to be called first,
// then Test's children will be painted (= the TextArea)
frame.repaint();
}).start();
// initialize the textarea only once
test = new TextArea();
test.setEditable(true);
test.setBounds(new Rectangle(100, 100, 100, 100));
test.setText("hi");
frame.add(test);
}
#Override
public void paint(Graphics g) {
// put only painting logic in your paint/render.
// don't set the bounds of components here,
// as this will trigger a repaint.
g.setColor(Color.black);
g.fillRect(0, 0, 400, 400);
g.setColor(Color.yellow);
g.fillOval(x, y, 20, 20);
}
}
Hi I have following classes, I want display content
(paintComponentor that panel with this rectangle from paint class)
inside my JFrame. I try already find out how to achieve this by
looking at different examples posted on this forum however this
doesn't help me I need simple example like panel inside frame with
paint component or something similar to understand how should work!
ps. don't hang me on tree because I am newbie jut ask question!!!
[I want something like this][1]
package scp;
import java.awt.*;
import javax.swing.*;
public class Panel extends JPanel {
public Panel() {
//this.setPreferredSize(new Dimension(200,200));
//panel = new Panel();
setVisible(true);
setLayout(new FlowLayout());
setSize(200, 300);
getRootPane();
}
#Override
public void paintComponent(Graphics g){
g.drawString("HEY",20,20);
g.drawRect(200, 200, 200, 200);
}
}
and
package scp;
import javax.swing.JFrame;
import javax.swing.JButton;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.beans.EventHandler;
public class Frame extends JFrame {
JButton redButton;
JButton blueButton;
public Frame()
{
super("EventHandling");
setLayout(new FlowLayout());
redButton = new JButton("Red");
add(redButton);
blueButton = new JButton("Blue");
add(blueButton);
EventHandler h = new EventHandler();
redButton.addActionListener(h);
blueButton.addActionListener(h);
}
private class EventHandler implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
if (e.getSource()==redButton)
getContentPane().setBackground(Color.RED);
else if(e.getSource()==blueButton)
getContentPane().setBackground(Color.BLUE);
}
}
}
and
package scp;
import javax.swing.JFrame;
public class EventHandling {
public static void main(String[] args) {
Frame f = new Frame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(800,600);
f.setVisible(true);
f.getContentPane().add(new Panel());
}
}
[1]: http://i.stack.imgur.com/OJTrq.png
Start by taking a look at:
Painting in AWT and Swing
Performing Custom Painting
2D Graphics
This is probably the simplest I can make it...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int width = getWidth() - 100;
int height = getHeight() - 100;
int x = (getWidth() - width) / 2;
int y = (getHeight() - height) / 2;
g2d.setColor(Color.RED);
g2d.drawRect(x, y, width, height);
g2d.dispose();
}
}
}
Compound Example
This example uses an outer panel, which has an empty border applied to it, this pushes the content of the edges of the outer panel.
The inner panel (which is unchanged from the last example), as a light gray border applied to it so you can see it, the red rectangle is still been painted by the panels paintComponent method.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JPanel outer = new JPanel(new BorderLayout()) {
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
};
outer.setBorder(new EmptyBorder(50, 50, 50, 50));
TestPane tp = new TestPane();
tp.setBorder(new LineBorder(Color.LIGHT_GRAY));
outter.add(tp);
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(outer);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int width = getWidth() - 100;
int height = getHeight() - 100;
int x = (getWidth() - width) / 2;
int y = (getHeight() - height) / 2;
g2d.setColor(Color.RED);
g2d.drawRect(x, y, width, height);
g2d.dispose();
}
}
}
So I am working on a project here that requires a custom JLayeredPane - like class.
It has two members 'ground' and 'foreground' that are JPanel and an background (Image) member.
The way it was supposed to show was that the background Image should have been drawn and then all the components of the ground on top of it and then foreground's components at the apex. So foreground covers up ground which covers up background. Background should be shown only at places that do not have a Component in ground and foreground or where there is transparency in the JPanels.
It's paint function goes like this:
#Override
public void paint(Graphics g){
g.drawImage(background, 0, 0, null);
ground.paint(g.create());
foreground.paint(g.create());
g.dispose();
}
But nothing like that happens. Just the background image gets painted and nothing else shows.
I have used System.out.println() function to check that ground and foreground actually hold components and they do. But they just don't show.
Can anyone help me here?
The most significant issue is you're not calling super.paint, which is preventing what ever was previously painted to the Graphics context from been cleared or any child components from been painted.
For painting the background, you should be using paintComponent, which is used to paint the background of the component.
If you need to paint under the child components, but above the background, you should still use paintComponent, but paint the background first and then the next layer. The components will be painted after paintComponent.
Painting over components is actually more complex
Take a closer look at Custom Painting and Painting in Swing and AWT
Updated based on code snippets
In Screen which extends from Container, you are doing...
#Override
public void paint(Graphics g) {
super.paint(g);
GraphicsUtilities.drawPictureTiled(background, g);
paintComponents(g);
g.dispose();
}
Don't call paintComponents, super.paint has already done this.
Don't call dispose on a Graphics context you don't create
Based on the rest of the example code I have, you should be extending from JPanel and overriding paintComponent instead. This will allow you to put under the component layer
Because GroundPanel and ForeGroundPanel are both JPanels, there's no need to ever paint them yourself. In fact, you could simply use OverlayLayout or even a GridBagLayout and add them directly to the NestedScreen which is itself a container...
So, I stripped down you example code so I could get it working with the missing code as an example. I got a little more fancy and simply made a JPanel to act as the pause screen
This is all done by simply overlaying the components on top of each other using a GridBagLayout
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test1001 {
public static void main(String[] args) {
new Test1001();
}
public Test1001() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
try {
NestedScreen screen = new NestedScreen();
screen.setBackgroundLayer(ImageIO.read(getClass().getResource("/Sky.png")));
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(screen);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException exp) {
exp.printStackTrace();
}
}
});
}
public interface GraphicsEngineComponents {
}
public class NestedScreen extends Screen implements GraphicsEngineComponents {
GroundPanel ground;
ForeGroundPanel foreground;
private PausePane pausePane;
public NestedScreen() {
ground = new GroundPanel();
foreground = new ForeGroundPanel();
pausePane = new PausePane();
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.weightx = 1;
gbc.weighty = 1;
gbc.fill = GridBagConstraints.BOTH;
add(pausePane, gbc);
add(foreground, gbc);
add(ground, gbc);
MouseAdapter handler = new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
pausePane.setVisible(!pausePane.isVisible());
}
};
addMouseListener(handler);
foreground.addMouseListener(handler);
ground.addMouseListener(handler);
}
public GroundPanel getGroundLayer() {
return ground;
}
public ForeGroundPanel getForegroundLayer() {
return foreground;
}
public void setBackgroundLayer(BufferedImage background) {
super.setBackgroundLayer(background);
}
public class GroundPanel extends JPanel {
public GroundPanel() {
setOpaque(false);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.GREEN);
g.fillRect(0, getHeight() - 200, getWidth(), 200);
}
}
public class PausePane extends JPanel {
private JLabel label;
public PausePane() {
setVisible(false);
setOpaque(false);
setBackground(new Color(0, 0, 0, 128));
setLayout(new GridBagLayout());
label = new JLabel("Paused");
label.setHorizontalAlignment(JLabel.CENTER);
label.setVerticalAlignment(JLabel.CENTER);
Font font = label.getFont();
font = font.deriveFont(Font.BOLD, 48f);
label.setFont(font);
label.setForeground(Color.WHITE);
add(label);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(getBackground());
g.fillRect(0, 0, getWidth(), getHeight());
}
}
public class ForeGroundPanel extends JPanel {
private BufferedImage pony;
public ForeGroundPanel() {
setOpaque(false);
try {
pony = ImageIO.read(getClass().getResource("/Pony.png"));
} catch (IOException ex) {
ex.printStackTrace();
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (pony != null) {
int x = (getWidth() - pony.getWidth()) / 2;
int y = getHeight() - 200 - (pony.getHeight() / 2);
g.drawImage(pony, x, y, this);
}
}
}
}
public class Screen extends JPanel implements GraphicsEngineComponents {
private BufferedImage background;
public Screen() {
}
#Override
public String toString() {
return "Screen{" + "background=" + background + '}';
}
public BufferedImage getBackgroundPicture() {
return background;
}
#Override
public Dimension getPreferredSize() {
return background == null ? super.getPreferredSize() : new Dimension(background.getWidth(), background.getHeight());
}
protected void setBackgroundLayer(BufferedImage background) {
if (background != null && background.getHeight() != 0 && background.getWidth() != 0) {
this.background = background;
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (background != null) {
g.drawImage(background, 0, 0, this);
}
}
}
}
Take a look at Painting in AWT and Swing and Performing Custom Painting to understand how painting works in Swing.
A basic idea would be to avoid using all these compound or nested components and instead, create an engine that can paint the layers directly onto a Graphics context, maybe even painting to a BufferedImage which you can the paint onto a single component...
There are a few ways you go about doing this. I'll just introduce one way.
Create a background panel where you paint the background image (in the example below, it is BackgroundPanel with the image only being the forresty background). Set that panel as the content pane to the frame.
Create another ground panel where you can also paint something (in the example below if it the GroundPanel with only the image of bugs bunny painted.
Create your foreground panel and add it to the ground panel. You can add your foreground components to it. (in the example below the foreground image is the grassy hill, and I also add a button to it
All the panels' opaque property should be set to false, as to allow the panel behind it to be shown under any transparency.
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
public class ThreeTier {
public static final int DIM_WIDTH = 600;
public static final int DIM_HEIGHT = 400;
private BufferedImage backgroundImage;
private BufferedImage groundImage;
private BufferedImage foregroundImage;
public ThreeTier() {
initImages();
JFrame frame = new JFrame();
frame.setContentPane(new BackgroundPanel());
frame.add(new GroundPanel());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private void initImages() {
try {
backgroundImage = ImageIO.read(getClass().getResource("/resources/background.png"));
foregroundImage = ImageIO.read(getClass().getResource("/resources/foreground.png"));
groundImage = ImageIO.read(getClass().getResource("/resources/bugsBunny.png"));
} catch (IOException ex) {
Logger.getLogger(ThreeTier.class.getName()).log(Level.SEVERE, null, ex);
}
}
class BackgroundPanel extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(backgroundImage, 0, 0, DIM_WIDTH, DIM_HEIGHT, this);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(ThreeTier.DIM_WIDTH, ThreeTier.DIM_HEIGHT);
}
}
class GroundPanel extends JPanel {
private static final String RIGHT_ACTION = "rightAction";
private int imageX = 50;
private int imageY = 140;
public GroundPanel() {
setOpaque(false);
add(new ForegroundPanel());
InputMap im = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
im.put(KeyStroke.getKeyStroke("RIGHT"), RIGHT_ACTION);
getActionMap().put(RIGHT_ACTION, new AbstractAction() {
public void actionPerformed(ActionEvent e) {
if (imageX >= DIM_WIDTH) {
imageX = 0 - groundImage.getWidth();
repaint();
} else {
imageX += 10;
repaint();
}
}
});
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(groundImage, imageX, imageY, groundImage.getWidth(), groundImage.getHeight(), this);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(ThreeTier.DIM_WIDTH, ThreeTier.DIM_HEIGHT);
}
}
class ForegroundPanel extends JPanel {
public ForegroundPanel() {
setOpaque(false);
setLayout(new FlowLayout(FlowLayout.TRAILING, 10, 0));
JButton button = new JButton("I'm in the Foreground!");
add(button);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(foregroundImage, 0, 0, DIM_WIDTH, DIM_HEIGHT, this);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(ThreeTier.DIM_WIDTH, ThreeTier.DIM_HEIGHT);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new ThreeTier();
}
});
}
}
UPDATE
Of course you could always just use the JLayeredPane. That's what it's for. See this example
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Image;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.border.LineBorder;
public class LayeredPaneDemo {
public static final int DIM_WIDTH = 600;
public static final int DIM_HEIGHT = 400;
public LayeredPaneDemo() {
ContainerPanel container = new ContainerPanel();
JLabel title = new JLabel("Lame Google Map");
title.setFont(new Font("verdana", Font.BOLD, 36));
title.setHorizontalAlignment(JLabel.CENTER);
JPanel panel = new JPanel(new GridBagLayout());
panel.add(container);
JFrame frame = new JFrame();
frame.add(panel);
frame.add(title, BorderLayout.NORTH);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new LayeredPaneDemo();
}
});
}
public class ContainerPanel extends JPanel {
public ContainerPanel() {
JLayeredPane layeredPane = new JLayeredPane();
layeredPane.setPreferredSize(new Dimension(DIM_WIDTH, DIM_HEIGHT));
BackgroundPanel bg = new BackgroundPanel();
GroundPanel gg = new GroundPanel();
ForegroundPanel fg = new ForegroundPanel();
bg.setBounds(0, 0, DIM_WIDTH, DIM_HEIGHT);
layeredPane.add(bg, new Integer(1));
gg.setBounds(0, 0, DIM_WIDTH, DIM_HEIGHT);
layeredPane.add(gg, new Integer(2));
fg.setBounds(0, 0, DIM_WIDTH, DIM_HEIGHT);
layeredPane.add(fg, new Integer(3));
setLayout(new GridBagLayout());
add(layeredPane);
setBorder(new LineBorder(Color.BLUE, 10));
}
}
public class ForegroundPanel extends JPanel {
public ForegroundPanel() {
JPanel buttonPanel = new JPanel(new GridLayout(3, 3));
buttonPanel.setOpaque(false);
buttonPanel.add(new JLabel());
buttonPanel.add(new JButton("UP"));
buttonPanel.add(new JLabel());
buttonPanel.add(new JButton("Left"));
buttonPanel.add(new JLabel());
buttonPanel.add(new JButton("Right"));
buttonPanel.add(new JLabel());
buttonPanel.add(new JButton("Down"));
buttonPanel.add(new JLabel());
FlowLayout flow = (FlowLayout) getLayout();
flow.setAlignment(FlowLayout.TRAILING);
flow.setHgap(0);
flow.setVgap(0);
add(buttonPanel);
setOpaque(false);
}
}
public class GroundPanel extends JPanel {
Image image = null;
public GroundPanel() {
try {
image = ImageIO.read(getClass().getResource("/resources/california.png"));
} catch (IOException ex) {
Logger.getLogger(LayeredPaneDemo.class.getName()).log(Level.SEVERE, null, ex);
}
setOpaque(false);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, DIM_WIDTH, DIM_HEIGHT, this);
}
}
public class BackgroundPanel extends JPanel {
public BackgroundPanel() {
setLayout(new GridBagLayout());
setBackground(Color.WHITE);
try {
Image img = ImageIO.read(getClass().getResource("/resources/google.jpg"));
add(new JLabel(new ImageIcon(img)));
} catch (IOException ex) {
Logger.getLogger(LayeredPaneDemo.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}