set maximum size of JPanel inside BorderLayout.CENTER - java

I have a JPanel inside BorderLayout.CENTER
The JPanel has a Grid Layout, and I want it to expand with the CENTER for its width, but the height must stop at a maximum and use the preferredSize when possible.
I have this code
JPanel wrapperCenterPanel = new JPanel(new FlowLayout());
wrapperCenterPanel.add(centerPanel);
panel.add(wrapperCenterPanel, BorderLayout.CENTER);
centerPanel is my panel (uses GridLayout), I'm wrapping it with a FlowLayout panel, and putting this last one in the CENTER.
Now the size is the preferred one, but it's fixed!! The height doesn't shrink if necessary, and neither does the width.
How can I do this?

Try using a BoxLayout as the wrapper panel. A BoxLayout respects the maximum/minimum and preferred size of a component.

I think that not possible with BorderLayout, especially for BorderLayout.CENTER area, nor without side_effects as blinking UFO on the screen from the code
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ComponentEvent;
import javax.swing.JComponent;
import javax.swing.JFrame;
public class CustomComponent extends JFrame {
private static final long serialVersionUID = 1L;
public CustomComponent() {
setTitle("Custom Component Graphics2D");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void display() {
CustomComponents cc = new CustomComponents();
cc.addComponentListener(new java.awt.event.ComponentAdapter() {
#Override
public void componentResized(ComponentEvent event) {
setSize(Math.min(getPreferredSize().width, getWidth()),
Math.min(getPreferredSize().height, getHeight()));
}
});
add(cc, BorderLayout.CENTER);
CustomComponents cc1 = new CustomComponents();
add(cc1, BorderLayout.EAST);
pack();
// enforces the minimum size of both frame and component
setMinimumSize(getSize());
//setMaximumSize(getMaximumSize());
setVisible(true);
}
public static void main(String[] args) {
CustomComponent main = new CustomComponent();
main.display();
}
}
class CustomComponents extends JComponent {
private static final long serialVersionUID = 1L;
#Override
public Dimension getMinimumSize() {
return new Dimension(100, 100);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 300);
}
#Override
public Dimension getMaximumSize() {
return new Dimension(800, 600);
}
#Override
public void paintComponent(Graphics g) {
int margin = 10;
Dimension dim = getSize();
super.paintComponent(g);
g.setColor(Color.red);
g.fillRect(margin, margin, dim.width - margin * 2, dim.height - margin * 2);
}
}

The FLowLayout layout manager does not redistribute free space; it uses each component's preferred size (see FlowLayout documentation in the Java API).
I personally would change your wrapper panel's layout manager to a GridBagLayout, and add your centerPanel into it, specify a proper GridBagConstraints object to handle the space distribution as you need.

Related

How to make scroll pane appear after dynamically adding text fields to a JPanel?

I am trying to solve the following problem: I have a program, where text fields are being added dynamically to a JPanel, but when too many fields are added, I want a scrollbar to be shown, so that the user doesn't have to resize the window in order to see all the fields. So far I can generate the fields without a problem, but adding the scrollbar seems not to be working...
I have the following code:
import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.FlowLayout;
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.GridBagLayout;
import java.awt.GridBagConstraints;
import java.awt.Insets;
import javax.swing.JScrollBar;
public class AddRuleFrame extends JFrame implements ActionListener {
JPanel panel;
JPanel buttonPanel;
JScrollPane scroll;
private JButton btnAddType;
private JButton btnDeleteField;
private JButton btnSaveRule;
public AddRuleFrame() {
getContentPane().setLayout(new BorderLayout());
this.buttonPanel=new JPanel();
getContentPane().add(buttonPanel, BorderLayout.SOUTH);
buttonPanel.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));
//Initializing the JScrollPane
scroll = new JScrollPane(this.panel);
scroll.setViewportView(this.panel);
btnAddType = new JButton("Add type");
btnAddType.addActionListener(this);
buttonPanel.add(btnAddType);
btnDeleteField = new JButton("Delete field");
btnDeleteField.addActionListener(this);
buttonPanel.add(btnDeleteField);
btnSaveRule = new JButton("Save rule");
buttonPanel.add(btnSaveRule);
this.panel = new JPanel();
this.panel.setLayout(new FlowLayout());
getContentPane().add(panel, BorderLayout.CENTER);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(538, 487);
setVisible(true);
}
public void actionPerformed(ActionEvent evt) {
if(evt.getSource()==this.btnAddType){
this.panel.add(new JTextField(20));
this.panel.revalidate();
}
if(evt.getSource()==this.btnDeleteField){
System.out.println("delete pressed");
}
if(evt.getSource()==this.btnSaveRule){
System.out.println("");
}
validate();
}
public static void main(String[] args) {
AddRuleFrame frame = new AddRuleFrame();
}
}
Thank you!
The issue with the scroll pane simply was that you were adding it to nothing and setting its viewport before the panel was intialized.
However, I noticed a few other issues.
One issue is that FlowLayout adds components horizontally, so I've changed the layout of the panel to a BoxLayout and created a small subclass of JTextField to override the maximum size. (BoxLayout uses maximum sizes to size components, so without doing that the text fields get stretched to the height of the panel.)
I also used SwingUtilities.invokeLater to start the program on the Swing thread, as show in the Initial Threads tutorial.
Instead of calling setSize on a JFrame directly, I overrode getPreferredSize and calculated a size dynamically based on the screen dimensions, then called pack() to size the components automatically. In general, Swing isn't designed for explicitly setting pixel dimensions.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
class AddRuleFrame extends JFrame implements ActionListener {
private JPanel panel;
private JPanel buttonPanel;
private JScrollPane scroll;
private JButton btnAddType;
private JButton btnDeleteField;
private JButton btnSaveRule;
public AddRuleFrame() {
getContentPane().setLayout(new BorderLayout());
buttonPanel = new JPanel();
getContentPane().add(buttonPanel, BorderLayout.SOUTH);
buttonPanel.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));
btnAddType = new JButton("Add type");
btnAddType.addActionListener(this);
buttonPanel.add(btnAddType);
btnDeleteField = new JButton("Delete field");
btnDeleteField.addActionListener(this);
buttonPanel.add(btnDeleteField);
btnSaveRule = new JButton("Save rule");
buttonPanel.add(btnSaveRule);
panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
scroll = new JScrollPane(panel,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
getContentPane().add(scroll, BorderLayout.CENTER);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setLocationRelativeTo(null); // this centers the window
setVisible(true);
}
#Override
public void actionPerformed(ActionEvent evt) {
if (evt.getSource() == btnAddType) {
panel.add(new BoxyTextField(20));
panel.revalidate();
}
validate();
}
class BoxyTextField extends JTextField {
BoxyTextField(int width) {
super(width);
}
#Override
public Dimension getMaximumSize() {
Dimension size = super.getMaximumSize();
size.height = getPreferredSize().height;
return size;
}
}
#Override
public Dimension getPreferredSize() {
// See my exchange with MadProgrammer in the comments for
// a discussion of whether Toolkit#getScreenSize() is an
// appropriate way to get the screen dimensions for sizing
// a window.
// Dimension size = Toolkit.getDefaultToolkit().getScreenSize();
// This is the correct way, as suggested in the documentation
// for java.awt.GraphicsEnvironment#getMaximumWindowBounds():
GraphicsConfiguration config = getGraphicsConfiguration();
Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(config);
Dimension size = config.getBounds().getSize();
size.width -= insets.left + insets.right;
size.height -= insets.top + insets.bottom;
// Now we have the actual available space of the screen
// so we can compute a relative size for the JFrame.
size.width = size.width / 3;
size.height = size.height * 2 / 3;
return size;
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
AddRuleFrame frame = new AddRuleFrame();
}
});
}
}
For your comment, generally the correct way to add a gap between components with a BoxLayout is to use a filler component. This is discussed in the tutorial, which I already linked to.
So you might do something like this:
#Override
public void actionPerformed(ActionEvent evt) {
if (evt.getSource() == btnAddType) {
if (panel.getComponentCount() > 0) {
panel.add(Box.createVerticalStrut(10));
}
panel.add(new BoxyTextField(20));
panel.revalidate();
}
However, this creates a bit of an issue if you're planning on removing stuff dynamically, because you need to remember to remove the filler component as well:
if (evt.getSource() == btnDeleteField) {
int lastZIndex = panel.getComponentCount() - 1;
if (lastZIndex >= 0) {
panel.remove(lastZIndex);
if (lastZIndex > 0) {
panel.remove(lastZIndex - 1);
}
panel.revalidate();
}
}
validate();
panel.repaint();
}
So I think the best option is that instead of extending JTextField and adding the the text field and filler to the panel directly, extend JPanel, and do something like this:
class BoxyTextFieldCell extends JPanel {
JTextField jTextField;
BoxyTextFieldCell(int width, int margin) {
jTextField = new JTextField(width);
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
add(jTextField);
add(Box.createVerticalStrut(margin));
}
#Override
public Dimension getMaximumSize() {
Dimension size = super.getMaximumSize();
size.height = getPreferredSize().height;
return size;
}
}
#Override
public void actionPerformed(ActionEvent evt) {
if (evt.getSource() == btnAddType) {
panel.add(new BoxyTextFieldCell(20, 10));
panel.revalidate();
}
if (evt.getSource() == btnDeleteField) {
int lastZIndex = panel.getComponentCount() - 1;
if (lastZIndex >= 0) {
panel.remove(lastZIndex);
panel.revalidate();
}
}
validate();
panel.repaint();
}
Doing something like that certainly leaves you with a lot of flexibility.
Otherwise, I think you could also use an editable JTable with a single column (which more or less behaves just like a stack of text fields) and use setRowMargin(int). I guess it might end up being easier to use a JTable if you aren't very comfortable with using layouts yet. See e.g. here for examples of adding and removing rows in a JTable.
There are two problems :
1) You never add your JScrollPane to anything.
2) You set its viewport view to a Component that is null (not yet initialized).
This is a modified version of your constructor that fixes both problems (see comments in the code) :
public AddRuleFrame() {
getContentPane().setLayout(new BorderLayout());
buttonPanel = new JPanel();
getContentPane().add(buttonPanel, BorderLayout.SOUTH);
buttonPanel.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));
//Initializing the JScrollPane
btnAddType = new JButton("Add type");
btnAddType.addActionListener(this);
buttonPanel.add(btnAddType);
btnDeleteField = new JButton("Delete field");
btnDeleteField.addActionListener(this);
buttonPanel.add(btnDeleteField);
btnSaveRule = new JButton("Save rule");
buttonPanel.add(btnSaveRule);
panel = new JPanel();
panel.setLayout(new FlowLayout());
scroll = new JScrollPane(panel);
scroll.setViewportView(panel);// Set the viewport view only when the panel has been initialized
getContentPane().add(scroll, BorderLayout.CENTER);// Add the scrollpane, not the panel
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(538, 487);
setVisible(true);
}

Alignment of Single Characters in Java BoxLayout on Y-Axis Is Off-Center

There seems to be an issue with aligning certain characters to the center of a BoxLayout along the y-axis in Java. I don't know what could cause this, & I've created an SSCCE to demonstrate the effect. In the example, I only use the character 'a', & I draw a line down the direct middle of each JPanel to demonstrate how far off each case is from the center. The case with bold text seems to line up fine, but normal formatting & italics are both grossly off-center, despite using both setAlignmentX & setHorizontalAlignment. Any help on understanding this effect is appreciated.
In the case that somehow the problem is with Java on my specific computer, this is an image of what displays on my screen when I run the SSCCE, which loads three different JPanels with BoxLayouts along the y-axis & puts a centered JLabel with only the character 'a' in each:
& here is the code for the SSCCE:
import javax.swing.*;
import java.awt.*;
import javax.swing.border.*;
public class AlignmentTest extends JPanel
{
public AlignmentTest(char label, int style)
{
JLabel l = new JLabel(Character.toString(label));
setBorder(BorderFactory.createLineBorder(Color.BLACK,1));
setBackground(Color.WHITE);
setLayout(new BoxLayout(this,BoxLayout.Y_AXIS));
setPreferredSize(new Dimension(300,50));
add(Box.createVerticalGlue());
add(l);
l.setFont(l.getFont().deriveFont(style));
l.setAlignmentX(CENTER_ALIGNMENT);
l.setHorizontalAlignment(JLabel.CENTER);
add(Box.createVerticalGlue());
}
public static void main(String[] args)
{
JFrame f = new JFrame("Alignment Test");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLayout(new GridLayout(1,0,5,5));
f.add(new AlignmentTest('a',Font.PLAIN));
f.add(new AlignmentTest('a',Font.BOLD));
f.add(new AlignmentTest('a',Font.ITALIC));
f.pack();
f.setVisible(true);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawLine(getWidth()/2,0,getWidth()/2,getHeight());
}
}
Another way to avoid "Box Layout Features: … Any extra space appears at the right of the container", you would need to override the JLabel#getMinimumSize() method to return the same Dimension as JLabel#getPreferredSize().
Sorry, I misunderstood.
As #camickr has already said,
I would guess there is some weird rounding error in the layout. This seems like a bug to me.
is quite correct.
Fixed example:
//MinimumSize checkbox
//selected true: set min width = 100px
//selected false: set min width = 7px(default "a" width)
//Here's my attempt(I am running JDK 1.7.0_72 on Windows 7):
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
public class AlignmentTest4 extends JPanel {
private static boolean FLAG = false;
#Override public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawLine(getWidth() / 2, 0, getWidth() / 2, getHeight());
}
#Override public Dimension getPreferredSize() {
return new Dimension(300, 80);
}
public static JLabel makeLabel(String label, int style) {
JLabel l = new JLabel(label) {
#Override public Dimension getPreferredSize() {
return new Dimension(120, 30);
}
#Override public Dimension getMinimumSize() {
Dimension d = super.getMinimumSize();
if (FLAG) {
d.width = 100;
} else {
d.width = 7;
}
return d;
//if (FLAG) {
// return this.getPreferredSize();
//} else {
// return super.getMinimumSize();
//}
}
};
l.setOpaque(true);
l.setBackground(Color.ORANGE);
l.setFont(l.getFont().deriveFont(style));
l.setAlignmentX(Component.CENTER_ALIGNMENT);
l.setAlignmentY(Component.CENTER_ALIGNMENT);
l.setVerticalAlignment(SwingConstants.CENTER);
l.setVerticalTextPosition(SwingConstants.CENTER);
l.setHorizontalAlignment(SwingConstants.CENTER);
l.setHorizontalTextPosition(SwingConstants.CENTER);
return l;
}
public static JComponent makePanel() {
JPanel p = new JPanel(new GridLayout(0, 1, 5, 5));
JPanel p1 = new AlignmentTest4();
p1.setBorder(BorderFactory.createTitledBorder("BoxLayout.X_AXIS"));
p1.setLayout(new BoxLayout(p1, BoxLayout.X_AXIS));
p1.add(Box.createHorizontalGlue());
p1.add(makeLabel("a", Font.PLAIN));
p1.add(Box.createHorizontalGlue());
JPanel p2 = new AlignmentTest4();
p2.setBorder(BorderFactory.createTitledBorder("BoxLayout.Y_AXIS"));
p2.setLayout(new BoxLayout(p2, BoxLayout.Y_AXIS));
p2.add(Box.createVerticalGlue());
p2.add(makeLabel("a", Font.PLAIN));
p2.add(Box.createVerticalGlue());
for (JPanel c : Arrays.asList(p1, p2)) {
c.setBackground(Color.WHITE);
p.add(c);
}
return p;
}
public static JComponent makeUI() {
final JPanel p = new JPanel(new BorderLayout());
p.add(makePanel());
p.add(new JCheckBox(new AbstractAction("MinimumSize") {
#Override public void actionPerformed(ActionEvent e) {
FLAG = ((JCheckBox) e.getSource()).isSelected();
SwingUtilities.updateComponentTreeUI(p);
}
}), BorderLayout.SOUTH);
return p;
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override public void run() {
createAndShowGUI();
}
});
}
public static void createAndShowGUI() {
JFrame f = new JFrame("Alignment Test");
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(makeUI());
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
Using JDK7 on Windows 7 none of the characters are center aligned.
I made some changes to display a JTextField and I played with the columns of the JTextField (1, 3, 5). As the columns increased the center aligned improved and was reasonable at columns 5 and above.
So the problem is somehow related to the width of the component.
I would guess there is some weird rounding error in the layout. This seems like a bug to me.
In case you are interested in a layout that provides some similar functionality to the BoxLayout you can check out the Relative Layout. The changes to your example are minor:
import javax.swing.*;
import java.awt.*;
import javax.swing.border.*;
public class AlignmentTest extends JPanel
{
public AlignmentTest(char label, int style)
{
JLabel l = new JLabel(Character.toString(label));
setBorder(BorderFactory.createLineBorder(Color.BLACK,1));
setBackground(Color.WHITE);
// setLayout(new BoxLayout(this,BoxLayout.Y_AXIS));
setLayout(new RelativeLayout(RelativeLayout.Y_AXIS));
setPreferredSize(new Dimension(300,50));
// add(Box.createVerticalGlue());
add(Box.createVerticalGlue(), new Float(1));
add(l);
l.setFont(l.getFont().deriveFont(style));
l.setAlignmentX(CENTER_ALIGNMENT);
l.setHorizontalAlignment(JLabel.CENTER);
// add(Box.createVerticalGlue());
add(Box.createVerticalGlue(), new Float(1));
}
public static void main(String[] args)
{
JFrame f = new JFrame("Alignment Test");
JScrollPane scroller = new JScrollPane();
JPanel panel = new JPanel(new GridLayout(1,0,5,5));
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLayout(new GridLayout(1,0,5,5));
f.add(new AlignmentTest('a',Font.PLAIN));
f.add(new AlignmentTest('a',Font.BOLD));
f.add(new AlignmentTest('a',Font.ITALIC));
f.pack();
f.setVisible(true);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawLine(getWidth()/2,0,getWidth()/2,getHeight());
}
}
The effect you observe appears to be an artifact of the way BoxLayout works. Interpolating from How to Use BoxLayout: Box Layout Features, "When a BoxLayout lays out components from left to right, … Any extra space appears at the right of the container." When the enclosing container's initial size is a small multiple of the label's (fixed) size, as shown below, the anomaly is minimal; stretch the frame horizontally to see how it grows. One work-around would be to minimize the degree to which the enclosing container's preferred size is artificially enlarged.
import javax.swing.*;
import java.awt.*;
public class AlignmentTest extends JPanel {
private final JLabel l;
public AlignmentTest(String label, int style) {
setBorder(BorderFactory.createLineBorder(Color.BLACK, 1));
setBackground(Color.WHITE);
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
l = new JLabel(label, JLabel.CENTER);
l.setFont(l.getFont().deriveFont(style));
l.setAlignmentX(CENTER_ALIGNMENT);
l.setOpaque(true);
l.setBackground(Color.cyan);
add(Box.createVerticalGlue());
add(l);
add(Box.createVerticalGlue());
}
#Override
public Dimension getPreferredSize() {
int w = l.getPreferredSize().width;
int h = l.getPreferredSize().height;
return new Dimension(w * 3, h * 3);
}
public static void main(String[] args) {
JFrame f = new JFrame("Alignment Test");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLayout(new GridLayout(1, 0, 5, 5));
f.add(new AlignmentTest("aMa", Font.PLAIN));
f.add(new AlignmentTest("aMa", Font.BOLD));
f.add(new AlignmentTest("aMa", Font.ITALIC));
f.pack();
f.setVisible(true);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawLine(getWidth() / 2, 0, getWidth() / 2, getHeight());
}
}

EmptyBorders on JPanel in BoxLayout.X_AXIS

I have a custom JPanel, which the paintComponent method is overridden to paint an image.
I want to insert several of these custom panels vertically centered in a container. To do this I created a jpanel with BoxLayout.X_AXIS as layout manager.
This works great and shows what I want, but I would like to add margins between the custom panels.
The EmptyMargins are just ignored, and the tricky part is that I can't (or would not like to...) add struts or boxes between them because I need to get each custom panel from a loop which takes all components of the container and cast them into CustomPanel.
See the problem ? If I add struts between the panels there will be a cast exception and EmptyBorders aren't working... Any ideas welcome!
Note : I'm open to other layout manager propositions ! ;-)
Here is the code :
public class StackExemple {
public StackExemple() {
JFrame frame = new JFrame();
frame.setPreferredSize(new Dimension(600, 300));
JPanel container = new JPanel();
container.setPreferredSize(new Dimension(600, 300));
container.setLayout(new BoxLayout(container, BoxLayout.X_AXIS));
CustomPanel customPanel1 = new CustomPanel();
CustomPanel customPanel2 = new CustomPanel();
CustomPanel customPanel3 = new CustomPanel();
container.add(customPanel1);
container.add(customPanel2);
container.add(customPanel3);
frame.getContentPane().add(container);
frame.pack();
frame.setVisible(true);
//Loop which takes the custompanels
for(Component comp : container.getComponents()) {
CustomPanel panel = (CustomPanel)comp;
//DO SOMETHING
System.out.println("Hello World");
}
}
private class CustomPanel extends JPanel{
private BufferedImage image;
public CustomPanel() {
setPreferredSize(new Dimension(100, 100));
setMinimumSize(getPreferredSize());
setMaximumSize(getPreferredSize());
setBorder(BorderFactory.createEmptyBorder(0,50,0,0));
setBackground(Color.RED);
// try {
// image = ImageIO.read(ClassLoader.getSystemClassLoader().getResource("Ressources/img.png"));
// } catch (IOException ex) {
// System.out.println("Ooops... ");
// }
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
// int x = (this.getWidth() - image.getWidth()) / 2;
// int y = (this.getHeight() - image.getHeight()) / 2;
// g.drawImage(image, x, y, null);
}
}
}
Borders are correct, have to getBackground from parent for LineBorders
override Min / Max / PreferredSize for BoxLayout
BoxLayout accepting Min / Max / PreferredSize by default
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.image.BufferedImage;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class StackExemple {
public StackExemple() {
JFrame frame = new JFrame();
JPanel container = new JPanel();
container.setLayout(new BoxLayout(container, BoxLayout.X_AXIS));
CustomPanel customPanel1 = new CustomPanel(Color.blue);
CustomPanel customPanel2 = new CustomPanel(Color.red);
CustomPanel customPanel3 = new CustomPanel(Color.green);
container.add(customPanel1);
container.add(customPanel2);
container.add(customPanel3);
frame.getContentPane().add(container);
frame.pack();
frame.setVisible(true);
for (Component comp : container.getComponents()) {
CustomPanel panel = (CustomPanel) comp;
System.out.println("Hello World");
}
}
private class CustomPanel extends JPanel {
private BufferedImage image;
#Override
public Dimension getMinimumSize() {
return new Dimension(100, 80);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 160);
}
#Override
public Dimension getMaximumSize() {
return new Dimension(400, 320);
}
public CustomPanel(Color c) {
setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createEmptyBorder(10, 10, 10, 10),
BorderFactory.createLineBorder(Color.black, 1)));
//setBorder(BorderFactory.createCompoundBorder(
//BorderFactory.createLineBorder(Color.black, 1),
//BorderFactory.createEmptyBorder(10, 10, 10, 10)));
setBackground(c);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
StackExemple stackExemple = new StackExemple();
}
});
}
}
The underlying reason that the border appears to not be respected is that the panel by default is opaque, that is it guarantees to fill each pixel of its area with a fully solid background color. The area covered by the border is part of the panel's area, so must be filled with the panel's background as well.
As you seem to be doing custom painting anyway, you might consider to report its opaqueness as false and only paint the background (and/or the background image) inside the bordered area:
// in constructor
setOpaque(false);
#Override
protected void paintComponent(Graphics g) {
// take over background filling inside the border
Insets insets = getInsets();
g.setColor(getBackground());
g.fillRect(insets.left, insets.top,
getWidth() - insets.left - insets.right,
getHeight() - insets.top - insets.bottom);
super.paintComponent(g);
// for a background image, you would need to take the insets
// into account as well
// int x = (this.getWidth() - image.getWidth()) / 2;
// int y = (this.getHeight() - image.getHeight()) / 2;
// g.drawImage(image, x, y, null);
}

Drawing rectangles on a JPanel

I have a JScrollPane and on top of it I have a JPanel named 'panel1'.
I want some rectangles to be drawn on this JPanel.
I have a class named DrawRectPanel which extends JPanel and does all the drawing stuff.
The problem is that, I tried to draw the rectangles on panel1 by writing the following code :
panel1.add(new DrawRectPanel());
but nothing appeared on panel1
then I tried, just as a test to the class DrawRectPanel :
JFrame frame = new JFrame();
frame.setSize(1000, 500);
Container contentPane = frame.getContentPane();
contentPane.add(new DrawRectPanel());
frame.show();
This worked, and produced the drawings but on a separate JFrame
How can I draw the rectangles on panel1 ?
Thanks in advance.
EDIT :
code for DrawRectPanel
public class DrawRectPanel extends JPanel {
DrawRectPanel() {
Dimension g = new Dimension(400,400);
this.setPreferredSize(g);
System.out.println("label 1");
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("label 2");
g.setColor(Color.red);
g.fillRect(20, 10, 80, 30);
}
}
only label 1 is printed on the screen
still no idea,
for example
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JComponent;
import javax.swing.JFrame;
public class CustomComponent extends JFrame {
private static final long serialVersionUID = 1L;
public CustomComponent() {
setTitle("Custom Component Graphics2D");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void display() {
add(new CustomComponents());
pack();
// enforces the minimum size of both frame and component
setMinimumSize(getSize());
setVisible(true);
}
public static void main(String[] args) {
CustomComponent main = new CustomComponent();
main.display();
}
}
class CustomComponents extends JComponent {
private static final long serialVersionUID = 1L;
#Override
public Dimension getMinimumSize() {
return new Dimension(100, 100);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 300);
}
#Override
public void paintComponent(Graphics g) {
int margin = 10;
Dimension dim = getSize();
super.paintComponent(g);
g.setColor(Color.red);
g.fillRect(margin, margin, dim.width - margin * 2, dim.height - margin * 2);
}
}
instead of adding
contentPane.add(new DrawRectPanel());
you should do
contentPane.add(panel1);
Because you already have new DrawRectPanel in panel1. But in your code you are adding another instance of DrawRectPanel in contentPane. And never added panel1 in none of your container.
to fix your problem, change "paintComponent" to "paint" when the window repaints automatically, it should work.

How can I properly center a JPanel ( FIXED SIZE ) inside a JFrame?

Hi all!
I'm trying to solve an -apparently- simple problem, but I cannot fix it.
I'm working on a sample application with Java/Swing libraries;
I have a JFrame and a JPanel.
I just want to achieve the following objectives:
JPanel MUST be centered inside the JFrame.
JPanel MUST have ALWAYS the size that is specified with
setPreferredSize() method. It MUST NOT be resized under this size.
I tried by using a GridBagLayout: it's the ONLY way I can do it.
See the sample below:
/* file StackSample01.java */
import java.awt.*;
import javax.swing.*;
public class StackSample01 {
public static void main(String [] args) {
JFrame frame = new JFrame();
JPanel panel = new JPanel();
panel.setPreferredSize(new Dimension(100, 100));
panel.setBackground(Color.RED);
frame.setLayout(new GridBagLayout());
frame.add(panel, new GridBagConstraints());
frame.setSize(new Dimension(200, 200));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
Here a screenshot:
I would not use a GridBagLayout to do a thing too simple.
I tried a simplest solution, by using a Box, but this does not work:
Sample code:
/* file StackSample02.java */
import java.awt.*;
import javax.swing.*;
public class StackSample02 {
public static void main(String [] args) {
JFrame frame = new JFrame();
JPanel panel = new JPanel();
panel.setPreferredSize(new Dimension(100, 100));
panel.setBackground(Color.RED); // for debug
panel.setAlignmentX(JComponent.CENTER_ALIGNMENT); // have no effect
Box box = new Box(BoxLayout.Y_AXIS);
box.add(Box.createVerticalGlue());
box.add(panel);
box.add(Box.createVerticalGlue()); // causes a deformation
frame.add(box);
frame.setSize(new Dimension(200, 200));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
Here a screenshot,
Any ideas? Thanks to all :-)
BoxLayout can pretty to hold your setXxxSize(), then just add panel.setMaximumSize(new Dimension(100, 100));
and your output would be
Removed by setMinimumSize(notice if Container has greater size as ... )
import java.awt.*;
import javax.swing.*;
public class CustomComponent12 extends JFrame {
private static final long serialVersionUID = 1L;
public CustomComponent12() {
Box box = new Box(BoxLayout.Y_AXIS);
box.setAlignmentX(JComponent.CENTER_ALIGNMENT);
box.add(Box.createVerticalGlue());
box.add(new CustomComponents12());
box.add(Box.createVerticalGlue());
add(box);
pack();
setTitle("Custom Component Test / BoxLayout");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setMaximumSize(getMinimumSize());
setMinimumSize(getMinimumSize());
setPreferredSize(getPreferredSize());
setLocation(150, 150);
setVisible(true);
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
CustomComponent12 main = new CustomComponent12();
}
};
javax.swing.SwingUtilities.invokeLater(r);
}
}
class CustomComponents12 extends JPanel {
private static final long serialVersionUID = 1L;
#Override
public Dimension getMinimumSize() {
return new Dimension(100, 100);
}
#Override
public Dimension getMaximumSize() {
return new Dimension(100, 100);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(100, 100);
}
#Override
public void paintComponent(Graphics g) {
int margin = 10;
Dimension dim = getSize();
super.paintComponent(g);
g.setColor(Color.red);
g.fillRect(margin, margin, dim.width - margin * 2, dim.height - margin * 2);
}
}
First of all, thanks to all.
I reply another time to my own question, to show everyone the choice I have made.
See the sample code below;
As you can see, I have included only minimal steps which are absolutely necessary to achieve the goal.
/* file StackResponse.java */
import java.awt.*;
import javax.swing.*;
public class StackResponse {
public static void main(String [] args) {
JPanel panel = new JPanel();
Dimension expectedDimension = new Dimension(100, 100);
panel.setPreferredSize(expectedDimension);
panel.setMaximumSize(expectedDimension);
panel.setMinimumSize(expectedDimension);
panel.setBackground(Color.RED); // for debug only
Box box = new Box(BoxLayout.Y_AXIS);
box.add(Box.createVerticalGlue());
box.add(panel);
box.add(Box.createVerticalGlue());
JFrame frame = new JFrame();
frame.add(box);
frame.setSize(new Dimension(200, 200));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setMinimumSize(frame.getMinimumSize()); // cannot be resized-
frame.setVisible(true);
}
}
Here you can see a screenshot.
Problem solved.
Many thanks again to all.
IT
create a panel by name "FixedPanel" with GridBagLayout and set preferred size to frame size
then add your frame into FixedPanel.
Frame = new JFrame("CenterFrame");
Frame.setLocation(0, 0);
Frame.setSize(new Dimension(400,400));//dim
JPanel FixedPanel = new JPanel(new GridBagLayout());
FixedPanel.setPreferredSize(Frame.getSize());
JPanel myPanel = new JPanel();
myPanel.setPreferredSize(new Dimension(100,100));
myPanel.setBackground(Color.BLACK);
FixedPanel.add(myPanel);
Frame.add(FixedPanel);
Frame.setVisible(true);
You can do this. I had to make a chess game, and I wanted the chess piece piece to always go in the center of a cell which was a JlayeredPane:
private void formMouseReleased(java.awt.event.MouseEvent evt) {
// TODO add your handling code here:
if (jl != null)
{
jl.setLocation(evt.getX()+10, evt.getY()+10);
Component com = findComponentAt(evt.getPoint());
if (com instanceof JPanel)
{
// System.out.println("Yes, it's a jpanel");
((JPanel)com).add(jl);
((JPanel)com).validate();
}
}
}
Its Just Having
jPanel.setBounds(x, y, 1046, 503);
Where x is space for right side and y is space for left side.
you have to calculate the space from both side according to screen height and width
use
panel.setMaximumSize(new Dimension(200,200));
panel.setResizable(false)
instead?

Categories

Resources