Difference between a JPanel and a LayerManager - java

Ok I gather that every JComponent can set its location...bet it JPanel, JButton, JLabel..whatever. It can set its location use .setLocation(x,y).
I have come to suspect that actually when I do
JButton btn = new JButton("Click me!") ;
btn.setLocation(10,200);
I am actually changing the location of a button in a panel, and not in the frame. And if I do
JPanel jPanel = new JPanel();
jPanel.setLocation(10,100);
I am changing the location of jPanel not in the JFrame but in the default JPanel provided by default. So JComponents can change their locations, why not just dump everything directly into a bloody jFrame object? :S
I haven't tried but I believe I can arrange stuff just by using the setLocation(x,y) method..and I guess that'd be a big pain the butt.
This leads to my question..if we can set components location by using the method, what is the LayoutManager for?
Can you please provide example to show the difference?

Can you please provide example to show the difference?
Here is an example of using layouts, as well as a challenge.
The challenge is to make a resizable, PLAF changable version of that UI using setLocation()/setBounds().
If you (or anyone) can manage it (in code that is small enough to post to the thread), I'll contribute 500 bounty points to the answer.

Related

JButton size larger than specified

I created a jframe, added a jbutton to it, and set the size of the jframe as 500,500 and size of the jbutton as 40,60. However, when I executed my program, my jbutton was covering the whole of my jframe. I
tried many things and looked into many sources, but I could not find a solution.
Please help me solve this.
You're not using a LayoutManager of any kind. The JButton will try to inherit the size of the parent container, as the default for a JFrame is BorderLayout (specifically, BorderLayout.CENTER). You could try using
button.setPreferredSize(x, y);
However I don't think this would be enough by itself. Call getContentPane on the Jframe, and set a layout on that contentPane container (I use FlowLayout a lot, as it respects setPreferredSize). Put your JButton inside that.
This is always a good starting point:
https://docs.oracle.com/javase/tutorial/uiswing/layout/visual.html
As the methods that you can call on a UI element vary depending on what LayoutManager you're assigning them to (be the child of). Sometimes it's setMinimumSize, sometimes it's setPreferredSize, and sometimes it's a combination.

Java: Upgrading to a single window

So I have been coding Java for a few months now, and I have been using JOptionPane to display text and variables in my games. I want to upgrade to a single window like a normal game, but I want to only focus on simple buttons and text on the screen. I have tried learning JFrame and ActionListsner, but I failed to completley figure it out. JFrame really confused me.
My question is this: Is there an easier way beside JFrame to just have a window that I can have simple text, buttons and TextFields without the hassle of opening a bunch of windows with JOptionPane, making crap loads of ActionListeners with JFrame or having to get into GUI? If not, where can I find help on how to make games with JFrame?
You should be using a JFrame. Trust me, they aren't that hard to use. Using a JFrame, you could create multiple panels and switch between them using CardLayout.
Since you said you aren't sure about how JFrame works, I gave a short introduction to them at the end of this post. But first, lets first talk about how to solve your problem.
Switching Panels via CardLayout
When you want to switch whats being viewed in the window completely, you're gonna want an entirely different panel for that specific purpose (for example, one for Main Menu, and one for the game). CardLayout was created for this.
You specify a "deck" panel. This panel will hold all the other panels you wanna switch between (cards):
CardLayout layout = new CardLayout();
JPanel deck = new JPanel();
deck.setLayout(layout);
You'll need to maintain a reference to the layout (via a variable) so you can switch between panels.
Now that we have a "deck" panel, we need to add some cards. We do this by creating more JPanels, and specifying a name when we add it to the frame (constraints):
JPanel firstCard = new JPanel();
JPanel secondCard = new JPanel();
deck.add(firstCard, "first");
deck.add(secondCard, "second");
The first card added to the deck will always be the first one to show.
Once you have all your cards added, you can switch between them by calling layout.show(deck, "second");. This is how you use CardLayout to manage multiple panels within your container.
Creating listeners
There's no easier way to manage it. It only gets harder from there (bindings - I highly suggest you look into them). For listeners, there are 2 steps:
Create the listener
Add it to the component
Could be 1 if you created the listener "on the fly" using a lambda:
JButton button = new JButton();
//Java 8+
button.addActionListener(event -> {
//whenever you click the button, code in here triggers
});
For those who don't use Java 8, you will need to use an anonymous class:
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
//whenever you click the button, code in here triggers
}
});
Only some components support ActionListener. Anything that extends AbstractButton, like JMenuItem, JButton, JRadioButton, and more will support ActionListeners. JTextField also supports it, even though it's not an AbstractButton. Every component supports KeyListener, though, which can also be used to listen for input events from the user.
If you have any questions about this, let me know and I'll be glad to answer them.
Using Swing Components
JFrame
You initialize a JFrame:
JFrame frame = new JFrame();
You then want to set the defaultCloseOperation, to determine what happens when the window closes:
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
There are different options, but EXIT_ON_CLOSE will terminate your program after closing the window. If you do not set the defaultCloseOperation, then your window will close, but your program will still be running. BIG NO NO. If you don't want the entire program to exit when you close the frame, use DISPOSE_ON_CLOSE.
After you have the close operation, you might be tempted to set the size. This is bad practice. Your frame should size depending on what's inside of it. To do this, you add your components to your frame, then call pack():
JButton button = new JButton("Button");
frame.add(button);
//add other components
frame.pack();
This will ensure your frame sizes to fit what's inside of it. You should always design GUI from the inside out, to ensure you always have enough room for what you need.
Containers
JFrame is a Container. Containers are just components that hold other components. All containers should have a LayoutManager to manage how components are laid out and, if needed, sized. JFrame isn't the only container though. JPanel is actually a container that's meant to be added to another container (or window). You can add things to a JPanel, then add that panel to another container. This is how you keep things neatly organized. Containers are pretty straight forward, so there's not much to talk about.
Content Pane
When you create a JFrame, it comes along with something called the contentPane. It is a JPanel nested within the JFrame. When you do frame.add(button), you'll notice that add actually refers to the contentPane:
//in Container class
public Component add(Component comp) {
addImpl(comp, null, -1); //where it's added
return comp;
}
//In JFrame class (overriding)
protected void addImpl(Component comp, Object constraints, int index) {
if(isRootPaneCheckingEnabled()) {
getContentPane().add(comp, constraints, index); //adds to content pane instead
} else {
super.addImpl(comp, constraints, index); //if root panes were not supported, which they currently are
}
}
You too can grab the contentPane from the frame using
Container pane = frame.getContentPane();
The reason why the contentPane is in Container form is to ensure a strong API (if JPanels were no longer going to be used for this, we wouldn't need to worry about changing the method type of getContentPane). Although, since it IS a JPanel, you can cast it back to it's original form:
JPanel pane = (JPanel) frame.getContentPane();
Although it's usually not needed. Using it as a Container tends to work just fine.
Layouts
JFrame, by default, uses BorderLayout, but you can change this by calling the setLayout method of the container:
FlowLayout layout = new FlowLayout();
frame.setLayout(layout);
Before jumping into layouts, I want to mention that JPanels use FlowLayout as default, except for the frame's default contentPane, which you can also change by doing frame.setContentPane(...). The contentPane uses BorderLayout as default.
Now, lets talk about a couple, starting with the JFrame default: BorderLayout.
Some layouts require what are called constraints, which tell the layout how to handle that specific component that's being added. You specify these constraints when you add the component to the container:
frame.add(button, BorderLayout.SOUTH);
BorderLayout is pretty simple. You have 5 options: NORTH, SOUTH, EAST, WEST, or CENTER. (there are other values such as PAGE_START, which are interchangeable. It's best to just use the easier form)
All constraints for BorderLayout are static field variables that you call similar to how I did. BorderLayout is actually an easy layout to use, seeing how there's not much to it. Even though it's simplicity limits what you can do (you can only put it in a certain position like NORTH), you'd usually combine layouts to get the result you want. This layout can be very powerful when combined with other layouts.
FlowLayout is pretty straight forward, as well as other layouts out there. One of the less straight-forward ones would be GridBagLayout, which can be a really flexible layout. It can also be pretty complex, though, as the documentation even states.
When using GridBagLayout, you need to create a constraints object: GridBagConstraints. To work with GridBagLayout, you set the constraints using the constraints object, then add both the component and the constraints to the container:
frame.setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
JButton button = new JButton("yoyoyo");
frame.add(button, gbc);
Even if we don't adjust the constraints, we MUST still specify it when adding a component to the container.
Lets say we had 4 buttons, and we wanted to put them side by side. You would adjust the constraint's gridx and gridy values:
JButton button = new JButton("1");
JButton button2 = new JButton("2");
JButton button3 = new JButton("3");
JButton button4 = new JButton("4");
frame.add(button, gbc);
gbc.gridx = 1; //or gridx += 1, or gridx = 1. gridx starts at 0
frame.add(button2, gbc);
gbc.gridx = 0; //must reset the value back to normal
gbc.gridy = 1;
frame.add(button3, gbc);
gbc.gridx = 1;
//since gridy already == 1, no need to change it
frame.add(button4, gbc);
We can use the same constraints object, as long as we reset values when needed.
GridBagLayout starts centered, and works from the center out, unless you specify otherwise. You cannot skip grid spaces either. Also, as you'll notice, all your buttons will be touching. If you wanted a little space between each component, you can set the insets of the constraints:
int top = 5, left = 5, bottom = 1, right = 1;
gbc.insets.set(top, left, bottom, right);
There is a LOT more to this layout, and sadly I just don't feel this is the best place to give the explanation to it all, seeing how it's already documented (I even added the link).
There are many other layouts out there. Get familiar with as many as you possibly can, then find the ones that'll help suit your needs. Positioning should ALWAYS rely on the LayoutManager that's being used. As for sizing, that kinda depends on the layout you're using.
I would highly recommend using JavaFX. It's a very nice fairly easy to use GUI system with nice looking and customizable controls. JavaFX events (basically ActionListeners) are pretty straightforward as well.
This should get you started: http://docs.oracle.com/javase/8/javafx/get-started-tutorial/get_start_apps.htm#JFXST804
I believe CardLayout is what you're looking for. With it, the programmer can choose which JPanels should be visible in the JFrame. Upon user interaction you can switch the contents of the JFrame without new JFrames or JOptionPanes popping up.
Is there an easier way than using Swing for simple games? Swing has a learning curve, but with the right resources and practice you can learn to build simple GUI applications pretty quickly.

Adding a button anywhere in a JPanel

Without using the Swing GUI on Eclipse, I've been struggling with adding a button to a JFrame anywhere in the frame (so no BorderLayout.CENTER). I can't get past:
JPanel panel = new JPanel();
JButton OKButton = new JButton("OK");
OKButton.addActionListener(new MyAction());
panel.add(OKButton,BorderLayout.CENTER);
So would something like this be completely redesigned or is there something I'm missing?
EDIT: By Anywhere (as I'm planning to add more than one button/label to a frame), I meant perhaps a coordinate on the frame. So other than dead center, (example) 75% from the left and 25% down.
You can use different Panels with different LayoutMangers to arrange the GUI as you like.
Have a look here for some common LayoutManagers:
http://docs.oracle.com/javase/tutorial/uiswing/layout/using.html
Otherwise you could use the null Layout (panel.setLayout(null)) and place all components by setting the position. But I would recommend you the LayoutMangers

Jbutton is moving components in different panel

I have searched for this problem and haven't found anything close. I will try to be specific and post code however this is a large program so I can't post all the code. The problem in general is this: A JButton on one panel causes components on another panel to shift at first click. This only occurs when there is an action listener added to the button. (clicking the button without an action listener doe noting (obviously)). The problem is that the action listener i add only changes the button background, text, and size (to fit new text).
Here is the action listener as of right now. login is the JButton:
private class LoginListener implements ActionListener{
public void actionPerformed(ActionEvent e){
loggedIn = !loggedIn;
if(loggedIn){
login.setText("Logout");
login.setBackground(Constants.RED);
}
else{
login.setText("Login");
login.setBackground(Constants.UPPER_BOUNDARY);
}
login.setSize(login.getPreferredSize());
}
}
The setup is this. The action listener is a subclass of the loginPanel where the login button is located. That loginPanel is added to the main JFrame at the upper 1/4 of the frame. The lower 3/4 of the main JFrame is mainPanel which has other swing components. The loginPanel and mainPanel do not share components or variables or really know of each other's existence (as far as I have coded). Yet when this actionlistener above is added to the login button components in the mainPanel shift from their positions to other positions. The only happens at first click and then they stay where they are at (not where i want them).
Other factors:
- I use absolute positioning (sorry if you don't like it but I like it better)
- I am using a SynthLookAndFeel but have never had this issue with this look and feel before.
Thanks
Other factors: - I use absolute positioning (sorry if you don't like it but I like it better)
There's nothing to be sorry about, and the solution is simple: Don't use absolute positioning, but instead learn about and use the layout managers to there full abilities. One of the reasons to use them is to avoid pernicious bugs like this one. It's quite possible that your code is in fact using a component's default layout manager even now without you knowing about it. You can find out more about them here. One of the keys to using them well is to nest them by using nested JPanels, each using its own layout manager. Then they can do the heavy layout lifting for you automatically.

Java: Can't get JButton to be horizontally snug around text

I have a JButton that is much wider than the text I put into it. I've researched this, and I keep finding the suggestion that I use Jbutton.setMargin(new Insets(0,0,0,0)); But this just does not seem to work. Also, setMaximumSize has no effect, although if I also set a minimum size, it does change the size of the button. But I don't want to set the size manually. I just want it to be less wide. What am I missing?
Here's my code to create the button:
plusminus = new JButton("+");
plusminus.setMargin(new Insets(0,0,0,0));
And here's what it looks like:
Thanks.
I'm manually making my GUI. In this case, the layout is GroupLayout
Then that may be part of your problem. Your JButton's size is constrained by the layout of the container that holds it. One possible solution if you absolutely need to use GroupLayout (which I hate by the way), is to place your JButton inside of a JPanel that uses FlowLayout or some other layout that allows flexible sized components, and place this JPanel into the container that's currently holding your button. Beware though if your button's bigger than the JPanel.
On a lark i tried negative left & right insets & unbelievably it worked. I did not then need to mess w/ the min/max/pref sizes. Btw my buttons are in one column of a JTable.

Categories