I'm making a score-keeping program, but I'm running into a problem. What I've tried to do is have a JPanel at the top that contains two JPanels, which, in turn, contains two team names. I'm confused as to why the two JLabels at the top of the program aren't centered inside of the JPanels they're contained in.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ScoreFrame extends JFrame {
private static final Dimension SCREEN_SIZE = Toolkit.getDefaultToolkit().getScreenSize();
private static final int WIDTH = SCREEN_SIZE.width;
private static final int HEIGHT = SCREEN_SIZE.height;
private final JTextField[] nameField = new JTextField[] { new JTextField(), new JTextField() };
private final JLabel[] nameLabel = new JLabel[] { new JLabel("Team 1"), new JLabel("Team 2") };
private final GridBagLayout gridBag = new GridBagLayout();
private final GridBagConstraints constraints = new GridBagConstraints();
private final JPanel topPanel = new JPanel();
public ScoreFrame() {
super();
setResizable(false);
setSize(SCREEN_SIZE);
setLayout(gridBag);
setUndecorated(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
addKeyListener(new EscapeListener());
addComponents();
}
private void addComponents() {
addToTopPanel();
constraints.insets = new Insets(0, 0, (int) (HEIGHT * (double) 4 / 5), 0);
gridBag.setConstraints(topPanel, constraints);
add(topPanel);
}
private void addToTopPanel() {
final JPanel[] teamPanel = new JPanel[] { new JPanel(), new JPanel() };
topPanel.setLayout(gridBag);
topPanel.setSize(new Dimension(WIDTH, HEIGHT / 5));
Dimension teamPanelSize = new Dimension(WIDTH / 2, HEIGHT / 5);
teamPanel[0].setSize(teamPanelSize);
teamPanel[1].setSize(teamPanelSize);
Font nameFont = new Font("Times New Roman", Font.PLAIN, 50);
nameLabel[0].setFont(nameFont);
nameLabel[1].setFont(nameFont);
teamPanel[0].add(nameLabel[0]);
teamPanel[1].add(nameLabel[1]);
gridBag.setConstraints(teamPanel[0], constraints);
constraints.gridx = 1;
gridBag.setConstraints(teamPanel[1], constraints);
topPanel.add(teamPanel[0]);
topPanel.add(teamPanel[1]);
}
public void paint(Graphics g) {
super.paint(g);
int strokeSize = ((WIDTH + HEIGHT) / 2) / 300;
if (strokeSize < 1) {
strokeSize = 1;
}
final int fontSize = (int) (strokeSize * 12.5);
Graphics2D g2d = (Graphics2D) g;
g2d.setStroke(new BasicStroke(strokeSize));
g.drawLine(WIDTH / 2, 0, WIDTH / 2, HEIGHT / 5);
g.drawLine(WIDTH / 2, (int) (HEIGHT * (double) 105 / 400), WIDTH / 2, HEIGHT);
g.drawLine(0, HEIGHT / 5, WIDTH, HEIGHT / 5);
g.drawRect((int) (WIDTH * (double) 45 / 100), HEIGHT / 5, WIDTH / 10, (int) (HEIGHT * (double) 3 / 20));
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g.setFont(new Font("Times New Roman", Font.PLAIN, fontSize));
g.drawString("Errors", (int) (WIDTH * (double) 101 / 220), HEIGHT / 4);
}
private JFrame getFrame() {
return this;
}
public static void main(final String args[]) {
new ScoreFrame().setVisible(true);
}
public class EscapeListener implements KeyListener {
public void keyPressed(final KeyEvent event) {
if (event.getKeyCode() == 27) {
final int choice = JOptionPane.showConfirmDialog(getFrame(), "Do you want to exit the program?");
if (choice == 0) {
System.exit(0);
}
}
}
public void keyReleased(final KeyEvent event) {
}
public void keyTyped(final KeyEvent event) {
}
}
}
Invoking pack() is a critical step in using layouts. This example uses JLabel.CENTER and GridLayout to center the labels equally as the frame is resized. For simplicity, the center panel is simply a placeholder. This somewhat more complex example uses a similar approach along with java.text.MessageFormat.
Addendum: But how would I apply pack() to my code?
Simply invoke pack() as shown in the examples cited. I don't see an easy way to salvage your current approach of setting sizes extrinsically. Instead, override getPreferredSize() in a JPanel for your main content. No matter the screen size, your implementation of paintComponent() should adapt to the current size, for example.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
/** #see https://stackoverflow.com/a/14422016/230513 */
public class Scores {
private final JLabel[] nameLabel = new JLabel[]{
new JLabel("Team 1", JLabel.CENTER),
new JLabel("Team 2", JLabel.CENTER)};
private void display() {
JFrame f = new JFrame("Scores");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel teamPanel = new JPanel(new GridLayout(1, 0));
teamPanel.add(nameLabel[0]);
teamPanel.add(nameLabel[1]);
f.add(teamPanel, BorderLayout.NORTH);
f.add(new JPanel() {
#Override
public Dimension getPreferredSize() {
return new Dimension(320, 240);
}
}, BorderLayout.CENTER);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new Scores().display();
}
});
}
}
Related
I'm trying to make a login page for an idea I'm working on and am trying to center two buttons. When I get the screen dimensions and divide them by 2 it is not centered. Here's my code:
import javax.swing.*;
import java.awt.*;
public class ChatWindow extends JFrame {
public ChatWindow() {
JFrame frame = new JFrame("EasyChat");
JButton login = new JButton("Login");
JButton signup = new JButton("Don't have an account? Sign Up");
JPanel mainPanel = new JPanel();
Dimension ss = Toolkit.getDefaultToolkit().getScreenSize();
frame.setVisible(true);
frame.setLayout(null);
frame.setSize(800,450);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
frame.getContentPane().add(login);
frame.getContentPane().add(signup);
login.setPreferredSize(new Dimension(25,60));
login.setFont(new Font("HelveticaNeue", Font.BOLD, 20));
signup.setBounds(ss.width / 2, ss.height / 2 + 125, 200, 100);
login.setBounds(ss.width / 2, ss.height / 2, 200, 100);
mainPanel.setLayout(new BorderLayout());
mainPanel.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
}
}
I also want to know how to make the buttons stayed centered if the user exits fullscreen mode.
Thank you.
Make use of the available layout managers. See Laying Out Components Within a Container for more details
For simplicity, I've just use GridBagLayout as this will centre the components within the container by default
And you get resisability for free
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
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() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setBorder(new EmptyBorder(16, 16, 16, 16));
setLayout(new GridBagLayout());
add(new JButton("Login"));
add(new JButton("Sign Up"));
}
}
}
Now, if you want the buttons to be the same, you might be able to get it to work using something like GridLayout,
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
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() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setBorder(new EmptyBorder(32, 32, 32, 32));
setLayout(new GridBagLayout());
JPanel buttonPane = new JPanel(new GridLayout(1, -1));
buttonPane.add(new JButton("This is a long button"));
buttonPane.add(new JButton("Sign Up"));
add(buttonPane);
}
}
}
Or you could use a custom layout manager, for example...
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Insets;
import java.awt.LayoutManager2;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
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() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setBorder(new EmptyBorder(32, 32, 32, 32));
setLayout(new ButtonLayoutManager());
add(new JButton("This is a long button"));
add(new JButton("Sign Up"));
}
}
public class ButtonLayoutManager implements LayoutManager2 {
private int horizontalPadding = 0;
#Override
public void addLayoutComponent(Component comp, Object constraints) {
}
#Override
public void addLayoutComponent(String name, Component comp) {
}
#Override
public void removeLayoutComponent(Component comp) {
}
#Override
public void invalidateLayout(Container target) {
}
#Override
public Dimension maximumLayoutSize(Container target) {
return preferredLayoutSize(target);
}
#Override
public Dimension minimumLayoutSize(Container parent) {
return preferredLayoutSize(parent);
}
#Override
public Dimension preferredLayoutSize(Container parent) {
int height = 0;
int width = 0;
for (Component comp : parent.getComponents()) {
height = Math.max(comp.getPreferredSize().height, height);
width = Math.max(comp.getPreferredSize().width, width);
}
width = (width * parent.getComponentCount()) + (horizontalPadding * parent.getComponentCount() - 1);
Insets insets = parent.getInsets();
width += insets.left + insets.right;
height += insets.top + insets.bottom;
return new Dimension(width, height);
}
#Override
public float getLayoutAlignmentX(Container target) {
return 0.5f;
}
#Override
public float getLayoutAlignmentY(Container target) {
return 0.5f;
}
#Override
public void layoutContainer(Container parent) {
int width = parent.getWidth();
int height = parent.getHeight();
Insets insets = parent.getInsets();
int maxWidth = 0;
int maxHeight = 0;
for (Component comp : parent.getComponents()) {
maxWidth = Math.max(comp.getPreferredSize().width, maxWidth);
maxHeight = Math.max(comp.getPreferredSize().height, maxHeight);
}
int padding = (horizontalPadding * parent.getComponentCount() - 1);
int totalWidth = padding + (maxWidth * parent.getComponentCount());
int yOffset = (height - maxHeight) / 2;
int xOffset = (width - totalWidth) / 2;
for (Component comp : parent.getComponents()) {
comp.setBounds(xOffset, yOffset, maxWidth, maxHeight);
xOffset += horizontalPadding + maxWidth;
}
}
}
}
nb: I've not done extensive testing on this and is only meant for demonstration purposes
This answer seems to use the (first) approach detailed by MadProgrammer, but since I have it ready.
import java.awt.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
public class CenteredButtons {
public CenteredButtons() {
JFrame f = new JFrame("Centered Buttons");
// A FlowLayout might also be used here
// Doing so would allow each button to be its natural size
JPanel buttonPanel = new JPanel(new GridLayout(1,0,20,20));
buttonPanel.add(new JButton("Yes"));
buttonPanel.add(new JButton("No"));
buttonPanel.setBorder(new EmptyBorder(30,75,30,75));
// a component (e.g. buttonPanel) added with no constraints will be centered
JPanel centerPanel = new JPanel(new GridBagLayout());
centerPanel.add(buttonPanel);
f.setContentPane(centerPanel);
f.pack(); // validates the layout and sets a size for the frame
f.setLocationRelativeTo(null); // centers the window on the screen
f.setExtendedState(JFrame.MAXIMIZED_BOTH); // maximizes the window
f.setVisible(true);
}
public static void main(String[] args) {
Runnable r = () -> new CenteredButtons();
SwingUtilities.invokeLater(r);
}
}
You need to subtract half the width and half the height of each of the buttons from the screen dimensions to center the two widgets.
Change the code where you set the bounds on the buttons to the following:
signup.setBounds(ss.width / 2 - 200 / 2, ss.height / 2 + 125 - 100 / 2, 200, 100);
login.setBounds(ss.width / 2 - 200 / 2, ss.height / 2 - 100 / 2, 200, 100);
To be notified when the user exits full screen mode, add a WindowStateListener to the frame and reset the bounds on the buttons.
Here's the complete code:
import javax.swing.*;
import java.awt.*;
import java.awt.event.WindowEvent;
import java.awt.event.WindowStateListener;
public class ChatWindow extends JFrame implements WindowStateListener{
Dimension ss;
JButton login;
JButton signup;
JFrame frame;
public ChatWindow() {
frame = new JFrame("EasyChat");
frame.addWindowStateListener(this);
login = new JButton("Login");
signup = new JButton("Don't have an account? Sign Up");
JPanel mainPanel = new JPanel();
ss = Toolkit.getDefaultToolkit().getScreenSize();
frame.setVisible(true);
frame.setLayout(null);
frame.setSize(800,450);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
frame.getContentPane().add(login);
frame.getContentPane().add(signup);
login.setPreferredSize(new Dimension(25,60));
login.setFont(new Font("HelveticaNeue", Font.BOLD, 20));
signup.setBounds(ss.width / 2 - 200 / 2, ss.height / 2 + 125 - 100 / 2, 200, 100);
login.setBounds(ss.width / 2 - 200 / 2, ss.height / 2 - 100 / 2, 200, 100);
mainPanel.setLayout(new BorderLayout());
mainPanel.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
}
public static void main(String[] args) {
new ChatWindow();
}
#Override
public void windowStateChanged(WindowEvent e) {
Dimension ss = frame.getSize();
signup.setBounds(ss.width / 2 - 200 / 2, ss.height / 2 + 125 - 100 / 2, 200, 100);
login.setBounds(ss.width / 2 - 200 / 2, ss.height / 2 - 100 / 2, 200, 100);
}
}
Hello I am new to JAVA and recently studied graphics but got stuck on how to draw a circle (I learn on my own through Google) I would be happy if you help me with the following lines of code
(Do not refer to the background sub button)
public class Panel_ {
static JPanel panel1 = new JPanel();
public static Color randomColor() {
int r = (int) Math.round(Math.random() * 255 - 1);
int g = (int) Math.round(Math.random() * 255 - 1);
int b = (int) Math.round(Math.random() * 255 - 1);
Color R_col = new Color(r, g, b);
System.out.println(R_col);
return R_col;
}
public static void Panel() {
var color_changer = new JButton("To change color");
color_changer.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
panel1.setBackground(randomColor());
}
});
var Panel = new JPanel();
Panel.add(color_changer);
Panel.setBackground(Color.blue);
panel1.setBackground(Color.black);
panel1.setPreferredSize(new Dimension(400, 430));
var frame = new JFrame("Color changer");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(panel1, BorderLayout.NORTH);
frame.getContentPane().add(Panel, BorderLayout.SOUTH);
frame.pack();
frame.setSize(500, 500);
frame.setBackground(Color.blue);
frame.setVisible(true);
}
public void paintCircle(Graphics g) {
g.setColor(Color.blue);
g.fillOval(60, 80, 100, 100);
}
public static void main(String args[]) {
Panel();
}
}
Use paintComponent for panel1 which I changed the name to topPanel in the runnable code below:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import javax.swing.*;
public class Panel extends JFrame {
private static final long serialVersionUID = 1L;
private JPanel topPanel;
private JPanel panel;
private JButton color_changer;
public Panel() {
initializeForm();
}
private void initializeForm() {
setAlwaysOnTop(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBackground(Color.blue);
color_changer = new JButton("To change color");
color_changer.addActionListener((ActionEvent e) -> {
topPanel.setBackground(randomColor());
});
topPanel = new JPanel() {
private static final long serialVersionUID = 1L;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.blue);
g.fillOval(60, 80, 100, 100);
};
};
panel = new JPanel();
panel.add(color_changer);
panel.setBackground(Color.blue);
topPanel.setBackground(Color.black);
topPanel.setOpaque(true);
topPanel.setPreferredSize(new Dimension(400, 430));
getContentPane().add(topPanel, BorderLayout.NORTH);
getContentPane().add(panel, BorderLayout.SOUTH);
pack();
setLocationRelativeTo(null);
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(() -> {
new Panel().setVisible(true);
});
}
public static Color randomColor() {
int r = (int) Math.round(Math.random() * 255 - 1);
int g = (int) Math.round(Math.random() * 255 - 1);
int b = (int) Math.round(Math.random() * 255 - 1);
Color R_col = new Color(r, g, b);
System.out.println(R_col);
return R_col;
}
}
Say I have two components, A and B, in a JPanel. I want Component A to stay left aligned, while Component B does its best to stay in the middle of the panel. I mocked up the following demo (sorry for the quality, I made it in paint):
What I am doing now is using a GridBagLayout on the JPanel, and keeping A left aligned while keeping B centered, but B stays centered within the 2nd column, so it is centered in the space remaining after A is placed, instead of centered with respect to the panel as a whole.
I cannot use any 3rd party libraries for this. Is there a way to do this using pure Swing?
Edit:
Firefly's answer is correct (as marked) but I created an SSCCE showing my original problem (first row), Hovercraft Full Of Eels' attempted solution (second row), and the Firefly's correct solution (third row). I figured it can't hurt to post it:
package stackoverflow;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.LayoutManager2;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class StackOverflowTest extends JFrame
{
public StackOverflowTest()
{
super("Stack Overflow Test");
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
JPanel testPanel = new JPanel(new GridLayout(3, 1));
// set up grid bag layout example
JPanel gridBagPanel = new JPanel(new GridBagLayout());
GridBagConstraints gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.anchor = GridBagConstraints.LINE_START;
gridBagPanel.add(getA(), gridBagConstraints);
gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 0;
gridBagConstraints.anchor = GridBagConstraints.CENTER;
gridBagConstraints.weightx = 1.0;
gridBagPanel.add(getB(), gridBagConstraints);
testPanel.add(gridBagPanel);
// set up border layout panel
JPanel borderPanel = new JPanel(new BorderLayout());
borderPanel.add(getA(), BorderLayout.LINE_START);
JPanel flowPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 0));
borderPanel.add(flowPanel, BorderLayout.CENTER);
flowPanel.add(getB());
testPanel.add(borderPanel);
// set up sly493 layout panel
JPanel sly493LayoutPanel = new JPanel(new Sly493LayoutManager());
sly493LayoutPanel.add(getA(), Sly493LayoutManager.LEFT);
sly493LayoutPanel.add(getB(), Sly493LayoutManager.CENTERED);
testPanel.add(sly493LayoutPanel);
// set up panel to act as the midpoint marker
JPanel midpointMarkerPanel = new JPanel()
{
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.BLACK);
int w = getWidth();
int h = getHeight();
int x = w / 2;
g2.drawLine(x, 0, x, h);
g2.drawLine(x, 0, x - (h / 5), (h / 5));
g2.drawLine(x, 0, x + (h / 5), (h / 5));
}
};
midpointMarkerPanel.setPreferredSize(new Dimension(1, 50));
// setup up content pane
JPanel contentPane = new JPanel(new BorderLayout());
contentPane.add(testPanel, BorderLayout.NORTH);
contentPane.add(midpointMarkerPanel, BorderLayout.CENTER);
this.setContentPane(contentPane);
pack();
}
private JPanel getA()
{
JPanel aPanel = new JPanel(new BorderLayout());
JLabel aLabel = new JLabel("A", JLabel.CENTER);
aLabel.setFont(aLabel.getFont().deriveFont(Font.BOLD, 36));
aPanel.add(aLabel, BorderLayout.CENTER);
aPanel.setBackground(Color.RED);
aPanel.setPreferredSize(new Dimension(50, 50));
return aPanel;
}
private JPanel getB()
{
JPanel bPanel = new JPanel();
JLabel bLabel = new JLabel("B", JLabel.CENTER);
bLabel.setForeground(Color.WHITE);
bLabel.setFont(bLabel.getFont().deriveFont(Font.BOLD, 36));
bPanel.add(bLabel, BorderLayout.CENTER);
bPanel.setBackground(Color.BLUE);
bPanel.setPreferredSize(new Dimension(50, 50));
return bPanel;
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
new StackOverflowTest().setVisible(true);
}
});
}
private static class Sly493LayoutManager implements LayoutManager2
{
public static final Integer LEFT = 0;
public static final Integer CENTERED = 1;
private Component leftComponent;
private Component centeredComponent;
#Override
public void addLayoutComponent(String name, Component comp)
{
}
#Override
public void removeLayoutComponent(Component comp)
{
if (leftComponent == comp)
{
leftComponent = null;
}
else if (centeredComponent == comp)
{
centeredComponent = null;
}
}
#Override
public Dimension preferredLayoutSize(Container parent)
{
Dimension d = new Dimension();
for (Component c : parent.getComponents())
{
//wide enough to stack the left and center components horizontally without overlap
d.width += c.getPreferredSize().width;
//tall enough to fit the tallest component
d.height = Math.max(d.height, c.getPreferredSize().height);
}
return d;
}
#Override
public Dimension minimumLayoutSize(Container parent)
{
return preferredLayoutSize(parent);
}
#Override
public void layoutContainer(Container parent)
{
//in this method we will:
//1) position the left component on the left edge of the parent and center it vertically
//2) position the center component in the center of the parent (as long as it would not overlap
//the left component) and center it vertically
int leftComponentWidth = leftComponent.getPreferredSize().width;
int leftComponentHeight = leftComponent.getPreferredSize().height;
int centeredComponentWidth = centeredComponent.getPreferredSize().width;
int centeredComponentHeight = centeredComponent.getPreferredSize().height;
leftComponent.setBounds(0, (parent.getHeight() - leftComponentHeight) / 2, leftComponentWidth, leftComponentHeight);
int leftComponentRightEdge = leftComponent.getX() + leftComponent.getWidth();
int centerComponentLeftEdge = (parent.getWidth() - centeredComponentWidth) / 2;
int centerComponentTopEdge = (parent.getHeight() - centeredComponentHeight) / 2;
if (leftComponentRightEdge >= centerComponentLeftEdge)
{
//Center component will "do its best" to remain in the center
//but it will not do so if it would cause it to overlap the left component
centerComponentLeftEdge = leftComponentRightEdge;
}
centeredComponent.setBounds(centerComponentLeftEdge,
centerComponentTopEdge,
centeredComponentWidth,
centeredComponentHeight);
}
#Override
public void addLayoutComponent(Component comp, Object constraints)
{
if (LEFT.equals(constraints))
{
if (leftComponent != null)
{
throw new IllegalStateException("A left component has already been assigned to this layout.");
}
leftComponent = comp;
}
else if (CENTERED.equals(constraints))
{
if (centeredComponent != null)
{
throw new IllegalStateException("A centered component has already been assigned to this layout.");
}
centeredComponent = comp;
}
else
{
throw new IllegalStateException("Unexpected constraints '" + constraints + "'.");
}
}
#Override
public Dimension maximumLayoutSize(Container target)
{
return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
}
#Override
public float getLayoutAlignmentX(Container target)
{
return 0;
}
#Override
public float getLayoutAlignmentY(Container target)
{
return 0;
}
#Override
public void invalidateLayout(Container target)
{
}
}
}
If I'm understanding your needs correctly, you want B centered relative to the parent as a whole, not centered in the space left over after A is positioned. That makes this problem interesting and after testing the other suggested answers, I don't believe they can meet that requirement.
I'm having trouble thinking of a way to combine the built-in layout managers in a way that would achieve that. So, I've hacked up a custom implementation of LayoutManager2.
The following executable example may meet your needs. The implementation is quick and dirty and is in no way an example of a good generalized layout manager, but it appears to meet your requirements and behaves like your drawings made me think it should. I interpreted your requirement that "B does its best to stay in the middle of the panel" to mean that B should try to remain centered relative to the panel as a whole, but not at the expense of overlapping A.
package com.example;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.LayoutManager2;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Example {
public Example() {
JPanel a = new JPanel();
a.setBackground(Color.RED);
a.setPreferredSize(new Dimension(128, 128));
JPanel b = new JPanel();
b.setBackground(Color.BLUE);
b.setPreferredSize(new Dimension(128, 128));
JPanel panel = new JPanel(new Sly493LayoutManager());
panel.add(a, Sly493LayoutManager.LEFT);
panel.add(b, Sly493LayoutManager.CENTERED);
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(panel);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
new Example();
}
private static class Sly493LayoutManager implements LayoutManager2 {
public static final Integer LEFT = 0;
public static final Integer CENTERED = 1;
private Component leftComponent;
private Component centeredComponent;
#Override
public void addLayoutComponent(String name, Component comp) { }
#Override
public void removeLayoutComponent(Component comp) {
if (leftComponent == comp) {
leftComponent = null;
} else if (centeredComponent == comp) {
centeredComponent = null;
}
}
#Override
public Dimension preferredLayoutSize(Container parent) {
Dimension d = new Dimension();
for (Component c : parent.getComponents()) {
//wide enough to stack the left and center components horizontally without overlap
d.width += c.getPreferredSize().width;
//tall enough to fit the tallest component
d.height = Math.max(d.height, c.getPreferredSize().height);
}
return d;
}
#Override
public Dimension minimumLayoutSize(Container parent) {
return preferredLayoutSize(parent);
}
#Override
public void layoutContainer(Container parent) {
//in this method we will:
//1) position the left component on the left edge of the parent and center it vertically
//2) position the center component in the center of the parent (as long as it would not overlap
//the left component) and center it vertically
int leftComponentWidth = leftComponent.getPreferredSize().width;
int leftComponentHeight = leftComponent.getPreferredSize().height;
int centeredComponentWidth = centeredComponent.getPreferredSize().width;
int centeredComponentHeight = centeredComponent.getPreferredSize().height;
leftComponent.setBounds(0, (parent.getHeight() - leftComponentHeight) / 2, leftComponentWidth, leftComponentHeight);
int leftComponentRightEdge = leftComponent.getX() + leftComponent.getWidth();
int centerComponentLeftEdge = (parent.getWidth() - centeredComponentWidth) / 2;
int centerComponentTopEdge = (parent.getHeight() - centeredComponentHeight) / 2;
if (leftComponentRightEdge >= centerComponentLeftEdge) {
//Center component will "do its best" to remain in the center
//but it will not do so if it would cause it to overlap the left component
centerComponentLeftEdge = leftComponentRightEdge;
}
centeredComponent.setBounds(centerComponentLeftEdge,
centerComponentTopEdge,
centeredComponentWidth,
centeredComponentHeight);
}
#Override
public void addLayoutComponent(Component comp, Object constraints) {
if (LEFT.equals(constraints)) {
if (leftComponent != null) {
throw new IllegalStateException("A left component has already been assigned to this layout.");
}
leftComponent = comp;
} else if (CENTERED.equals(constraints)) {
if (centeredComponent != null) {
throw new IllegalStateException("A centered component has already been assigned to this layout.");
}
centeredComponent = comp;
} else {
throw new IllegalStateException("Unexpected constraints '" + constraints + "'.");
}
}
#Override
public Dimension maximumLayoutSize(Container target) {
return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
}
#Override
public float getLayoutAlignmentX(Container target) {
return 0;
}
#Override
public float getLayoutAlignmentY(Container target) {
return 0;
}
#Override
public void invalidateLayout(Container target) {
}
}
}
Here's a really quick and dirty way, similar to Firefly's answer - just create a JPanel with null Layout, and place the two child panels in its paintComponent method:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Example extends JPanel {
protected JPanel a;
protected JPanel b;
int size = 200;
public Example() {
setLayout( null );
a = new JPanel();
a.setBackground( Color.red );
a.setPreferredSize( new Dimension( size, size ) );
b = new JPanel();
b.setBackground( Color.blue );
b.setPreferredSize( new Dimension( size, size ) );
add( a );
add( b );
setPreferredSize( new Dimension( 4 * size, size ) );
}
#Override
public void paintComponent( final Graphics g ) {
super.paintComponent( g );
a.setBounds( 0, 0, size, size );
int w = getWidth();
int x = (w - size) / 2;
if ( x < size ) {
x = size;
}
b.setBounds( x, 0, size, size );
}
public static void main( String[] args ) {
SwingUtilities.invokeLater( new Runnable() {
#Override
public void run() {
// Create and set up the window.
JFrame jf = new JFrame();
jf.setName( "Example" );
Example item = new Example();
jf.add( item );
// Display the window.
jf.pack();
jf.setVisible( true );
jf.addWindowListener( new WindowAdapter() {
#Override
public void windowClosing( WindowEvent arg0 ) {
System.exit( 0 );
}
} );
}
} );
}
}
Have the overall container use BorderLayout
Add A to its BorderLayout.LINE_START position.
Add another FlowLayout JPanel BorderLayout.CENTER. This panel will hold B.
Add B to the JPanel above. Since FlowLayout defaults to FlowLayout.CENTER, B should be centered in this JPanel.
For my practical purposes, I did not need precise centering. And I needed right alignment. And I needed two rows. And the text on the right was much shorter than the text in the center. So the trick is:
have both rows in a separate JPanel
use GridBagLayout
the "centered" item is really the left item, but it is right-aligned.
Some code from a real app (the {} blocks used to be in different functions, but that's not important now):
JPanel myPanel = new JPanel(new GridBagLayout());
JLabel centerFirst = new JLabel("first line");
JLabel rightFirst = new JLabel("...");
{
GridBagConstraints gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 0;
gridBagConstraints.anchor = GridBagConstraints.EAST;
gridBagConstraints.weightx = 0.5;
myPanel.add(centerFirst, gridBagConstraints);
}
{
GridBagConstraints gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 3;
gridBagConstraints.gridy = 0;
gridBagConstraints.anchor = GridBagConstraints.EAST;
gridBagConstraints.weightx = 0.5; // you don't really need 0.5 and 0.5, it may be 0.1 and 0.1, two equal values
myPanel.add(rightFirst, gridBagConstraints);
}
JLabel centerSecond = new JLabel("second line");
JLabel rightSecond = new JLabel("...");
{
GridBagConstraints gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 1;
gridBagConstraints.anchor = GridBagConstraints.EAST;
gridBagConstraints.weightx = 0.5;
myPanel.add(centerSecond, gridBagConstraints);
}
{
GridBagConstraints gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 3;
gridBagConstraints.gridy = 1;
gridBagConstraints.anchor = GridBagConstraints.EAST;
gridBagConstraints.weightx = 0.5;
myPanel.add(rightSecond, gridBagConstraints);
}
//...
// the main frame uses GridBagLayout too
JFrame frame = new JFrame();
{
GridBagLayout gbl = new GridBagLayout();
//...
frame.setLayout(gbl);
}
// myPanel: full width of the enclosing GridBagLayout
{
GridBagLayout gbl = (GridBagLayout) frame.getContentPane().getLayout();
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0; // it will occupy columns 0 and 1 of the GridBagLayout
gbc.gridy = 4; // and there will be stuff above it
gbc.gridheight = 1; // full width, therefore height=1 is enough
gbc.gridwidth = 2; // the number of columns in this GridBagLayout
gbc.fill = GridBagConstraints.HORIZONTAL;
gbl.setConstraints(myPanel, gbc);
frame.add(myPanel);
}
Box layout
container.add(componentA)
container.add(Box.createHorisontalGlue())
container.add(componentB)
container.add(Box.createHorisontalGlue())
I am very new to java in general and especially using Swing. I have created a custom swing component that zooms in and out on a picture using a slider. However I am now required to make it so that my code represents three classes
• Model
• The UI delegate
• An implementation of the component to bundle the model and UI delegate
together
I am very lost and cannot seem to get the code to function properly when I begin splitting it up into different classes. I have the interface working when implemented in a single class. Any help on how best to retool this code so that it fits into the previously mentioned model would be greatly appreciated. Here is my code.
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.event.*;
import java.io.File;
import java.net.URL;
import javax.imageio.ImageIO;
public class CustomComponent extends JComponent implements ChangeListener {
JPanel gui;
JLabel imageCanvas;
Dimension size;
double scale = 1.0;
private BufferedImage image;
public CustomComponent() {
size = new Dimension(10, 10);
setBackground(Color.black);
try {
image = ImageIO.read(new File("car.jpg"));
} catch (Exception ex) {
ex.printStackTrace();
}
}
public void setImage(Image image) {
imageCanvas.setIcon(new ImageIcon(image));
}
public void initComponents() {
if (gui == null) {
gui = new JPanel(new BorderLayout());
gui.setBorder(new EmptyBorder(5, 5, 5, 5));
imageCanvas = new JLabel();
JPanel imageCenter = new JPanel(new GridBagLayout());
imageCenter.add(imageCanvas);
JScrollPane imageScroll = new JScrollPane(imageCenter);
imageScroll.setPreferredSize(new Dimension(300, 100));
gui.add(imageScroll, BorderLayout.CENTER);
}
}
public Container getGui() {
initComponents();
return gui;
}
public void stateChanged(ChangeEvent e) {
int value = ((JSlider) e.getSource()).getValue();
scale = value / 100.0;
paintImage();
}
protected void paintImage() {
int imageWidth = image.getWidth();
int imageHeight = image.getHeight();
BufferedImage bi = new BufferedImage(
(int)(imageWidth*scale),
(int)(imageHeight*scale),
image.getType());
Graphics2D g2 = bi.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
AffineTransform at = AffineTransform.getTranslateInstance(0, 0);
at.scale(scale, scale);
g2.drawRenderedImage(image, at);
setImage(bi);
}
public Dimension getPreferredSize() {
int w = (int) (scale * size.width);
int h = (int) (scale * size.height);
return new Dimension(w, h);
}
private JSlider getControl() {
JSlider slider = new JSlider(JSlider.HORIZONTAL, 1, 500, 50);
slider.setMajorTickSpacing(50);
slider.setMinorTickSpacing(25);
slider.setPaintTicks(true);
slider.setPaintLabels(true);
slider.addChangeListener(this);
return slider;
}
public static void main(String[] args) {
CustomComponent app = new CustomComponent();
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(app.getGui());
app.setImage(app.image);
// frame.getContentPane().add(new JScrollPane(app));
frame.getContentPane().add(app.getControl(), "Last");
frame.setSize(700, 500);
frame.setLocation(200, 200);
frame.setVisible(true);
}
}
I am trying to get a JPanel to appear inside of another JPanel. Currently, the JPanel is in an external JFrame and loads with my other JFrame. I want the JPanel to be inside the other JPanel so the program does not open two different windows.
Here is a picture:
The small JPanel with the text logs I want inside of the main game frame. I've tried adding the panel to the panel, panel.add(othePanel). I've tried adding it the JFrame, frame.add(otherPanel). It just overwrites everything else and gives it a black background.
How can I add the panel, resize, and move it?
Edits:
That is where I want the chatbox to be.
Class code:
Left out top of class.
public static JPanel panel;
public static JTextArea textArea = new JTextArea(5, 30);
public static JTextField userInputField = new JTextField(30);
public static void write(String message) {
Chatbox.textArea.append("[Game]: " + message + "\n");
Chatbox.textArea.setCaretPosition(Chatbox.textArea.getDocument()
.getLength());
Chatbox.userInputField.setText("");
}
public Chatbox() {
panel = new JPanel();
panel.setPreferredSize(new Dimension(220, 40));
panel.setBackground(Color.BLACK);
JScrollPane scrollPane = new JScrollPane(textArea);
scrollPane.setPreferredSize(new Dimension(380, 100));
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
textArea.setEditable(false);
scrollPane
.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
userInputField.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
String fromUser = userInputField.getText();
if (fromUser != null) {
textArea.append(Frame.username + ":" + fromUser + "\n");
textArea.setCaretPosition(textArea.getDocument()
.getLength());
userInputField.setText("");
}
}
});
panel.add(userInputField, SwingConstants.CENTER);
panel.add(scrollPane, SwingConstants.CENTER);
//JFrame frame = new JFrame();
//frame.add(panel);
//frame.setSize(400, 170);
//frame.setVisible(true);
}
Main frame class:
public Frame() {
frame.getContentPane().remove(loginPanel);
frame.repaint();
String capName = capitalizeString(Frame.username);
name = new JLabel(capName);
new EnemyHealth("enemyhealth10.png");
new Health("health10.png");
new LoadRedCharacter("goingdown.gif");
new Spellbook();
new LoadMobs();
new LoadItems();
new Background();
new Inventory();
new ChatboxInterface();
frame.setBackground(Color.black);
Frame.redHealthLabel.setFont(new Font("Serif", Font.PLAIN, 20));
ticks.setFont(new Font("Serif", Font.PLAIN, 20));
ticks.setForeground(Color.yellow);
Frame.redHealthLabel.setForeground(Color.black);
// Inventory slots
panel.add(slot1);
panel.add(name);
name.setFont(new Font("Serif", Font.PLAIN, 20));
name.setForeground(Color.white);
panel.add(enemyHealthLabel);
panel.add(redHealthLabel);
panel.add(fireSpellBookLabel);
panel.add(iceSpellBookLabel);
panel.add(spiderLabel);
panel.add(appleLabel);
panel.add(fireMagicLabel);
panel.add(swordLabel);
// Character
panel.add(redCharacterLabel);
// Interface
panel.add(inventoryLabel);
panel.add(chatboxLabel);
// Background
panel.add(backgroundLabel);
frame.setContentPane(panel);
frame.getContentPane().invalidate();
frame.getContentPane().validate();
frame.getContentPane().repaint();
//I WOULD LIKE THE LOADING OF THE PANEL SOMEWHERE IN THIS CONSTRUCTOR.
new ResetEntities();
frame.repaint();
panel.setLayout(null);
Run.loadKeyListener();
Player.px = Connect.x;
Player.py = Connect.y;
new Mouse();
TextualMenu.rect = new Rectangle(Frame.inventoryLabel.getX() + 80,
Frame.inventoryLabel.getY() + 100,
Frame.inventoryLabel.getWidth(),
Frame.inventoryLabel.getHeight());
Player.startMessage();
}
Don't use static variables.
Don't use a null layout.
Use appropriate layout managers. Maybe the main panel uses a BorderLayout. Then you add your main component to the CENTER and a second panel to the EAST. The second panel can also use a BorderLayout. You can then add the two components to the NORTH, CENTER or SOUTH as you require.
For example, using a custom Border:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.RadialGradientPaint;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.border.AbstractBorder;
#SuppressWarnings("serial")
public class FrameEg extends JPanel {
public static final String FRAME_URL_PATH = "http://th02.deviantart.net/"
+ "fs70/PRE/i/2010/199/1/0/Just_Frames_5_by_ScrapBee.png";
public static final int INSET_GAP = 120;
private BufferedImage frameImg;
private BufferedImage smlFrameImg;
public FrameEg() {
try {
URL frameUrl = new URL(FRAME_URL_PATH);
frameImg = ImageIO.read(frameUrl);
final int smlFrameWidth = frameImg.getWidth() / 2;
final int smlFrameHeight = frameImg.getHeight() / 2;
smlFrameImg = new BufferedImage(smlFrameWidth, smlFrameHeight,
BufferedImage.TYPE_INT_ARGB);
Graphics g = smlFrameImg.getGraphics();
g.drawImage(frameImg, 0, 0, smlFrameWidth, smlFrameHeight, null);
g.dispose();
int top = INSET_GAP;
int left = top;
int bottom = top;
int right = left;
Insets insets = new Insets(top, left, bottom, right);
MyBorder myBorder = new MyBorder(frameImg, insets);
JTextArea textArea = new JTextArea(50, 60);
textArea.setWrapStyleWord(true);
textArea.setLineWrap(true);
for (int i = 0; i < 300; i++) {
textArea.append("Hello world! How is it going? ");
}
setLayout(new BorderLayout(1, 1));
setBackground(Color.black);
Dimension prefSize = new Dimension(frameImg.getWidth(),
frameImg.getHeight());
JPanel centerPanel = new MyPanel(prefSize);
centerPanel.setBorder(myBorder);
centerPanel.setLayout(new BorderLayout(1, 1));
centerPanel.add(new JScrollPane(textArea), BorderLayout.CENTER);
MyPanel rightUpperPanel = new MyPanel(new Dimension(smlFrameWidth,
smlFrameHeight));
MyPanel rightLowerPanel = new MyPanel(new Dimension(smlFrameWidth,
smlFrameHeight));
top = top / 2;
left = left / 2;
bottom = bottom / 2;
right = right / 2;
Insets smlInsets = new Insets(top, left, bottom, right);
rightUpperPanel.setBorder(new MyBorder(smlFrameImg, smlInsets));
rightUpperPanel.setLayout(new BorderLayout());
rightLowerPanel.setBorder(new MyBorder(smlFrameImg, smlInsets));
rightLowerPanel.setBackgroundImg(createBackgroundImg(rightLowerPanel
.getPreferredSize()));
JTextArea ruTextArea1 = new JTextArea(textArea.getDocument());
ruTextArea1.setWrapStyleWord(true);
ruTextArea1.setLineWrap(true);
rightUpperPanel.add(new JScrollPane(ruTextArea1), BorderLayout.CENTER);
JPanel rightPanel = new JPanel(new GridLayout(0, 1, 1, 1));
rightPanel.add(rightUpperPanel);
rightPanel.add(rightLowerPanel);
rightPanel.setOpaque(false);
add(centerPanel, BorderLayout.CENTER);
add(rightPanel, BorderLayout.EAST);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private BufferedImage createBackgroundImg(Dimension preferredSize) {
BufferedImage img = new BufferedImage(preferredSize.width,
preferredSize.height, BufferedImage.TYPE_INT_ARGB);
Point2D center = new Point2D.Float(img.getWidth()/2, img.getHeight()/2);
float radius = img.getWidth() / 2;
float[] dist = {0.0f, 1.0f};
Color centerColor = new Color(100, 100, 50);
Color outerColor = new Color(25, 25, 0);
Color[] colors = {centerColor , outerColor };
RadialGradientPaint paint = new RadialGradientPaint(center, radius, dist, colors);
Graphics2D g2 = img.createGraphics();
g2.setPaint(paint);
g2.fillRect(0, 0, img.getWidth(), img.getHeight());
g2.dispose();
return img;
}
private static void createAndShowGui() {
FrameEg mainPanel = new FrameEg();
JFrame frame = new JFrame("FrameEg");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.setResizable(false);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
#SuppressWarnings("serial")
class MyPanel extends JPanel {
private Dimension prefSize;
private BufferedImage backgroundImg;
public MyPanel(Dimension prefSize) {
this.prefSize = prefSize;
}
public void setBackgroundImg(BufferedImage background) {
this.backgroundImg = background;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (backgroundImg != null) {
g.drawImage(backgroundImg, 0, 0, this);
}
}
#Override
public Dimension getPreferredSize() {
return prefSize;
}
}
#SuppressWarnings("serial")
class MyBorder extends AbstractBorder {
private BufferedImage borderImg;
private Insets insets;
public MyBorder(BufferedImage borderImg, Insets insets) {
this.borderImg = borderImg;
this.insets = insets;
}
#Override
public void paintBorder(Component c, Graphics g, int x, int y, int width,
int height) {
g.drawImage(borderImg, 0, 0, c);
}
#Override
public Insets getBorderInsets(Component c) {
return insets;
}
}
Which would look like: