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:
Related
I'm currently trying to create a primitive HUD inside a JFramewindow for a test game I am working on. In the course of doing this, all my attempts have been thwarted. Either the panel which contains all the buttons that are meant to represent the HUD does not display, or, the Canvas I attempt to draw to appears to not exist as no graphics I draw to it appear.
What I believe the problem is, is that the layers are not 'overlapping', and thus one is blocking out the other. However, I tried using a Flowlayout()(just for testing purposes) & that still did not fix my issue. So right now, I am at a loss for trying to have a HUD of buttons/Labels.
For visual purposes, I am trying to do something like this:
But I am actually getting this (just a test drawn tile, no HUD):
Here is the code that is involved:
public static void createDrawFrame(int width, int height) {
f = new JFrame();
c = new Canvas();
c.setPreferredSize(new Dimension(width, height));
c.setFocusable(false);
f.add(c);
f.pack();
f.setSize(width, height);
f.setLocationRelativeTo(null);
And
public static JFrame getDrawFrame() {
f.setVisible(true);
return f;
}
And
public static JFrame createHUD(JFrame f, Game game) {
Panel p = new Panel();
p.setLayout(new FlowLayout());
p.setSize(100, 100);
p.setLocation(300, 0);
Button b0 = new Button("Settings");
Button b1 = new Button("Exit");
MenuListeners mListeners = new MenuListeners(game);
b0.addActionListener(mListeners);
b1.addActionListener(mListeners);
p.add(b0);
p.add(b1);
f.add(p);
return f;
}
are called here:
private void init() {
Display.createDrawFrame(width, height);
Display.createMenuFrame(width, height);
this.f = Display.getDrawFrame();
this.c = Display.getCanvas();
this.f = HUDDisplay.createHUD(this.f, this);
MapAssets.init();
}
And are rendered here:
private void render() {
b = c.getBufferStrategy();
if(b == null) {
c.createBufferStrategy(3);
return;
}
do {
do {
g = b.getDrawGraphics();
g.clearRect(0, 0, width, height);
state.render(g);
b.show();
}while(b.contentsLost());
g.dispose();
}while(b.contentsRestored());
}
Hopefully I illustrated this problem correctly. To recap, I just want to know why my buttons are not displaying within the JFrame as they should be. Thanks for any help.
I think you can use JFrame's add method with BorderLayout to position the canvas and the buttons. I changed your code to show (demonstrate) it can be done. I hope it is what you are looking for.
I added the components (Canvas and Panel with buttons) to the JFrame using code like this to make them visible:
frame.add(panel, BorderLayout.NORTH); // panel with buttons
frame.add(canvas, BorderLayout.CENTER);
Here are links to Oracle's Java tutorials on Swing for further details; see the sections on Swing Components and Laying Out Components Within a Container.
Example code:
import java.awt.*;
import javax.swing.*;
public class FrameLayoutTest {
private static JFrame f;
private static Canvas c;
public static void main(String [] args) {
init();
}
private static void init() {
//Display.createDrawFrame(width, height);
createDrawFrame(400, 400);
//Display.createMenuFrame(width, height);
createHUD();
//this.f = Display.getDrawFrame();
//getDrawFrame();
//this.c = Display.getCanvas();
//this.f = createHUD(this.f, this);
//MapAssets.init();
}
public static void createDrawFrame(int width, int height) {
f = new JFrame();
c = new Canvas();
c.setBackground(Color.blue);
c.setPreferredSize(new Dimension(width, height));
c.setFocusable(false);
f.add(c, BorderLayout.CENTER); //f.add(c);
f.pack();
f.setVisible(true); // getDrawFrame()
f.setSize(width, height);
f.setLocationRelativeTo(null);
}
public static void createHUD() {
Panel p = new Panel();
p.setLayout(new FlowLayout());
p.setSize(100, 100);
p.setLocation(300, 0);
Button b0 = new Button("Settings");
Button b1 = new Button("Exit");
//MenuListeners mListeners = new MenuListeners(game);
//b0.addActionListener(mListeners);
//b1.addActionListener(mListeners);
p.add(b0);
p.add(b1);
f.add(p, BorderLayout.NORTH); //f.add(p);
}
}
I'm new to java and I'm trying to figure out how action listeners and buttons work. I've found that I can get a working JButton if I put it directly into my JFrame object. But if I put it in a JPanel within that JFrame, it won't respond. Why is that?
Main.java
public class Main {
private static Frame f = new Frame();
public static void main(String[] args) {}
}
Frame.java
public class Frame extends JFrame {
private final int WIDTH = 640, HEIGHT = 480;
private Panel p = new Panel();
Frame() {
super("Java Program");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(WIDTH, HEIGHT);
this.setLayout(null);
this.setLocationRelativeTo(null);
this.setVisible(true);
}
public void paint(Graphics g) {
super.paint(g);
p.paintComponent(g);
}
}
Panel.java
public class Panel extends JPanel {
JButton b = new JButton("Button");
Panel() {
b.setBounds(0, 0, 200, 100);
add(b);
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
b.setText("Pressed");
}
});
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
b.paint(g);
}
}
I am not a Swing expert so I can't really explain why it does not work. It seems like an unresponsive button is painted on top of you button. I tweaked it a little and here are a few modifications to get it to work:
Add the panel to the Frame: add(p);
Remove the this.setLayout(null); line, it seems to mess up the frame
To set the size of the Frame, use setPreferredSize: this.setPreferredSize(new Dimension(WIDTH, HEIGHT));
You also need to call pack() at then end of your Frame constructor.
And you need to remove b.paint(g) from your Panel.paintComponent(), this seems to be what paints the "unresponsive" button, (see image at the end of the answer).
Optionally, you can remove the paint() from the Frame, it does nothing more than the JFrame's one
Here is a modified working version:
class Frame extends JFrame {
private final int WIDTH = 640, HEIGHT = 480;
private Panel p = new Panel();
Frame() {
super("Java Program");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setPreferredSize(new Dimension(WIDTH, HEIGHT));
this.setLocationRelativeTo(null);
this.setVisible(true);
// add the panel to the frame
add(p);
pack();
}
}
class Panel extends JPanel {
JButton b = new JButton("Button");
Panel() {
b.setBounds(0, 0, 200, 100);
add(b);
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
b.setText("Pressed");
}
});
}
// You can also get rid of this method,
// I just leave it here to show that I removed the b.paint(g) line
public void paintComponent(Graphics g) {
super.paintComponent(g);
}
}
Here is what the same code shows if you leave b.paint(g) in Panel.paintComponent(), as you can see there are 2 buttons, the one in the corner does not work.
public class InputPanel extends JPanel{
public static int shapeType; //1: Rectangle; 2: Oval; 3: Line
public static boolean isFilled; //whether or not the shape is filled
public static Color color; //color of the shape
public InputPanel(){
JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT));
panel.setBackground(Color.GRAY);
setPreferredSize(new Dimension(200,500));
JButton rect = new JButton("Rectangle");
JButton oval = new JButton("Oval");
JButton line = new JButton("Line");
JRadioButton fill = new JRadioButton("Filled:");
JButton color1 = new JButton("Color..");
rect.addActionListener(new rectListener());
oval.addActionListener(new ovalListener());
line.addActionListener(new lineListener());
isFilled = fill.isSelected();
color1.addActionListener(new colorListener());
panel.add(rect);
panel.add(oval);
panel.add(line);
panel.add(fill);
panel.add(color1);
this.setVisible(true);
}
}
public class PaintPanel extends JPanel{
public static int x1, y1, x2, y2;
ArrayList<Shape> shapeList = new ArrayList<Shape>();
public PaintPanel(){
JPanel panel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
panel.setBackground(Color.WHITE);
setPreferredSize(new Dimension(500, 500));
this.addMouseListener(new MouseAdapter() {
#Override public void mousePressed(MouseEvent e) {
PaintPanel.x1 = e.getX();
PaintPanel.y1 = e.getY();
}
#Override public void mouseReleased(MouseEvent e) {
PaintPanel.x2 = e.getX();
PaintPanel.y2 = e.getY();
if(InputPanel.shapeType == 1){
shapeList.add(new Rectangle(PaintPanel.x1, PaintPanel.y1, PaintPanel.x2, PaintPanel.y2, InputPanel.isFilled));
}
if(InputPanel.shapeType == 2){
shapeList.add(new Oval(PaintPanel.x1, PaintPanel.y1, PaintPanel.x2, PaintPanel.y2, InputPanel.isFilled));
}
if(InputPanel.shapeType == 3){
shapeList.add(new Line(PaintPanel.x1, PaintPanel.y1, PaintPanel.x2, PaintPanel.y2));
}
repaint();
}
});
this.setVisible(true);
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
for(Shape s : shapeList){
s.draw(g);
}
}
}
public class PaintGUI {
public static void main(String[] args){
JFrame frame = new JFrame("Shape Drawer!");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new InputPanel());
frame.add(new PaintPanel());
frame.pack();
frame.setVisible(true);
}
}
I'm positive I've created the JFrame properly and all of my other classes work, but there must be something in here I'm missing...
When I run the main method all I get is a gray box that is clearly a square (500x500, as instantiated in the PaintPanel class. What am I doing wrong?
Apart from what Andrew mentioned, I noticed that within both your InputPanel and PaintPanel you're creating a new JPanel. You're adding new components to this panel, for sure, but at the end you're not adding this JPanel itself to your InputPanel or PaintPanel. So, make sure that in your constructors for these panels you have a add(panel) at the end.
Also, as a side note, do please keep in mind that most operations in Swing are not thread-safe and so read about "Concurrency in Swing" before creating/interacting with UI components. In other words, any updates to the user interface must happen on the event dispatch thread, like the start-up of your application:
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("Shape Drawer!");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//set the layout, add your panels
frame.pack();
frame.setVisible(true);
}
});
}
JFrame by default uses BorderLayout.
frame.add(new InputPanel());
frame.add(new PaintPanel());
is equivalent to saying,
frame.add(new InputPanel(), BorderLayout.CENTER);
frame.add(new PaintPanel(), BorderLayout.CENTER);
The net result being that the Panel that is added last would be the one that is visible, provided the rest of your code is working correctly.
Must add the panel to the frame, use:
this.add(panel);
I am using Swing and AWT (for the listeners) to make a small program. I have a problem concerning getting the size of my JPanel (the class named Chess).
My Layout:
public class Main extends JFrame implements MouseListener, ActionListener{
Chess chessPanel = new Chess ();
JButton newGameButton = new JButton ("New Game");
JButton loadGameButton = new JButton ("Load Game");
JButton saveGameButton = new JButton ("Save Game");
JButton exitButton = new JButton ("Exit");
public static void main (String [] args) {
new Main();
}
Main () {
super ("Chess");
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
setSize(dim);
setLocation(0,0);
setUndecorated(true);
chessPanel.addMouseListener(this);
add(chessPanel, BorderLayout.CENTER);
JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new FlowLayout());
newGameButton.addActionListener(this);
loadGameButton.addActionListener(this);
saveGameButton.addActionListener(this);
exitButton.addActionListener(this);
buttonPanel.add(newGameButton);
buttonPanel.add(loadGameButton);
buttonPanel.add(saveGameButton);
buttonPanel.add(exitButton);
add(buttonPanel, BorderLayout.SOUTH);
setVisible(true);
}
// ... Code ...
}
As you can see by the code, I have one JPanel in the CENTER, which takes nearly all the screen. In the bottom I have another JPanel (SOUTH), which has a row of buttons.
What I need is the size that the JPanel in the CENTER takes. When I call the getWidth(), getHeight() or getBounds() methods inherited from JPanel, they all return 0, because of the BorderLayout.
Any idea how to get the real values?
PS: The screen always takes up the entire screen, and will never be resized, if that helps.
You're likely calling getWidth before the JPanel has been rendered, and so it will be 0. The solution is to get the size after rendering, for instance after pack() or setVisible(true) has been called on the root container that holds this JPanel.
Also, I recommend against calling setSize() on anything since most of the standard layout managers observe the preferred size of a component, not the size, and when you call pack() telling the layout managers to do their thing, the set sizes are usually ignored. You may want to make your JPanel that is in the center set its own size by overriding its setPreferredSize method if it needs to be a certain size. Then let the JFrame and its held containers set the bet fit size based on the their layout managers when you call pack.
e.g.,
import java.awt.*;
import javax.swing.*;
public class Main extends JFrame {
Chess chessPanel = new Chess();
JButton newGameButton = new JButton("New Game");
JButton loadGameButton = new JButton("Load Game");
JButton saveGameButton = new JButton("Save Game");
JButton exitButton = new JButton("Exit");
public static void main(String[] args) {
new Main();
}
Main() {
super("Chess");
add(chessPanel, BorderLayout.CENTER);
JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new FlowLayout());
buttonPanel.add(newGameButton);
buttonPanel.add(loadGameButton);
buttonPanel.add(saveGameButton);
buttonPanel.add(exitButton);
System.out.printf("chessPanel Size before rendering: %s%n", chessPanel.getSize());
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
add(buttonPanel, BorderLayout.SOUTH);
pack();
System.out.printf("chessPanel Size after rendering: %s%n", chessPanel.getSize());
setLocationRelativeTo(null);
setVisible(true);
}
// ... Code ...
}
#SuppressWarnings("serial")
class Chess extends JPanel {
private static final int CHESS_WIDTH = 600;
private static final int CHESS_HEIGHT = CHESS_WIDTH;
private static final int MAX_ROW = 8;
private static final int MAX_COL = 8;
private static final Color LIGHT_COLOR = new Color(240, 190, 40);
private static final Color DARK_COLOR = new Color(180, 50, 0);
#Override
public Dimension getPreferredSize() {
return new Dimension(CHESS_WIDTH, CHESS_HEIGHT);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int panelWidth = getWidth();
int panelHeight = getHeight();
int sqrWidth = panelWidth / MAX_ROW;
int sqrHeight = panelHeight / MAX_COL;
for (int row = 0; row < MAX_ROW; row++) {
for (int col = 0; col < MAX_COL; col++) {
Color c = (row % 2 == col % 2) ? LIGHT_COLOR : DARK_COLOR;
g.setColor(c);
int x = (row * panelWidth) / MAX_ROW;
int y = (col * panelHeight) / MAX_COL;
g.fillRect(x, y, sqrWidth, sqrHeight);
}
}
}
}
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);
}
}