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);
}
}
}
}
Related
After learning that dispose() should be called on Graphics/Graphics2D object after use, I went about changing my game to incorporate this.
When I added g2d.dispose() in overridden paintComponent(Graphics g) of JPanel, my components which I added (extensions of JLabel class) where not rendered I was able to still click on them etc but they would not be painted.
I tested with a normal JLabel and JButton with same effect (though JButton is rendered when mouse is over it).
So my question is why does this happen?
Here is an SSCCE to demonstrate:
after uncommenting call to dispose() in paintComponent of MainMenuPanel class:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Test {
public Test() {
try {
initComponents();
} catch (Exception ex) {
Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Test();
}
});
}
private void initComponents() throws Exception {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
MainMenuPanel mmp = new MainMenuPanel();
frame.add(mmp);
frame.pack();
frame.setVisible(true);
}
}
class MainMenuPanel extends JPanel {
//create labels for Main Menu
private PopUpJLabel versusesModeLabel;
private PopUpJLabel singlePlayerModeLabel;
private PopUpJLabel optionsLabel;
private PopUpJLabel helpLabel;
private PopUpJLabel aboutLabel;
//create variable to hold background
private Image background;
private Dimension preferredDimensions;
public static String gameType;
public static final String SINGLE_PLAYER = "Single Player", VERSUS_MODE = "VS Mode";
/**
* Default constructor to initialize double buffered JPanel with
* GridBagLayout
*/
public MainMenuPanel() {
super(new GridBagLayout(), true);
try {
initComponents();
} catch (Exception ex) {
JOptionPane.showMessageDialog(null, "Could not load main menu background!", "Main Menu Error: 0x004", JOptionPane.ERROR_MESSAGE);
System.exit(4);
}
}
/*
* Create JPanel and its components
*/
private void initComponents() throws Exception {
//set prefered size of JPanel
preferredDimensions = new Dimension(800, 600);
background = scaleImage(800, 600, ImageIO.read(new URL("http://photos.appleinsider.com/12.08.30-Java.jpg")));
//create label instances
singlePlayerModeLabel = new PopUpJLabel("Single Player Mode");
singlePlayerModeLabel.setEnabled(false);
versusesModeLabel = new PopUpJLabel("Versus Mode");
optionsLabel = new PopUpJLabel("Options");
helpLabel = new PopUpJLabel("Help");
aboutLabel = new PopUpJLabel("About");
//create new constraints for gridbag
GridBagConstraints gc = new GridBagConstraints();
gc.fill = GridBagConstraints.HORIZONTAL;
gc.ipady = 50;//vertical spacing
//add newGameLabel to panel with constraints
gc.gridx = 0;
gc.gridy = 0;
add(singlePlayerModeLabel, gc);
gc.gridy = 1;
add(versusesModeLabel, gc);
//add optionsLabel to panel with constraints (x is the same)
gc.gridy = 2;
add(optionsLabel, gc);
//add helpLabel to panel with constraints (x is the same)
gc.gridy = 3;
add(helpLabel, gc);
//add aboutLabel to panel with constraints (x is the same)
gc.gridy = 4;
add(aboutLabel, gc);
}
public static BufferedImage scaleImage(int w, int h, BufferedImage img) throws Exception {
BufferedImage bi;
//bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
bi = new BufferedImage(w, h, img.getType());
Graphics2D g2d = (Graphics2D) bi.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.addRenderingHints(new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY));
g2d.drawImage(img, 0, 0, w, h, null);
g2d.dispose();
return bi;
}
/*
* Will return the preffered size of JPanel
*/
#Override
public Dimension getPreferredSize() {
return preferredDimensions;
}
/*
* Will draw the background to JPanel with anti-aliasing on and quality rendering
*/
#Override
protected void paintComponent(Graphics grphcs) {
super.paintComponent(grphcs);
//convert graphics object to graphics2d object
Graphics2D g2d = (Graphics2D) grphcs;
//set anti-aliasing on and rendering etc
//GamePanel.applyRenderHints(g2d);
//draw the image as the background
g2d.drawImage(background, 0, 0, null);
//g2d.dispose();//if I uncomment this no LAbels will be shown
}
}
class PopUpJLabel extends JLabel {
public final static Font defaultFont = new Font("Arial", Font.PLAIN, 50);
public final static Font hoverFont = new Font("Arial", Font.BOLD, 70);
PopUpJLabel(String text) {
super(text);
setHorizontalAlignment(JLabel.CENTER);
setForeground(Color.ORANGE);
setFont(defaultFont);
//allow component to be focusable
setFocusable(true);
//add focus adapter to change fints when focus is gained or lost (used for transversing labels with keys)
addFocusListener(new FocusAdapter() {
#Override
public void focusGained(FocusEvent fe) {
super.focusGained(fe);
if (isEnabled()) {
setFont(getHoverFont());
}
}
#Override
public void focusLost(FocusEvent fe) {
super.focusLost(fe);
setFont(getDefaultFont());
}
});
addMouseListener(new MouseAdapter() {
#Override
public void mouseEntered(MouseEvent me) {
super.mouseEntered(me);
if (isEnabled()) {
setFont(getHoverFont());
}
//call for focus mouse is over this component
requestFocusInWindow();
}
});
}
Font getDefaultFont() {
return defaultFont;
}
Font getHoverFont() {
return hoverFont;
}
}
The thing is that the Graphics context you are using in paintComponent is created and provided by the caller (the framework), which is also responsible for disposing of it.
You only need to dispose of Graphics when you actually create it yourself (for example by calling Component.getGraphics()). In your case, you're not creating it, you're just casting it, so do not call dispose in this case.
Because .dispose() releases your resources. Yes, everything.
I've done several inspection and done research on how to make a single panel transparent so that the image underneath it will show but has been unsuccessful with panels on a JTabbedPane. I have setOpaque(false) and setBackground(new Color(0,0,0,20)) on both the panels of the JTabbedPane and also did the same on the JTabbedPane itself. However, I still can't get the image on the back of the tabbed pane to show. What else am I missing here?
tabbePane.java
package tabbedpanetransparencypractice;
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.*;
public class tabbedPane extends JTabbedPane{
JPanel tab1panel = new JPanel();
JPanel tab2panel = new JPanel();
public tabbedPane(){
this.setPreferredSize(new Dimension(400,400));
this.setBackground(new Color(0,0,0,20));
this.setOpaque(false);
tab1panel.setBackground(new Color(0,0,0,20));
tab2panel.setBackground(new Color(0,0,0,20));
tab1panel.setOpaque(false);
tab2panel.setOpaque(false);
this.addTab("Tab 1", tab1panel);
this.addTab("Tab 2", tab2panel);
}
}
topPanel.java
package tabbedpanetransparencypractice;
import javax.swing.*;
import java.awt.*;
public class topPanel extends JPanel{
Image myBG = new ImageIcon(getClass().getClassLoader().getResource("Assets/loginBg.jpg")).getImage();
#Override
public void paintComponent(Graphics g){
g.drawImage(myBG,0,0,getWidth(),getHeight(),this);
}
public topPanel(){
addTabbedPane();
}
public void addTabbedPane(){
tabbedPane tabbedpane = new tabbedPane();
this.add(tabbedpane);
}
}
frame.java
package tabbedpanetransparencypractice;
import java.awt.Dimension;
import javax.swing.*;
public class frame extends JFrame{
topPanel myPanel = new topPanel();
public frame(){
this.setPreferredSize(new Dimension(600,600));
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
this.pack();
this.setLocationRelativeTo(null);
addComponents();
}
final void addComponents(){
this.setContentPane(myPanel);
}
}
main.java
package tabbedpanetransparencypractice;
public class main {
public static void main(String[] args) {
frame myFrame = new frame();
}
}
This is the output I get (I want the tab1 and tab2 to be transparent to reveal the bg image underneath)
I'd appreciate any help. Thanks.
First, Swing DOES NOT know how to deal with component's whose background colors are alpha based but which are opaque. Swing only knows how to deal with fully opaque and fully transparent components.
Using alpha based colors will generate weird and random paint artefacts. The simple answer is, you should never apply a alpha based color to a component's background (the only exception is JFrame - thanks Sun :P)
The primary solution is, fake it.
That is, make the component transparent (setOpaque(false)) and paint the background at a reduced alpha level. You can then use a alpha based color (because the component is no longer reliant on the color, as it's transparent), but I tend to just use a AlphaComposite as it's generally easier to control and manipulate.
import java.awt.AlphaComposite;
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 java.awt.GridBagLayout;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.EmptyBorder;
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();
}
BackgroundPane bp = new BackgroundPane();
bp.setLayout(new BorderLayout());
bp.setBorder(new EmptyBorder(20, 20, 20, 20));
SeeThroughTabbedPane tp = new SeeThroughTabbedPane();
tp.setAlpha(0.5f);
tp.addTab("Tab 1", new TestPane("I be see through"));
tp.addTab("Tab 2", new TestPane("But you can't see me"));
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(bp);
frame.add(tp);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane(String text) {
setOpaque(false);
setLayout(new GridBagLayout());
JLabel label = new JLabel(text);
label.setForeground(Color.WHITE);
add(label);
}
}
public class BackgroundPane extends JPanel {
private BufferedImage bg;
public BackgroundPane() {
try {
bg = ImageIO.read(new File("/Volumes/Disk02/Dropbox/MegaTokyo/megatokyo_omnibus_1_3_cover_by_fredrin-d4oupef 50%.jpg"));
} catch (IOException ex) {
ex.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
return bg == null ? new Dimension(200, 200) : new Dimension(bg.getWidth(), bg.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (bg != null) {
Graphics2D g2d = (Graphics2D) g.create();
int x = (getWidth() - bg.getWidth()) / 2;
int y = (getHeight() - bg.getHeight()) / 2;
g2d.drawImage(bg, x, y, this);
g2d.dispose();
}
}
}
public class SeeThroughTabbedPane extends JTabbedPane {
private float alpha;
public SeeThroughTabbedPane() {
setOpaque(false);
setAlpha(1f);
}
public void setAlpha(float value) {
if (alpha != value) {
float old = alpha;
this.alpha = value;
firePropertyChange("alpha", old, alpha);
repaint();
}
}
public float getAlpha() {
return alpha;
}
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(getBackground());
g2d.setComposite(AlphaComposite.SrcOver.derive(getAlpha()));
g2d.fillRect(0, 0, getWidth(), getHeight());
g2d.dispose();
super.paintComponent(g);
}
}
}
Remember, anything you added to the JTabbedPane which is still opaque will remain so, not in my TestPane's constructor, I set the panel's opaque state to false
You might also like to take a look at Painting in AWT and Swing and Performing Custom Painting
You might be able to use UIManager.put("TabbedPane.contentAreaColor", new Color(255, 255, 0, 100));
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class TransparentTabbedPaneTest {
public JComponent makeUI() {
Color bgc = new Color(110, 110, 0, 100);
Color fgc = new Color(255, 255, 0, 100);
UIManager.put("TabbedPane.shadow", fgc);
UIManager.put("TabbedPane.darkShadow", fgc);
UIManager.put("TabbedPane.light", fgc);
UIManager.put("TabbedPane.highlight", fgc);
UIManager.put("TabbedPane.tabAreaBackground", fgc);
UIManager.put("TabbedPane.unselectedBackground", fgc);
UIManager.put("TabbedPane.background", bgc);
UIManager.put("TabbedPane.foreground", Color.WHITE);
UIManager.put("TabbedPane.focus", fgc);
UIManager.put("TabbedPane.contentAreaColor", fgc);
UIManager.put("TabbedPane.selected", fgc);
UIManager.put("TabbedPane.selectHighlight", fgc);
UIManager.put("TabbedPane.borderHightlightColor", fgc);
JTabbedPane tabs = new JTabbedPane();
JPanel tab1panel = new JPanel();
tab1panel.setBackground(new Color(0, 220, 220, 50));
JPanel tab2panel = new JPanel();
tab2panel.setBackground(new Color(220, 0, 0, 50));
JPanel tab3panel = new JPanel();
tab3panel.setBackground(new Color(0, 0, 220, 50));
JCheckBox cb = new JCheckBox("setOpaque(false)");
cb.setOpaque(false);
tab3panel.add(cb);
tab3panel.add(new JCheckBox("setOpaque(true)"));
tabs.addTab("Tab 1", tab1panel);
tabs.addTab("Tab 2", tab2panel);
tabs.addTab("Tab 3", new AlphaContainer(tab3panel));
JPanel p = new JPanel(new BorderLayout()) {
private Image myBG = new ImageIcon(getClass().getResource("test.png")).getImage();
#Override public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(myBG, 0, 0, getWidth(), getHeight(), this);
}
};
p.add(tabs);
p.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
return p;
}
public static void main(String... args) {
EventQueue.invokeLater(() -> {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new TransparentTabbedPaneTest().makeUI());
f.setSize(320, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
});
}
}
//https://tips4java.wordpress.com/2009/05/31/backgrounds-with-transparency/
class AlphaContainer extends JComponent {
private JComponent component;
public AlphaContainer(JComponent component) {
this.component = component;
setLayout( new BorderLayout() );
setOpaque( false );
component.setOpaque( false );
add( component );
}
/**
* Paint the background using the background Color of the
* contained component
*/
#Override
public void paintComponent(Graphics g) {
g.setColor( component.getBackground() );
g.fillRect(0, 0, getWidth(), getHeight());
}
}
I want to create brightness functionality in Swing. JPanel and its component's brightness level will be adjust in this functionality.
To achieve this I used JLayeredPane and added JPanel as BrightNessPanel on the top of my JPanel called MainPanel. I am giving brightness effect by changing the opacity of BrightNessPanel. This will simulate Brightness effect for my MainPanel.
Now problem is that, I am not able to click the buttons present on MainPanel because of layer of BrightNessPanel.
How do I pass through clicks from BrightNessPanel to the buttons present on MainPanel??
You might be able to make use the JLayer API which allows you to perform painting operations ontop of other components.
import java.awt.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLayer;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.plaf.LayerUI;
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();
}
TestPane testPane = new TestPane();
BrightnessLayerUI layerUI = new BrightnessLayerUI();
JLayer<JComponent> layer = new JLayer<>(testPane, layerUI);
JSlider slider = new JSlider(0, 100);
slider.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
int value = slider.getValue();
float brightness = (100 - value) / 100f;
layerUI.setBrightness(brightness);
testPane.repaint();
}
});
slider.setValue(100);
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(layer);
frame.add(slider, BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
add(new JTextField(10), gbc);
add(new JButton("Hello"), gbc);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.dispose();
}
}
public class BrightnessLayerUI extends LayerUI<JComponent> {
private float brightness = 0f;
public void setBrightness(float brightness) {
this.brightness = brightness;
}
public float getBrightness() {
return brightness;
}
#Override
public void paint(Graphics g, JComponent c) {
super.paint(g, c);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(c.getBackground());
g2d.setComposite(AlphaComposite.SrcOver.derive(getBrightness()));
g2d.fillRect(0, 0, c.getWidth(), c.getHeight());
g2d.dispose();
}
}
}
One of the advantages of this is you could actually introduce a "brightness" algorithm, rather than faking it, by using a backing buffer, which is demonstrated the link above (this use to to blur the UI)
After learning that dispose() should be called on Graphics/Graphics2D object after use, I went about changing my game to incorporate this.
When I added g2d.dispose() in overridden paintComponent(Graphics g) of JPanel, my components which I added (extensions of JLabel class) where not rendered I was able to still click on them etc but they would not be painted.
I tested with a normal JLabel and JButton with same effect (though JButton is rendered when mouse is over it).
So my question is why does this happen?
Here is an SSCCE to demonstrate:
after uncommenting call to dispose() in paintComponent of MainMenuPanel class:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Test {
public Test() {
try {
initComponents();
} catch (Exception ex) {
Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Test();
}
});
}
private void initComponents() throws Exception {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
MainMenuPanel mmp = new MainMenuPanel();
frame.add(mmp);
frame.pack();
frame.setVisible(true);
}
}
class MainMenuPanel extends JPanel {
//create labels for Main Menu
private PopUpJLabel versusesModeLabel;
private PopUpJLabel singlePlayerModeLabel;
private PopUpJLabel optionsLabel;
private PopUpJLabel helpLabel;
private PopUpJLabel aboutLabel;
//create variable to hold background
private Image background;
private Dimension preferredDimensions;
public static String gameType;
public static final String SINGLE_PLAYER = "Single Player", VERSUS_MODE = "VS Mode";
/**
* Default constructor to initialize double buffered JPanel with
* GridBagLayout
*/
public MainMenuPanel() {
super(new GridBagLayout(), true);
try {
initComponents();
} catch (Exception ex) {
JOptionPane.showMessageDialog(null, "Could not load main menu background!", "Main Menu Error: 0x004", JOptionPane.ERROR_MESSAGE);
System.exit(4);
}
}
/*
* Create JPanel and its components
*/
private void initComponents() throws Exception {
//set prefered size of JPanel
preferredDimensions = new Dimension(800, 600);
background = scaleImage(800, 600, ImageIO.read(new URL("http://photos.appleinsider.com/12.08.30-Java.jpg")));
//create label instances
singlePlayerModeLabel = new PopUpJLabel("Single Player Mode");
singlePlayerModeLabel.setEnabled(false);
versusesModeLabel = new PopUpJLabel("Versus Mode");
optionsLabel = new PopUpJLabel("Options");
helpLabel = new PopUpJLabel("Help");
aboutLabel = new PopUpJLabel("About");
//create new constraints for gridbag
GridBagConstraints gc = new GridBagConstraints();
gc.fill = GridBagConstraints.HORIZONTAL;
gc.ipady = 50;//vertical spacing
//add newGameLabel to panel with constraints
gc.gridx = 0;
gc.gridy = 0;
add(singlePlayerModeLabel, gc);
gc.gridy = 1;
add(versusesModeLabel, gc);
//add optionsLabel to panel with constraints (x is the same)
gc.gridy = 2;
add(optionsLabel, gc);
//add helpLabel to panel with constraints (x is the same)
gc.gridy = 3;
add(helpLabel, gc);
//add aboutLabel to panel with constraints (x is the same)
gc.gridy = 4;
add(aboutLabel, gc);
}
public static BufferedImage scaleImage(int w, int h, BufferedImage img) throws Exception {
BufferedImage bi;
//bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
bi = new BufferedImage(w, h, img.getType());
Graphics2D g2d = (Graphics2D) bi.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.addRenderingHints(new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY));
g2d.drawImage(img, 0, 0, w, h, null);
g2d.dispose();
return bi;
}
/*
* Will return the preffered size of JPanel
*/
#Override
public Dimension getPreferredSize() {
return preferredDimensions;
}
/*
* Will draw the background to JPanel with anti-aliasing on and quality rendering
*/
#Override
protected void paintComponent(Graphics grphcs) {
super.paintComponent(grphcs);
//convert graphics object to graphics2d object
Graphics2D g2d = (Graphics2D) grphcs;
//set anti-aliasing on and rendering etc
//GamePanel.applyRenderHints(g2d);
//draw the image as the background
g2d.drawImage(background, 0, 0, null);
//g2d.dispose();//if I uncomment this no LAbels will be shown
}
}
class PopUpJLabel extends JLabel {
public final static Font defaultFont = new Font("Arial", Font.PLAIN, 50);
public final static Font hoverFont = new Font("Arial", Font.BOLD, 70);
PopUpJLabel(String text) {
super(text);
setHorizontalAlignment(JLabel.CENTER);
setForeground(Color.ORANGE);
setFont(defaultFont);
//allow component to be focusable
setFocusable(true);
//add focus adapter to change fints when focus is gained or lost (used for transversing labels with keys)
addFocusListener(new FocusAdapter() {
#Override
public void focusGained(FocusEvent fe) {
super.focusGained(fe);
if (isEnabled()) {
setFont(getHoverFont());
}
}
#Override
public void focusLost(FocusEvent fe) {
super.focusLost(fe);
setFont(getDefaultFont());
}
});
addMouseListener(new MouseAdapter() {
#Override
public void mouseEntered(MouseEvent me) {
super.mouseEntered(me);
if (isEnabled()) {
setFont(getHoverFont());
}
//call for focus mouse is over this component
requestFocusInWindow();
}
});
}
Font getDefaultFont() {
return defaultFont;
}
Font getHoverFont() {
return hoverFont;
}
}
The thing is that the Graphics context you are using in paintComponent is created and provided by the caller (the framework), which is also responsible for disposing of it.
You only need to dispose of Graphics when you actually create it yourself (for example by calling Component.getGraphics()). In your case, you're not creating it, you're just casting it, so do not call dispose in this case.
Because .dispose() releases your resources. Yes, everything.
I am attempting to use a FlowLayout to get my panels to align vertically. I want the bottom right to align toward the bottom of the top right panel, not the bottom of that row.
Here is what I have done:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class BigPanel extends JPanel {
#Override
public Component.BaselineResizeBehavior getBaselineResizeBehavior() {
return Component.BaselineResizeBehavior.CONSTANT_ASCENT;
}
#Override
public int getBaseline(int width, int height) {
return 0;
}
public BigPanel() {
FlowLayout layout = new FlowLayout();
layout.setAlignOnBaseline(true);
this.setLayout(layout);
this.add(new Panel1()); // size: 340x, 160y
this.add(new Panel2()); // size: 340x, 120y
this.add(new Panel3()); // size: 340x, 160y
this.add(new Panel4()); // size: 340x, 300y
}
}
How can I simply anchor panels and components to a set of coordinates? I have run into this problem many times this last week and use ridiculous workarounds for my jlabels, etc.
How it looks now:
There are probably a few ways you can achieve this, but the simplest would be to use compound layouts.
Use two panels to act as the columns and then place these onto the main container. Into these you would then place you other components.
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 java.awt.GridLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.LineBorder;
public class CompundLayout {
public static void main(String[] args) {
new CompundLayout();
}
public CompundLayout() {
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());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
JPanel left = new JPanel();
JPanel right = new JPanel();
setLayout(new GridLayout(0, 2, 2, 2));
left.add(createPane(Color.RED));
left.add(createPane(Color.GREEN));
right.add(createPane(Color.BLUE));
right.add(createPane(Color.MAGENTA));
add(left);
add(right);
}
protected JPanel createPane(Color color) {
SimplePane pane = new SimplePane();
pane.setBorder(new LineBorder(color));
return pane;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.dispose();
}
}
public class SimplePane extends JPanel {
private int width;
private int height;
public SimplePane() {
width = 100;
height = 50 + (int) Math.round(Math.random() * 100);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(width, height);
}
}
}