GridLayout Panel adding not considering preferreed size - java

I'm currently coding a java project and I need a JPanel named board to contain some Tiles that are themselves JPanel, I add them to a layout of appropriate size, but when I add everything with buttons in a grouplayout in a JFrame, only the first Tile is at an appropriate size, and others have ridiculous size like being 1p width and 50p height or 1*1p.
I don't feel like I miss anything, everything is added in the JFrame and added to correct layout, with correct number of rows and lines, and sizes are set to be 50*50p in the Tile class. Here are some code snippets containing the graphic settings, especially the constructors:
Tile.java :
class Case extends JPanel
{
private int _color;
private boolean _star;
private Case _casePere;
private ArrayList<Case> _fils;
private int _x,_y;
public Case(int x, int y)
{
_color = 0;
_star = false;
_casePere = null;
_fils = new ArrayList<Case>();
_x = x;
_y = y;
setPreferredSize(new Dimension(50,50));
setMinimumSize(new Dimension(50,50));
setBackground(Color.WHITE);
}
Board.java :
class Board extends JPanel{
private Case[][] _grid;
private Case[] _starsp1;
private Case[] _starsp2;
// constructeur
public Board(int nbStars, int length){
_grid = new Case[length][length];
_starsp1 = new Case[nbStars];
_starsp2 = new Case[nbStars];
//graphisme
Dimension d = new Dimension(50*length, length*50);
setBackground(Color.BLACK);
setPreferredSize(d);
GridLayout layout = new GridLayout(Constante.length, Constante.length,2 ,2);
setLayout(layout);
for(int y=0; y<length; ++y)
{
for(int x=0; x<length; ++x)
{
_grid[x][y] = new Case(x,y);
add(_grid[x][y]);
}
}
if you want, I can add some snippets of the Window class, but beside the Board, other components don't hav any issues, and it's mainly adding and grouping some components. Here's a screenshot of the output so you can see how the Board's drawing behaves
Edit : I was overriding getX and getY in my Case class overriding JPanel's one, kinda dumb issue again, thanks for the answers

setPreferredSize(d);
Don't set the preferred size of the board. The layout manager of the board will determine the preferred size based on the number of components added to the grid and the size of the largest component added.
Note, (based on the line below) you want a spacing of 2 pixels between each component which your calculation doesn't include. So let the layout manager do its job.
GridLayout layout = new GridLayout(Constante.length, Constante.length,2 ,2);
We don't know what Constante.length is. You pass in a length variable to your class so use that variable.
Also, why does your Case class have so many instance variables? Those variables are never used in the posted code. So maybe you have other methods that are causing problems with the layout. For example don't override getX() or getY() those methods are used by the layout manager.

Related

Java swing GUI absolute positioning

I know that absolute positioning is not recommended, but I need to show my labels randomly scattered as well as randomly changing their positions.
I have researched how to use setBounds but it doesn't seem to work. The following code shows the labels in a Flow Layout, and when I use setLayout(null) it shows a blank frame.
public class GUI extends JFrame{
device mobiles[];
device station;
JPanel pane= new JPanel();
public GUI()
{
setTitle("communication is Key");
setSize(1000, 1000);
setResizable(false);
setDefaultCloseOperation(EXIT_ON_CLOSE);
pane.setBackground(Color.WHITE);
int x=0; int y=0;
mobiles= new device[10];
for (int i = 0; i < 10; i++) {
x=randInt();
y=randInt();
mobiles[i]= new device(1,x,y);
pane.add(mobiles[i]);
}
x=randInt();
y=randInt();
station = new device(0,x,y);
pane.add(station);
this.add(pane);
}
and this is class "devices" that extends JLabel
public class device extends JLabel{
ImageIcon mob = new ImageIcon("mob.png");
ImageIcon tow = new ImageIcon("tower.png");
public device(int num, int x, int y)
{ if(num==1)
this.setIcon(mob);
else this.setIcon(tow);
this.setBounds(x, y, 3, 7);
}
}
any help in finding out what the problem is, would be be appreciated.
The following code shows the labels in a Flow Layout, and when I use setLayout(null) it shows a blank frame.
The layout manager sets the size and location of the component.
If you don't use the layout manager, then you are responsible for set the size and location of each component.
Typically I would set the size to equal to the components preferred size.
Also, did you display the x/y value that are randomly generated? Maybe the values are larger than the size of the panel.
and when I use setLayout(null) it shows a blank frame.
What layout is set to null? The panel of the frame. Your code doesn't use the above method. Post the code that you use to cause the problem. We don't want to guess what you may or may not be doing.
thanks to #CasparNoree ... the answer suggested was to initialize the Japnel from the start:
JPanel pane = new JPanel(null);
When you set the layout to null you can set the bounds manually with coordinates.
JFrame jf = new JFrame();
JPanel p = new JPanel();
JButton jb = new JButton();
// this is where you make it so setting the bounds actually does something
p.setLayout(null);
jb.setBounds(100,100,100,100);
p.add(jb);
jf.add(p);
jf.setVisible(true);

Positioning GUI Components with Absolute Layout

I know there are many questions on here already about positioning components using absolute layout, but they don't seem to answer my questions.
I'm working on creating a Solitaire game in java to better learn GUI components. I seem to understand the various ActionListener classes I'm using, but I'm having trouble positioning components where I want them in the window.
I'm trying to set up the window in a format that resembles the basic solitaire layout (deck, discard pile, 4 suit stacks at the top and 7 solitaire stacks below them). My thought process was that I'd need to use an absolute layout in my JFrame component to manually place the different stack elements where they should go (maybe this isn't the best approach?). In doing so, I've tried using setLocation(x, y), setLocation(Point), setBounds(x, y, width, height), etc and nothing seems to work. I just get a blank window.
Here's an example of my code trying to manually place components in the window:
public class SolitaireTable extends JFrame {
public static final int SUIT_STACK_CNT = 4;
public static final int SOL_STACK_CNT = 7;
public static final Point DECK_POS = new Point(5,5);
public static final Point DISCARD_POS = new Point(73+10, 5); //73 is width of card images and 10 gives it a 5 px border to left and right
public static final Point SUIT_STACK_POS = new Point(DISCARD_POS.x + 73 + 92, 5);
public static final Point SOL_STACK_POS = new Point(DECK_POS.x, DECK_POS.y + 97 + 5); //97 is heigh of card image. 5 gives it a border of 5
public SolitaireTable()
{
setLayout(null);
ImageIcon cardImg = new ImageIcon("images/2c.gif");
Card card1 = new Card(cardImg);
add(card1);
card1.setBounds(50, 50, card1.getWidth(), card1.getHeight());
}
public static void main(String[] args)
{
SolitaireTable table = new SolitaireTable();
table.setTitle("Solitaire");
table.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
table.setVisible(true);
table.setSize(800, 600);
}
}
public class Card extends JComponent {
private ImageIcon img;//current image displayed (either front or back of card)
private Point coords;
private String suit;
private int face; //use ints instead of strings to make descending face pattern calculations easier. 0 = ace, 1= 2, 10 = j, 12 = k, etc
private String color;
private boolean revealed; //whether see face or back of card
private ImageIcon frontImage; //image of card's face side (set in constructor)
public Card(ImageIcon img){
this.img = img;
}
public void moveTo(Point p) {
coords = p;
}
//================================================================= getWidth
public int getWidth() {
return img.getIconWidth();
}
//================================================================ getHeight
public int getHeight() {
return img.getIconHeight();
}
}
I've been scouring the internet for days trying to figure out how to position components in an absolute layout but haven't found much. Any help would be greatly appreciated. Thanks.
You create your Card class with an ImageIcon, but you never paint the Icon anywhere so there is nothing to paint and you get an empty screen.
You need to add painting code to your class. See the section from the Swing tutorial on Custom Painting for more information and working examples.
Or maybe you create a JLabel and add the Icon to the label and then add the label to the component. (Don't forget to set the layout manager of your card component if you use this approach).
Or you could just create a JLabel with the Icon. Then you would extend JLabel, instead of JComponent to add your custom methods.
Are you using Absolute layout ?
Absolute Layout is DEPRECATED ..... Have a look
here
From docs: Absolute layouts are less flexible and harder to maintain than other types of layouts without absolute positioning.
However, if it fits your special purpose, just use it.
You can use RelativeLayouts as described here:
Set the absolute position of a view

Android's LinearLayout equivalent in Swing

I have seen this thread which asked the exact same question I have now, but find the answers a bit unsatisfactory:
Android's LinearLayout for Swing
I created a class WeightedPanel like so:
public class WeightedPanel extends javax.swing.JPanel {
private static final long serialVersionUID = 6844740568601141924L;
private boolean mVertical;
private double mLastWeight = 1;
private GridBagConstraints mConstraints;
private int mLastGrid = 0;
public WeightedPanel(boolean vertical) {
mVertical = vertical;
mConstraints = new GridBagConstraints();
setLayout(new GridBagLayout());
}
#Override
public Component add(Component comp) {
return add(comp, mLastWeight);
}
public Component add(Component comp, double weight) {
if (mVertical) {
mConstraints.weighty = weight;
mConstraints.weightx = 1;
mConstraints.gridy = mLastGrid;
mConstraints.gridx = 0;
} else {
mConstraints.weightx = weight;
mConstraints.weighty = 1;
mConstraints.gridx = mLastGrid;
mConstraints.gridy = 0;
}
mConstraints.fill = GridBagConstraints.BOTH;
add(comp, mConstraints);
mLastWeight = weight;
mLastGrid += weight;
return comp;
}
public Component add(Component comp, int weight) {
return add(comp, (double) weight);
}
}
This kind of works, but I have two problems with it:
1) In my application, I have a login screen:
#Override
protected void addComponents(WeightedPanel jPanel) {
mUpdateListener = new UpdateListener() {
#Override
public void onUpdate() {
LoginFrame.this.onUpdate();
}
};
WeightedPanel panel = getUserPanel();
jPanel.add(panel);
panel = getPasswordPanel();
jPanel.add(panel);
mLoginButton = getLoginButton();
jPanel.add(mLoginButton);
}
private WeightedPanel getPasswordPanel() {
WeightedPanel result = new WeightedPanel(false);
JLabel label = new JLabel("Password");
result.add(label);
mPasswordField = new PasswordField(mUpdateListener);
result.add(mPasswordField);
return result;
}
private WeightedPanel getUserPanel() {
WeightedPanel result = new WeightedPanel(false);
JLabel label = new JLabel("User");
result.add(label);
mUserTextField = new TextField(mUpdateListener);
result.add(mUserTextField);
return result;
}
which in practice looks like this:
Click to view
Why aren't the labels and text fields all the same size here? I figure it's got something to do with the fact that "Password" is a longer string than "User", but that's obviously not what I want!
2) My second problem is this. I have another screen like so:
#Override
protected void addComponents(WeightedPanel jPanel) {
WeightedPanel scrollPanePanel = getOrdersScrollPane();
jPanel.add(scrollPanePanel);
WeightedPanel buttonPanel = getButtonPanel();
jPanel.add(buttonPanel);
}
private WeightedPanel getOrdersScrollPane() {
WeightedPanel result = new WeightedPanel(true);
JPanel filterPanel = getFilterPanel();
result.add(filterPanel, 1);
mTableModel = new OrdersTableModel();
mTable = new JTable(mTableModel);
mTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
mTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
#Override
public void valueChanged(ListSelectionEvent arg0) {
checkEnabled();
}
});
JScrollPane scrollPane = new JScrollPane(mTable);
result.add(scrollPane, 40);
return result;
}
It really doesn't look bad in practice:
Click to view
But have a look at the getOrdersScrollPane() function. The call to functions result.add(filterPanel, 1); and result.add(scrollPane, 50); say that the proportion between the filter panel and the scroll pane should be 1:50, but looking at the scroll pane, it's definitely not 50 times the size of the filter panel. Obviously, I am exaggerating to make my point, I don't really want a proportion of 1:50; it just strikes me that it makes no difference whether I do result.add(scrollPane, 10); or result.add(scrollPane, 50);
Both questions stem from an incorrect understanding of GridBagLayout. A bit more reading and experimenting should help) To answer the question at hand:
1) The problem here is that you want a single GridBagLayout, but instead are adding 2 independent panels.
The result: The columns in the top grid bag are independent of the columns in the bottom grid bag.
To rectify this, there are 2 things you can try:
Add both labels and both text fields to a single GridBag panel. That way the columns will align.
Make a minimum and preferred size for the labels so that their width matches and set their weightx to 0 (and weightx of text fields non-zero). That way you are making the GridBags allocate the same amount of space for the labels and text fields.
The first method is preferred, but not always possible. The second method is hacky and will likely break as soon as you change the label string, a user set a different default font etc, etc.
2) Here you are misunderstanding what weighty does.
It does not make your components of the specified proportion. That should be clear enough since you can mix 0 and non-0 weight components in a single layout.
What it does, is it allocates the preferred (or minimum) sizes for components, and distributes the remaining space in that proportion. Which means if you make your panel 100 pixels higher by resizing the window, 2 will go to the top panel adding spacing, and 98 will go to the table.
What you likely wanted is to make the weighty of the top filter 0 (so that there is no awkward spacings in large windows) and control its actual height with setPreferred and setMinimum size (or by setting those on the embedded components).
EDIT
As docs for Linear Layout state, to achieve a fixed proportion of sizes of components (the initial problem), one has to set their preferred sizes to 0, and then set weights (then all space is remaining space, and is distributed according to weights only). This also works for the GridBag variant.

Keeping the rectangle within JFrame

I know I am being an idiot and that's why I can't figure it out but I am trying to paint a bunch of rectangles with randoms size and position using paintComponent. I am trying to make sure that all of them are painted within the frame. I am able to do it with the following code (snippet) but I am wondering if there is a better way to do it than me hardcoding numbers into the program. Is there a method that I should take a look at that might be what I'm looking for?
Here's the inner class that overrides the paintComponent() method:
class DrawPanel extends JPanel {
public void paintComponent(Graphics g) {
int red = (int)(Math.random()*256);
int blue = (int)(Math.random()*256);
int green = (int)(Math.random()*256);
g.setColor(new Color(red, blue, green));
//The following 4 lines keep the rects within the frame
//The frame is 500,500
int ht = (int)(Math.random()*400);
int wd = (int)(Math.random()*400);
int x = (int)(Math.random()*100);
int y = (int)(Math.random()*100);
g.fillRect(x,y,ht,wd);
}
}
You should base your co-ordinates & sizes on the DrawPanel component size. Also using Random.nextInt instead of Math.random() will make it easier to keep within range based on the current size of the panel:
Random random = new Random();
int ht = random.nextInt(getHeight());
int wd = random.nextInt(getWidth());
int x = random.nextInt(getWidth() - wd);
int y = random.nextInt(getHeight() - ht);
I am wondering if there is a better way to do it than me hardcoding numbers into the program.
Yes there is
call getSize() on the enclosing JPanel, the DrawPanel, so you can see the actual boundaries of the component that is being drawn upon. (edit: or getWidth() and getHeight() as recommended by Lucas -- 1+ to his answer!).
Also, you will usually want to call the super's paintComponent(...) method within the child's override.
Also you will usually want to do your randomization elsewhere, such as in the DrawPanel's constructor so as not to change your rectangles each time you re-size the GUI.
There is a method to get the length and width of the panel you are working in.
getHeight();
getWidth();
These will return the current size of the JPanel you are working in, meaning if you resize the window it'll actually still draw them inside.
http://docs.oracle.com/javase/7/docs/api/javax/swing/JComponent.html#getWidth()

Java GridLayout is laying out all my JPanels in the top left corner

SOLVED:
Just found out what the problem was, after trying to make an SSCCE.
It had to do with my cell class, I didn't realise I was overriding getX() and getY() from the JComponent class.
After renaming these accessors it all works as expected
========================================
I have a JPanel with a GridLayout set at 3 rows x 3 cols.
I'm trying to add JPanels to each cell in the gridlayout to fill up all 9 cells.
Each one of these JPanels has an overriden paintChildren method which will paint some kind of rectangle starting at the top left of the JPanel - the end result will be each cell has a rectangle in it starting at the top left of the cell.
After adding all the JPanels to the gridlayout, they all appear in the top left corner overlapping each other (I have confirmed they are overlapping), instead of being laid out in a 3x3 grid.
How can I get them arranged in the 3x3 grid?
(Simplified) Code:
public class Panel extends JPanel {
public Panel(int x, int y) {
layout = new GridLayout(x, y, 2, 2);
setLayout(layout);
populateGrid();
}
public void populateGrid() {
removeAll();
for (int i = 0; i < 9; i++)
add(new Cell(50,50));
}
}
public class Cell extends JPanel {
public Cell(int x, int y) {
// x/y values used to define rectangle
setBorder(BorderFactory.createLineBorder(new Color(0,0,0)));
setBackground(Color.WHITE);
}
public void paintChildren(Graphics g) {
g.setColor(Color.BLUE);
g.fillRect(0, 0, x, y);
}
}
Make sure you import the right Panel. (And not the java.awt.Panel class.) Or, better, rename your class to GridPanel or something similar to avoid confusion / clashes.
You probably don't want to override paintChildren. Overriding paintComponent sounds like a better option in this case.
You may want to set minimum / preferred / maximum size for the Cell class.
Hard to make more observations without the actual code that instantiates and uses your Panel class.

Categories