Java swing setLocation / setSize not working for JPanel - java

So I have made a chess game where Inside the JFrame there is a boardPanel (chessboard) and a sidePanel (contains buttons like flipBoardButton).
The whole JFrame has a Dimension of 900x640 and the boardPanel therefore is 640x640.
public class BoardPanel extends JPanel{
private static final long serialVersionUID = 1L;
public BoardPanel() {
this.setSize(640, 640);
this.setLocation(0, 0);
}
#Override
public void paint(Graphics g){
boolean white = true;
for(int y = 0; y < 8; y++){
for(int x = 0; x < 8; x++){
if(white){
g.setColor(new Color(235, 235, 235));
}
else{
g.setColor(new Color(166, 123, 90));
}
g.fillRect(x*80, y*80, 80, 80);
white = !white;
}
white = !white;
}
for(Piece p : BoardHandler.piecesOnBoard)
{
Image image;
image = p.getImage();
g.drawImage(image, p.getX(), p.getY(), this);
}
}
}
My SidePanel should be on the right (at x=641 y=0) and have a width of 900-640=260 and a height of 640...
public class SidePanel extends JPanel{
public SidePanel()
{
this.setSize(260,640);
this.setLocation(641, 0);
}
#Override
public void paint(Graphics g) {
g.setColor(new Color(50,50,50));
g.fillRect(this.getX(), this.getY(), this.getWidth(), this.getHeight());
}
}
This is how I implemented both the panels (usual stuff):
JFrame frame = new JFrame("Chess");
BoardPanel boardPanel = new BoardPanel();
SidePanel sidePanel = new SidePanel();
frame.add(boardPanel);
frame.add(sidePanel);
frame.setVisible(true);
And I thought everything is working because this is what I got:
BUT: when I go into SidePanel calss and change the setSize to 100x100 or when I set the Location to 700,0 , I get the SAME result !
However, when I change Location / Size in the BoardPanel class, it works perfectly fine ?! (the chessboard then gets rearranged/resized)
How is this possible when I used the "same" code for both classes !?

The whole JFrame has a Dimension of 900x640 and the boardPanel therefore is 640x640.
The frame and boardPanel can't possibly have the same height because the frame has a border and a titlebar. Don't attempt to set the size/location of components. That is the job of layout managers.
Many issues:
Swing components are responsible for determining their own size. So when you do custom painting you need to implement the getPreferredSize() method so the layout manager can do its job.
For the boardPanel is would be something like:
#Override
public Dimension getPreferredSize()
{
return new Dimension(640, 640);
}
By default a JFrame uses a BorderLayout. You do NOT specify a constraint when you add your components to the frame, so be default each component is added to the CENTER. However, only the last component is managed by the BorderLayout so it will set the size/location of the sidePanel, which is why your attempt to do so is ignored.
Because the BorderLayout ignores the chessBoard your attempt to set the size/location appears to work.
However you should not attempt to set the size/location. Let the layout manager do its job.
Instead your code should be:
frame.add(boardPanel, BorderLayout.CENTER);
frame.add(sidePanel, BorderLayout.LINE_START);
Custom painting is done by overriding the paintComponent(...) method, not paint() and you always invoke super.paintComponent(...) first to make sure the background of the panel is cleared.
Custom painting is relative to the component, not relative to its location in the frame.
The following code in your sidePanel class is wrong:
g.fillRect(this.getX(), this.getY(), this.getWidth(), this.getHeight());
The getX()/getY() is wrong. If you really need to do custom painting then you should just use (0, 0).
However, there is no need to even use custom painting.
In the constructor of your class you just use:
setBackground( new Color(...) );
and the background will be painted automatically.

Related

PaintComponent not being called with JPanel

when I run this code PaintComponent is never called because the "painted" message is never printed and I do not know why? can anyone help?
public class DisplayManager extends JPanel {
public static final int WIDTH = 700, HEIGHT = 900;
public Bottle bottle1 = new Bottle("res/bottleimage.png");
public Slider slider1 = new Slider();
public void initDisplay()
{
JFrame frame = new JFrame();
JPanel panel = new JPanel();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(new Dimension(WIDTH, HEIGHT));
frame.add(panel);
frame.setVisible(true);
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
bottle1.imageIcon.paintIcon(this, g, 50, 50);
System.out.println("painted");
}
}
There are a couple of problems with the basic code:
as already mentioned you need to add an instance of your DisplayManager class to a frame or panel.
When you do custom painting you need to override the getPreferredSize() method of the component to return your desired size. Currently the preferred size of your component is (0, 0).
The suggestion to add the DisplayManager to the frame only works because the default layout manager is a BorderLayout and by default is added to the CENTER of the layout which means it get all the available space in the frame.
However if you use:
frame.add(this, BorderLayout.PAGE_START);
you won't see the component size it has a size of (0, 0);

Nested panels with background image?

I have two panels defined like:
public class JPanel_with_BG extends JPanel
{
private Image bg_image;
public JPanel_with_BG(Image bg_image)
{
this.bg_image = bg_image;
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
if (bg_image != null)
{
g.drawImage(bg_image, 0, 0, this);
}
}
}
and,
public class MPanel extends JPanel
{
public void paint(Graphics g)
{
super.paint(g);
// draw something...
}
}
Then, I want to add the second panel over the bg_pnl. The top panel is smaller like bg_pnl.size = pnl + 2*padding.
MPanel pnl = new MPanel();
JPanel bg_pnl = new JPanel_with_BG(image);
int pad = 50;
bg_pnl.setBorder(new EmptyBorder(pad, pad, pad, pad));
bg_pnl.add(pnl);
The problem is that what I'm drawing on the top panel is not visible. What I can see is only the background image. Any ideas? Thanks.
You Code seems ok. I tried this at my end and I can see a small area on my UI showing MPanel. You need to validate the size of your panel on which you are showing this component.
It might happen that the area is not visible because of the dimension of the window. Also a panel's default layout is flow layout and it arranges the components added on it based on their size and if they are really small, It might not be visible

How does the opaque property work in Swing?

This a simple application I got from here this answer to How to set a Transparent Background of JPanel
that's supposed to explain how setOpaque() works.
public class TwoPanels {
public static void main(String[] args) {
JPanel p = new JPanel();
// setting layout to null so we can make panels overlap
p.setLayout(null);
CirclePanel topPanel = new CirclePanel();
// drawing should be in blue
topPanel.setForeground(Color.blue);
// background should be black, except it's not opaque, so
// background will not be drawn
topPanel.setBackground(Color.black);
// set opaque to false - background not drawn
topPanel.setOpaque(false);
topPanel.setBounds(50, 50, 100, 100);
// add topPanel - components paint in order added,
// so add topPanel first
p.add(topPanel);
CirclePanel bottomPanel = new CirclePanel();
// drawing in green
bottomPanel.setForeground(Color.green);
// background in cyan
bottomPanel.setBackground(Color.cyan);
// and it will show this time, because opaque is true
bottomPanel.setOpaque(true);
bottomPanel.setBounds(30, 30, 100, 100);
// add bottomPanel last...
p.add(bottomPanel);
// frame handling code...
JFrame f = new JFrame("Two Panels");
f.setContentPane(p);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(300, 300);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
// Panel with a circle drawn on it.
private static class CirclePanel extends JPanel {
// This is Swing, so override paint*Component* - not paint
protected void paintComponent(Graphics g) {
// call super.paintComponent to get default Swing
// painting behavior (opaque honored, etc.)
super.paintComponent(g);
int x = 10;
int y = 10;
int width = getWidth() - 20;
int height = getHeight() - 20;
g.fillArc(x, y, width, height, 0, 360);
}
}
}
The thing the I don't get is how come he is adding the opaque layer on top of transparent layer? shouldn't be the other way around?
The way that I picture how it should work is by adding the transparent layer on top of the opaque one, kinda of like how you put a screen protector over a phone(sorry for the dumb example)
Can someone please explain how transparency works in java?
I apologize of my question is a bit naive but this has been bothering me for a while!
Yes, the example reliest on the fact that with a null layout, the child components are indeed drawn in reverse order. An implementation dependency. That at least deserves mention. Adding a visible border would make it more evident:
private static class CirclePanel extends JPanel {
CirclePanel() {
setBorder(BorderFactory.createLineBorder(Color.RED));
}

Set size of JPanel, when there are no components in it

I have a custom JPanel. The only thing that is in it, is a drawing of a rectangle, using the drawRect method of the Graphics object. The JPanel is always in a very specific square size, refuses to get any bigger or smaller. Tried overriding the getPreferredSize() method, didn't work.
Tried setting different layout managers for this custom JPanel, and also tried every layout manager for the JPanel that hosts this JPanel. Still, the size of the custom JPanel stays the same.
As I said, the custom JPanel has no components in it, only a drawing of a rectangle.
Any ideas?
Without knowing more about what you're trying to achieve:
As far as your containing panel, you need to know which layout managers respect preferred sizes and which ones don't
Grid Flow Border Box GridBag
Respect PreferredSize NO YES NO YES YES
That being said, if you wrap the painted JPanel in a JPanel with one of the "NOs", the painted JPanel shoud stretch with the resizing of the frame.
Also if you want the drawn rectangle to stretch along with its JPanel, then you need to remember to draw the rectangle with getWidth() and getHeight() of the JPanel and not use hard coded values.
Here is an example using BorderLayout as the containing panel's layout, and making use of getWidth() and getHeight() when performing the painting.
import java.awt.*;
import javax.swing.*;
public class StretchRect {
public StretchRect() {
JPanel panel = new JPanel(new BorderLayout());
panel.add(new RectanglePanel());
JFrame frame = new JFrame();
frame.add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public class RectanglePanel extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.fillRect( (int)(getWidth() * 0.1), (int)(getHeight() * 0.1),
(int)(getWidth() * 0.8), (int)(getHeight() * 0.8) );
}
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 200);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new StretchRect();
}
});
}
}

JFrame pack() and requestFocus() is not working

My main issue is with the following piece of code when setting up a JFrame:
Why the panel doesn't show if I use the pack() and how to make it work?
Why the first requestFocusInWindow() doesn't work and what the principle to use it?
Why the default layout manager of JPanel doesn't work if I delete the setLayout()?
public class SoundGUI extends KeyAdapter{
public static void main(String[] args) {
SoundGUI sGUI = new SoundGUI();
sGUI.setUp();
}
public void setUp () {
JFrame frame = new JFrame ("Key test");
frame.setSize (1000, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible (true);
Panel p = new Panel ();
p.setLayout(new BorderLayout());//why this sentence is necessary FlowLayout doesn't fill the container but rather lets components size to their preferredSizes.
p.addKeyListener (this);
p.requestFocusInWindow();//it's useless here
//requestFocus only works on focusable components that are displayed.
MyDrawPanel dp = new MyDrawPanel();
dp.setBackground(Color.darkGray);
JLabel test = new JLabel("a trial");
JButton t = new JButton("b");
dp.add(t);
dp.add (test);
p.add (dp);
frame.getContentPane().add(p);
p.requestFocusInWindow();
//frame.pack();//why it doesn't work
//frame.setVisible(true);
}
class MyDrawPanel extends JPanel {
public void paintComponent (Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.white);
for (int i = 0; i < 1000; i += 42) {
g2.fill3DRect(i,100 ,20 ,80 ,true);
}
g2.setColor(Color.black);
for (int i = 21; i < 1000; i += 42) {
g2.fill3DRect(i,100 ,20 ,80 ,true);
}
}
}
}
Suggestions:
Call setVisible(true) after calling pack(). Makes sense, doesn't it?
The BorderLayout will tell the MyDrawPanel to fill the p container, since the component is being added in a default way (meaning BorderLayout.CENTER), and that's how BorderLayout works.
FlowLayout doesn't fill the container but rather lets components size to their preferredSizes.
Don't mix Swing with AWT components. i.e., don't use Panels, but rather use JPanels.
requestFocus only works on focusable components that are displayed.
Better to use Key Bindings than KeyListeners.
Better to avoid setting the sizes of anything if possible.
Based on your new code, your problem is due to you're calling setSize(). Most layout managers don't respect this but rather the preferred size. If your drawing JPanel needs to be so big, then make it so. For example try:
class MyDrawPanel extends JPanel {
private static final int PREF_W = 1000;
private static final int PREF_H = 300;
public void paintComponent(Graphics g) {
super.paintComponent(g); //!! ******** don't forget this!!! *********
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.white);
for (int i = 0; i < 1000; i += 42) {
g2.fill3DRect(i, 100, 20, 80, true);
}
g2.setColor(Color.black);
for (int i = 21; i < 1000; i += 42) {
g2.fill3DRect(i, 100, 20, 80, true);
}
}
// the getPReferredSize will make this JPanel preferentially be this size
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
}
Also note that request focus does work if the component is focusable:
JPanel p = new JPanel(); //!! This should be a JPanel, not a Panel
p.setFocusable(true); //!! This is needed
p.setLayout(new BorderLayout());
p.addKeyListener(this);
p.requestFocusInWindow();
But also note that KeyListeners should be avoided. Use Key Bindings instead.

Categories