JPanel flipping out, FlowLayout not working as intended - java

The other day this code was working. I changed some things and re-ran it and now it doesn't work as intended. Obviously something I changed altered the behaviour, but I have gone back and reverted all of those changes and it still doesn't work. Disregarding that bit of information (to start), why does this code not place a 15x15 grid of JLabels inside of a JPanel?
gameBoard.setLayout(new FlowLayout());
for (int i = 0; i < 15; i++)
{
for (int j = 0; j < 15; j++)
{
JLabel tile = new JLabel("");
tile.setHorizontalAlignment(SwingConstants.CENTER);
tile.setPreferredSize(new Dimension(27, 27));
tile.setBorder(new EtchedBorder());
tile.setEnabled(false);
gameBoard.add(tile);
}
}
gameBoard is a JPanel defined through NetBeans' GUI Builder. It has a preferred size, a maximum size (same as preferred). The horizontal/vertical resizable options are disabled, yet when this code runs every button extends horizontally in a row without ever breaking.
If I understand correctly, FlowLayout is supposed to wrap elements when they reach the end of their containing element. This was happening before today, I don't really know why this behaviour stopped?

It would be much better to use a GridLayout.
GridLayout gridLayout = new GridLayout(15, 15);
The flow layout may have changed due to using a new look and feel, different fonts or some Java VM upgrade. Defining a flow layout for this use is not recommended as you're reliant on underlying sizes that can vary across virtual machines or look and feels.

In the interests of teaching a man how to fish:
Revision control systems are great. Even your system is making a back up of your source at frequent intervals.
A useful debugging technique is to take away things until it does work. Does setting a border cause you code to break. Going the other way, can you write a trivial program that demonstrates the problem.

I've found great success and control using TableLayout and I would recommend it to anyone. You can define the columns and rows using specific number of pixels, or you can just use TableLayout.PREFERRED for each column and/or row. Very nice and flexible Layout. We use it everywhere.

I figured it out. Apparently I had to set the preferredSize within the code itself. Just setting it in the Netbeans properties window is not enough. Strange.

Related

Java Swing UI component layout

I am working on java swing and I am stuck with a UI layout
My current output is as below.
I want to modify it a lil and add 2 text inputs in between as shown in sample below. Please help on how to achieve the text inputs side by side.
First of all: You should really give your components more meaningful names than jComboBox2.
Your example picture is not that easy to produce with GridBagLayout. You have to understand that the layout will create a n*m grid and you can put your components (like textfields, labels, comboboxes, etc.) freely anywhere inside that grid.
For example your jLabel4 is at the position 0/3 in the grid and though i'm not actually sure what a gridwidth of -1 does i'm pretty sure it's still at 0/3. If gridwidth was for example 3, your jLabel4 would span from 0/3 to 2/3.
So if you want to put something between those two rows, you'll need to put it at the right grid coordinates and give it the right width and height.
BUT: Sadly, getting it exactly as in your picture requires you to use some tricks (for example increase the grid width of the upper and lower components or add another panel containing the new row components instead of the components themselves).
Try to somehow make it work (even if it doesn't look exactly like your picture) without those tricks first as that might help you understand how GridBagLayout actually works. As soon as you really understand that, it should not be that difficult to recreate your picture.

Java Swing: Resizing component in JFrame without affecting other components

I have a very simple game I'm creating as a novice project. It's based on an online card game called "Castlewars". There are two players, each with a tower which is affected by cards they and their opponent play. At the moment I have the basic framework of collections and classes I need to make the game operate at a very simple level, but I'm having problems displaying these effects to the user. I have the following code, which should update, amongst other things, two jLabels on a jFrame GUI (constructed in NetBeans 7.4) which represent the player's towers:
private void adjustScreen(){
System.out.println (Integer.toString(jLabel1.getSize().height));
jLabel1.setSize(100, (playerRed.getTower().currentHeight() * 2));
System.out.println(Integer.toString(playerRed.getTower().currentHeight() * 2));
System.out.println (Integer.toString(jLabel1.getSize().height));
jLabel2.setSize(100, (playerBlue.getTower().currentHeight() * 2));
jLabel5.setText(Integer.toString(playerBlue.getTower().currentHeight()));
jLabel6.setText(Integer.toString(playerBlue.getGold()));
jLabel9.setText(Integer.toString(playerRed.getTower().currentHeight()));
jLabel10.setText(Integer.toString(playerRed.getGold()));
if (TurnBlue){
jPanel21.setBackground(inPlay);
jPanel10.setBackground(outPlay);
}else{
jPanel10.setBackground(inPlay);
jPanel21.setBackground(outPlay);
}
}
When I run it, i get the following output:
(Initial label height) - 200
(adjusted Tower().height) - 100
(adjusted label height) - 100
Which suggests that what I'm trying to do is working on some level, but the actual labels visually stay the same size.
At start (the initiation of the game should have set the labels to half their visible size):
After a couple of plays - the Blue's new tower height is shown in the top left
I've played around with enabling and disabling the resize property, both on the label and on the frame, and I did once manage to get it to resize, but it then shifted the other components of the frame in an unfortunate way. As you can see, the "Cards" at the bottom do seem to resize themselves, although I am not explicitly instructing them to do this (maybe an effect of the text length in the "Cards"?) What am I doing wrong?
After re-reading the question I realized the answer is not really an answer to the question but more of a list of suggestions. Rather than deleting it, I'll leave it up in case someone can gain something from it :-)
"What am I doing wrong?"
Welcome to the world of "Why I should use Layout Managers"
There's an ocean of problems that may arise from null layouts and trying to set size and location to everything. Swing was made to be used with Layout Managers.
Some layout managers will respect preferred sizes and some wont.
Use the correct layout manager and make use of nested JPanel with different layout managers to get your desired result
Make use of EmptyBorder and vgap and hgap for empty space.
Don't set size or location to anything. Let the layout managers take care of that for you.
Go over Laying out Components withing a Container to learn the different layout managers.
EDIT
If you're using Netbeans GUI BUilder take a look at this answer for some help with how to use different layout managers using the design tool.

Is it Better to Paint or Use a JLabel

I am working on a Swing application that will have some images in the background. I was wondering which of the following ways are more efficient (or better), or if you had another to suggest:
getGraphics().drawImage(t.getImage().getImage(), i * 16, j * 16, this);
or
JLabel tile = new JLabel(t.getImage());
tile.setBounds(i * 16, j * 16, t.getImage().getIconWidth(), t.getImage().getIconHeight());
add(tile);
Edit:
This is where the stuff will happen:
for (int j = 0; j < 3; j++) {
for (int i = 0; i < 3; i++) {
Tile t = map.getTiles()[j][i];
if (t != null) {
// Draw it somehow.
}
}
}
Regarding:
getGraphics().drawImage(t.getImage().getImage(), i * 16, j * 16, this);
You should never draw with a Graphics object obtained by calling getGraphics() on a component as the Graphics object thus obtained will be short-lived, and your drawing can become unstable. To see what I mean, try using your technique, and then after your GUI has been created, minimize and restore it, and you'll likely see a portion or all of your image has disappeared. Instead you should either draw inside of the JPanel (or other JComponent-derived class)'s paintComponent(...) method using the Graphics object given you.
Also don't read in the image every time you want to draw it as doing this causes an unnecessary slowing of your program. Instead, if the images aren't huge, read them in once at program start up, put them in a BufferedImage or ImageIcon variable (depending on the need), and use them when and where needed in the application.
Regarding:
JLabel tile = new JLabel(t.getImage());
tile.setBounds(i * 16, j * 16, t.getImage().getIconWidth(), t.getImage().getIconHeight());
add(tile);
This is fine except for the setBounds(...) part which suggests that the program is not using layout managers appropriately. Let the JLabel and its ImageIcon set its own preferred size and let the layout managers use this when setting out components and sizing the GUI.
Note that you can give JLabel's layout managers and add components to them just as if they were a JPanel. The main difference is that JLabels are not opaque by default.
Regarding which is better, using a JLabel to hold the image or drawing in the paintComponent(...): often it depends on if the image must resize to fit the component. If so, draw in the JPanel's paintComponent(...). Otherwise use the JLabel.
Edit
Per your comments:
Regarding part two, I am not using a layout manager.
Then your GUI's are at risk of being ugly on different OS's, and are at risk of not being very extensible. The layout managers are one of the most powerful aspects of Swing programming, and if you use them your GUI's will be much easier to code, to update, to enhance, and to work well on other systems.
Regarding part two, what Graphics object am I passing to paintComponent()?
You don't pass any Graphics object into a JPanel's paintComponent(Graphics g). This is a method that is called by the JVM's repaint manager at either your suggestion (by calling repaint()) or the suggestion of the operating system.
Finally, are you saying that it doesn't really matter which of the two options I use?
No, I didn't say that. Please re-read my recommendation just above this edit.
Edit 2
Key Links:
Lesson: Performing Custom Painting: for the nuts and bolts
Painting in AWT and Swing: for the gory details.
Edit 3
For a tile map, I'd use ImageIcons for my tile images, and then would have them displayed within a grid of JLabels. This way, swapping tiles would be as trivial as calling setIcon(nextIcon) on the JLabel of interest.
For example, please see TrashGod and my answers to this question: jlabel images array.
Regarding:
As far as the layout manager, I have a good reason. I've made many GUIs with layout managers, but this one is special ;).
Do so at your own risk, and I'm willing to wager a beer that your reasoning for this is incorrect.
Edit 4
The reason why I am not using a layout manager is because I am trying to make a small RPG game in Swing.
Then the tiles would be best held in a container that uses a GridLayout, and that container could possibly be held in a JScrollPane so that you could scroll in whichever direction. You could then either have your sprites sit in the JLabels (by giving them an appropriate layout), or on top by using a JLayeredPane.
I don't want any comments telling me I can't or I shouldn't because if it is impossible, I'll find out on my own.
We're not here to tell you that you can't do anything, of course, but otherwise you can't stipulate what we should or shouldn't tell you. You've come here asking for our advice, and we have an obligation to tell you what we think would work best. You have a right to follow our advice or not since it's your program, but again, you can't stipulate what we can or can't tell you, other than that you should not tolerate or allow rude or offensive statements (flag the moderators if you see this, and they'll take care of it).
Using JLabel will occupy space in container.
While drawing on container will not occupy container's space.

Why will BoxLayout not allow me to change the width of a JButton but let me change the height?

I'm trying to get the Layout of a JDialog of mine to fit a particular look that a program in which I'm porting to Java has, I've used several LayoutManagers before with great success yet for some reason I cannot seem to get this working at all. My goal is to have the Right (East) side of the JDialog contain a "Find Next" and "Cancel" button in a top-down order and then any extra space below so that the two buttons are always at the top of the JDialog, yet for some reason BoxLayout is continously ignoring any attempts at changing (this is where I'm lost) the width of a JButton. Code follows.
JButton findNext = new JButton("Find Next");
JButton cancel = new JButton("Cancel");
cancel.setPreferredSize(new Dimension((int)findNext.getPreferredSize().getWidth(),
(int)cancel.getPreferredSize().getHeight()));
JPanel example = new JPanel();
example.setLayout(new BoxLayout(example, BoxLayout.Y_AXIS));
example.add(findNext);
example.add(cancel);
example.add(Box.createGlue());
No matter what I try, cancel always retains it's normal size. I've tried setMinimumSize() and setMaximumSize() with the same parameters as setPreferredSize with no luck. I've even tried cancel.setPreferredSize(new Dimension(500, 500)); and the buttons height was the only thing adjusted, it STILL retained the default width it was given.
To clear up any questions, here is what it looks like (now that I've finished it) and you'll see that the "Find Next" and "Cancel" buttons are not the same size.
I know this is an old question but I don't really see a good explanation. So for the sake of searchers that stumble upon this I will add my two cents.
There are three methods associated with sizing components in Swing: setPreferredSize(), setMinimumSize(), and setMaximumSize(). However, the important point is that it is up to the particular layout manager being used as to whether or not it honors any of these methods.
For BoxLayout (the layout the original poster is using):
setMinimumSize() -- BoxLayout honors this
setMaximumSize() -- BoxLayout honors this
setPreferredSize() -- if X_AXIS is being used width is honored, if Y_AXIS is being used height is honored
The OP is using a Y_AXIS BoxLayout which is why only his height was being changed.
Update: I put together a page with this same information for all of the layout managers. Hopefully it can help some searchers out: http://thebadprogrammer.com/swing-layout-manager-sizing/
You may not want Box.createGlue(), which "grows as necessary to absorb any extra space in its container." Instead, use Box.createVerticalStrut() between the buttons, as shown below and in the ControlPanel of this simulation.
example.setLayout(new BoxLayout(example, BoxLayout.Y_AXIS));
example.add(findNext);
Box.createVerticalStrut(10);
example.add(cancel);
Addendum:
adding in setMaximumSize() made it work.
This is the expected behavior for components having identical maximum widths in a vertical BoxLayout, as described in Box Layout Features. The preferred width of the container becomes that of the (equally wide) children, and the X alignment becomes irrelevant.
example.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
JButton findNext = new JButton("Find Next");
JButton cancel = new JButton("Cancel");
Dimension d = findNext.getMaximumSize();
cancel.setMaximumSize(new Dimension(d));
example.add(findNext);
example.add(cancel);
As mentioned in the comments on the question, you were able to fix it by switching to setMaximumSize(). However, as you noted, setPreferredSize() doesn't work. So, what's up with that?
With many things Swing, the properties used to determine the actual component size when using the BoxLayout are somewhat random (in my opinion). When determining how to render the components, Swing calls layoutComponent() on the layout manager, which is figures out where to position everything.
BoxLayout's implementation of layoutComponent() involves a call to a method that creates SizeRequirements objects for the width and height of each of the components you add to the JPanel, based on their getMinimum/Preferred/MaximumSize() methods.
Later, it calls SizeRequirements.calculateAlignedPositions() for determining the correct width values for each component, because your orientation is BoxLayout.Y_AXIS (The heights are calculated using a different method). Taking snippets from the source, the relevant implementation of this method is as follows:
for (int i = 0; i < children.length; i++) {
SizeRequirements req = children[i];
//...
int maxAscent = (int)(req.maximum * alignment);
int maxDescent = req.maximum - maxAscent;
//...
int descent = Math.min(totalDescent, maxDescent);
//...
spans[i] = (int)Math.min((long) ascent + (long)descent, Integer.MAX_VALUE);
}
Note that totalDescent is the available width, so descent is always set to maxDescent, which is based on SizeRequirements.maximum, which was taken from JButton.getMaximumSize(). The value of spans[i] is then used later in a call to JButton.setBounds() as the width. As you'll note, getPreferredSize() was never involved here, which is why setting it has no impact in this case.
Usually if want to ensure a size of the component in Swing you need to call setMinimumSize(), setMaximumSize(), and SetPrefferedSize() with the same value.
button.setMaximumSize(getMaximumSize());
If you put your buttons in a GridLayout panel they will be the same width.

Java - Layout Manager Selection

Is there a simply layout manager I can use in a JPanel to create something akin to a bar chart? FlowLayout almost meets this need. The added component orientation needs to be left to right (default for FlowLayout), but they need to "rest" on the bottom of the panel with excess space at the top (not available in FlowLayout). Also, the components will all the be the same height and width.
Thanks.
A BoxLayout will do the trick as demonstrated in this posting
If you are going to do something like a bar chart, you might want to consider not using Components at all. Just have a single JComponent that overrides (IIRC) paintComponent. It'll be easier to do the calculations in a manner appropriate to a bar chart rather than trying to use an inappropriate layout manager abstraction.
FWIW, I default to GridBagLayout, even if a simpler layout manager will do, on this basis that the code can be more consistent.
You can do exactly what you want in GridBagLayout. Yes, I know everyone hates GBL; yes, I know I'll get down-voted. But it really is not difficult to understand and you can use it for almost any layout goal.
The trick to get a component to "stick" to the bottom is to use the anchor and fill properties of the GridBagConstraints object properly (i.e. SOUTH and NONE)
A BoxLayout might work for you. It lets you layout components left-to-right or top-to-bottom, with the tightly coupled Box class to force spacing constraints.
I actually prefer the FormLayout, since it is very flexible but you have to write a lot of code though. And in the beginning its a little bit confusing with its percentage and pixel parameters.
But you can for example tell a control that it is 5 pixels left of another control (thats the main part...it layouts controls in relation to neighbors), then it takes 100% of the lasting space availabel including a border space of 5 pixels (you need to use -5 then).
I think it looks somewhat similar to this
FormData data = new FormData();
data.left = new FormAttachement(neighborControl, 5);
data.right = new FormAttachement(100, -5);
...
button.setLayoutData(data);
This example is for JFace, but there are Swing implementations as well.
I will look up my old code later this day to check if the code I wrote is right :)
HereĀ“s a additional link

Categories

Resources