Well the title is quite self explanatory. I want to build two panels one on-top of each other in layers using java. I want the top layer to contain a JPanel which will contain a graphics2d object. I'd like both the JPanel and graphics2d to have transparent background (I still want the content drawn by the graphics2d visible). Does anyone have an idea how it can be done?
Call setOpaque(false) on the JPanel - that will not paint the JPanel's background.
Depending on what method you're overriding to get at the Graphics2D (JPanel's don't contain a Graphics2D object like a component - a Graphics2D object is used to paint the JPanel) - if it's paintComponent() you should read the JavaDocs for JComponent - and call super.paintComponent(g) first so that opacity is honored - and then do the rest of your painting.
Working example:
package com.stackoverflow.opaque;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class OpaqueExample extends JFrame {
private JLayeredPane layers;
private JPanel up, down;
private JButton toggleOpaque;
public OpaqueExample() {
layers = new JLayeredPane();
down = new JPanel();
down.setBackground(Color.GREEN);
down.setBounds(100, 100, 200, 200);
layers.add(down, new Integer(1));
up = new JPanel() {
public void paintComponent(Graphics og) {
super.paintComponent(og);
Graphics2D g = (Graphics2D)og;
GradientPaint gradient = new GradientPaint(0, 0, Color.BLUE, 10, 0,
Color.WHITE, true );
Polygon poly = new Polygon();
poly.addPoint(10, 10);
poly.addPoint(100, 50);
poly.addPoint(190, 10);
poly.addPoint(150, 100);
poly.addPoint(190, 190);
poly.addPoint(100, 150);
poly.addPoint(10, 190);
poly.addPoint(50, 100);
poly.addPoint(10, 10);
g.setPaint(gradient);
g.fill(poly);
g.setPaint(Color.BLACK);
g.draw(poly);
}
};
up.setBackground(Color.RED);
up.setBounds(150, 150, 200, 200);
layers.add(up, new Integer(2));
getContentPane().add(layers, BorderLayout.CENTER);
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
toggleOpaque = new JButton("Toggle Opaque");
toggleOpaque.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
up.setOpaque(!up.isOpaque());
layers.repaint();
}
});
buttonPanel.add(toggleOpaque);
getContentPane().add(buttonPanel, BorderLayout.EAST);
}
public static void main(String[] args) {
JFrame f = new OpaqueExample();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(500, 500);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
Related
My goal is to have a window with 2 panels in different colors in the background. They each cover a specific percentage of the screen and this changes periodically. I did this by creating a JSplitPane. But now I want to add a JLabel showing some data in front of all of this in the middle of the screen. How would I do this?
How about using the JLayer: How to Decorate Components with the JLayer Class (The Java™ Tutorials > Creating a GUI With JFC/Swing > Using Other Swing Features)
import java.awt.*;
import javax.swing.*;
import javax.swing.plaf.*;
public class JLayerTest {
public Component makeUI() {
JSplitPane splitPane = new JSplitPane();
splitPane.setResizeWeight(.4);
splitPane.setLeftComponent(makeLabel(Color.RED));
splitPane.setRightComponent(makeLabel(Color.GREEN));
//splitPane.setEnabled(false);
//splitPane.setDividerSize(0);
JPanel rubberStamp = new JPanel();
JLabel label = makeLabel(Color.BLUE);
label.setText("JLabel");
label.setForeground(Color.WHITE);
label.setBorder(BorderFactory.createLineBorder(Color.BLUE, 50));
LayerUI<JSplitPane> layerUI = new LayerUI<JSplitPane>() {
#Override public void paint(Graphics g, JComponent c) {
super.paint(g, c);
Dimension d = label.getPreferredSize();
int x = (c.getWidth() - d.width) / 2;
int y = (c.getHeight() - d.height) / 2;
SwingUtilities.paintComponent(g, label, rubberStamp, x, y, d.width, d.height);
}
};
return new JLayer<>(splitPane, layerUI);
}
public static JLabel makeLabel(Color color) {
JLabel label = new JLabel();
label.setOpaque(true);
label.setBackground(color);
return label;
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new JLayerTest().makeUI());
f.setSize(320, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
});
}
}
Considering your description, I prefer using a paintComponent approach. You just paint 2 rectangles on the background of components and still positioning components as usual, as simple as this:
JFrame f = new JFrame();
f.setPreferredSize(new Dimension(600, 600));
f.pack();
f.setLayout(new BorderLayout());
JPanel p = new JPanel(new FlowLayout()) {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int perc = (int)((float)getWidth()*0.3f); // set % to fill
g.setColor(Color.RED);
g.fillRect(0, 0, perc, g.getClipBounds().height);
g.setColor(Color.BLUE);
g.fillRect(perc, 0, getWidth()-perc, getHeight());
}
};
f.add(p);
p.add(new JButton("test"));
f.setVisible(true);
My example does it on a JPanel but it can be done directly on JFrame and then puts a JButton over it using a FlowLayout. Here is the result:
public void graph()
{
// Build the panel
JFrame graphWindow = new JFrame();
JPanel graphPanel = new JPanel();
Graphics g;
graphWindow.setTitle(inputField.getText());
graphWindow.getContentPane().add(graphPanel,"Center");
graphPanel.setBackground(Color.white);
graphWindow.setSize(600, 600);
graphWindow.setLocation(200,300);
graphWindow.setVisible(true);
g = graphPanel.getGraphics();
graphPanel.addMouseListener(this);
g.setFont(new Font("Times Roman", Font.BOLD, 20));
g.drawString("Test",100,100);
}
My issue is that when my program returns from the method, the drawing is erased and I'm left with a blank white window. If I capture the method in a while loop, it isn't erased. Is there some way to preserve the graphics after returning from the method?
Make a JFrame. Make it visible. Put a JPanel in it. Override paintComponent in the JPanel to draw whatever you are trying to draw.
For instance:
JPanel panel = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
// drawing code using g
}
};
JFrame frame = new JFrame();
frame.setContentPane(panel);
frame.setSize(600, 600);
frame.setLocation(200,300);
frame.setVisible(true);
As soon as the JPanel calls it's paintComponent method, it will replace your drawing with the default look of a panel. Instead, create your JPanel with a custom paintComponent method (you can do it as an anonymous class like the example below or create your own class extending from JPanel).
// Build the panel
JFrame graphWindow = new JFrame();
JPanel graphPanel = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setFont(new Font("Times Roman", Font.BOLD, 20));
g.drawString("Test", 100, 100);
}
};
graphWindow.setTitle("Blabla");
graphWindow.getContentPane().add(graphPanel, "Center");
graphPanel.setBackground(Color.white);
graphPanel.setOpaque(true); // make the JPanel opaque
graphWindow.setSize(600, 600);
graphWindow.setLocation(200, 300);
graphWindow.setVisible(true);
Also, I noticed that you want a white background. You have to make your JPanel opaque, to do so. I inserted a line in your code to do that.
I am trying to make a JFrame display a different JPanel when a specific tab is selected. I have tried adding code to make it add the new panel based on which tab index is selected.
Where am I going wrong with this? What do I need to add to make it work? Thanks.
EDIT
Here is my solved SSCCE:
import javax.swing.*;
import java.awt.*;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
public class MainPanel {
private static JTabbedPane tabbedPane = new JTabbedPane();
private static JFrame frame = new JFrame("Testing");
public static void main(String[] args) {
EventQueue.invokeLater(MainPanel::createAndShowGUI);
}
protected static void createAndShowGUI()
{
DrawGraphics drawGraphics = new DrawGraphics();
DrawDifferentGraphics drawDifferentGraphics = new DrawDifferentGraphics();
frame.setLayout(new BorderLayout());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(tabbedPane, BorderLayout.WEST);
tabbedPane.addTab("CFG", null);
tabbedPane.addTab("CNX", null);
frame.add(drawGraphics);
tabbedPane.addChangeListener(e -> {
if (tabbedPane.getSelectedIndex() == 0) {
frame.remove(drawDifferentGraphics);
frame.add(drawGraphics);
frame.validate();
frame.repaint();
}
if (tabbedPane.getSelectedIndex() == 1) {
frame.remove(drawGraphics);
frame.add(drawDifferentGraphics);
frame.validate();
frame.repaint();
}});
frame.setLocationByPlatform(true);
frame.setSize(400, 400);
frame.setVisible(true);
}
}
class DrawGraphics extends JPanel {
private ArrayList<Shape> shapes = new ArrayList<Shape>();
public DrawGraphics() {
setLayout(new BorderLayout());
shapes.add(new Ellipse2D.Double(10, 10, 20, 20));
shapes.add(new Ellipse2D.Double(10, 30, 20, 20));
shapes.add(new Ellipse2D.Double(10, 50, 20, 20));
shapes.add(new Ellipse2D.Double(10, 70, 20, 20));
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g.create();
g2d.setColor(Color.BLUE);
shapes.forEach(g2d::fill);
g2d.dispose();
}
}
class DrawDifferentGraphics extends JPanel {
private ArrayList<Shape> shapes = new ArrayList<Shape>();
public DrawDifferentGraphics() {
setLayout(new BorderLayout());
shapes.add(new Rectangle2D.Double(10, 10, 10, 10));
shapes.add(new Rectangle2D.Double(10, 30, 10, 10));
shapes.add(new Rectangle2D.Double(10, 50, 10, 10));
shapes.add(new Rectangle2D.Double(10, 70, 10, 10));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g.create();
g2d.setColor(Color.RED);
shapes.forEach(g2d::fill);
g2d.dispose();
}
}
I want to display the graphics on the panel next to the tabbedPane.
Read the section from the Swing tutorial on How to Write a ChangeListener.
You will be notified when a tab has been clicked. You then get the selected tab and add the panel to the frame.
So basically your if (tabbedPane.getSelectedIndex() == 0) logic would be moved to the ChangeListener.
Or instead of having a bunch of "if statement" you could have a Map of Integer/JPanel values. Then you just get the index and get the panel from the Map.
Once you add the panel to the frame you then need to revalidate() and repaint() the frame.
Edit:
Actually the above suggestion is not complete. You can't just keep adding panels to the frame. The CENTER area of the BorderLayout should only contain a single component, otherwise you can get painting problems.
This can be demonstrated by clicking on the unselected tab, and then resize the frame. The original panel will be displayed.
You need to do one of the following:
Use a CardLayout (read the tutorial if you haven't used layout before) on a penel in the CENTER of the BordreLayout. So in this case the panel using the CardLayout is the only component in the CENTER and then it manages the panel that is displayed in the CardLayout. So your ChangeListener would need to identify the card to be displayed. You could set the card identifier to be the text of the selected tab. So
Remove the current panel BEFORE adding the new panel. In this case there is only a single panel in the CENTER so painting is as expected.
I am having a little bit of trouble drawing components from an ArrayList I have created. If I screw around with it, I can either get the first element or the second element and if I am lucky, neither, to show up!
import java.awt.*;
import java.awt.geom.*;
import java.util.*;
import javax.swing.*;
public class FaceShortCode {
ArrayList<CreateCircles> faceCircles = new ArrayList<CreateCircles>();
public FaceShortCode() {
JFrame main = new JFrame();
main.setTitle("Face Frame");
main.setSize(new Dimension(600, 600));
main.setLocationRelativeTo(null);
main.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
main.setVisible(true);
Container c = main.getContentPane();
// c.setLayout(null);
faceCircles.add(0, new CreateCircles(100, 50, 400, 350, Color.red));
faceCircles.add(1, new CreateCircles(200, 100, 65, 65, Color.black));
c.add(faceCircles.get(0));
c.add(faceCircles.get(1));
}
class CreateCircles extends JComponent {
double x, y, width, height;
Color myColor;
public CreateCircles(double x, double y, double width, double height,
Color myColor) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.myColor = myColor;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
Ellipse2D circle = new Ellipse2D.Double(x, y, width, height);
g2.setColor(myColor);
g2.fill(circle);
}
}
static class Run {
public static void main(String[] args) {
new FaceShortCode();
}
}
}
I have tried throwing in a main.repaint() after every addition to the Container c,tried a repaint() in my painting method but nothing seems to be working. Is there any were else to put a repaint() that I am just missing?
You need to put all your logic before you setVisible() or else your frame will become visible before the logic is performed.
public FaceShortCode() {
Container c = main.getContentPane();
// c.setLayout(null);
faceCircles.add(0, new CreateCircles(100, 50, 400, 350, Color.red));
faceCircles.add(1, new CreateCircles(200, 100, 65, 65, Color.black));
c.add(faceCircles.get(0));
c.add(faceCircles.get(1));
JFrame main = new JFrame();
main.setTitle("Face Frame");
main.setSize(new Dimension(600, 600));
main.setLocationRelativeTo(null);
main.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
main.setVisible(true);
}
Consider making FaceShortCode extends JFrame.
public class FaceShortCode extends JFrame {
private ArrayList<CreateCircles> faceCircles = new ArrayList<CreateCircles>();
public FaceShortCode {
setLayout(new Girdlayout(1, 2));
faceCircles.add(0, new CreateCircles(100, 50, 400, 350, Color.red));
faceCircles.add(1, new CreateCircles(200, 100, 65, 65, Color.black));
add(faceCircles.get(0));
add(faceCircles.get(1))
setTitle("Face Frame");
setSize(new Dimension(600, 600));
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
}
Your design is a bit confusing. There is no need for an ArrayList. When you use components you can just add the components directly to the panel. Normally you would only use an ArrayList when you are doing custom painting by painting Objects that are not components, for example when you want to paint a Shape. For an example of this approach take a look at Custom Painting Approaches.
When using components, a component needs a size and location in order to be painted automatically by Swing. Normally you would let a layout manager determine these properties. In your case you are doing random placement of your components so you would need to use a null layout and then set the size/location of each component.
So what you need to do is change how you paint your custom component. All painting should be done at location (0, 0) of your component. Then you would set the location of the component to be the x/y value. This means the component will be painted at the x/y location relative to the panel you add the component to. Then you need to set the size of your component which would be the width/height.
I know there is a way to extend a JLabel to paint 3D borders and a way to paint round borders, but how do you get both? Here is my code
protected void paintComponent(Graphics g) {
g.setColor(getBackground());
g.fillRoundRect(0, 0, getWidth()-1, getHeight()-1, 25, 25);
g.fill3DRect(10, 10, 30, 30, true);
super.paintComponent(g);
Use LineBorder with rounded corners or a variant of the TextBubbleBorder.
You refer this code to create Round Corner JLabel:
import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
public class RoundedLineBorder extends JPanel {
public RoundedLineBorder() {
super(true);
setLayout(new BorderLayout());
JLabel label = new JLabel("Rounded Corners");
label.setHorizontalAlignment(JLabel.CENTER);
LineBorder line = new LineBorder(Color.blue, 2, true);
label.setBorder(line);
add(label, BorderLayout.CENTER);
}
public static void main(String s[]) {
JFrame frame = new JFrame("Rounded Line Border");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500, 200);
frame.setContentPane(new RoundedLineBorder());
frame.setVisible(true);
}
}