Can you maintain the position of objects on a Java Swing panel? - java

I am currently desigining a calculator panel using Java Swing. However, it's an extreme PAIN to line up all of the buttons because they are always resizing and repositioning themseleves whenever I add a new button or change the size of a button.
Is there a type of layout or something that can "lock" the buttons in position so they are not affected when I move/resize/add other buttons?
Thanks,
Bob

Extending what Tom said...
To allow a component to become invisible yet hold its place, you can place it in a CardLayout along with an empty label and just swap which is visible.
You can create a class to do this for you as follows The main just shows an example where if you click a button it's deleted while retaining its position. I put in showComponent/hideComponent and setVisible(t/f) - depends on the style you like.
This might not exactly answer what you're looking for, but might be a useful piece for part of your application.
public class Placeholder extends JPanel {
private static final long serialVersionUID = 1L;
private CardLayout cardLayout_;
public Placeholder(JComponent component) {
cardLayout_ = new CardLayout();
setLayout(cardLayout_);
add(component, "visible"); // the component
add(new JLabel(), "hidden"); // empty label
}
public void showComponent() {
cardLayout_.show(this, "visible");
}
public void hideComponent() {
cardLayout_.show(this, "hidden");
}
public void setComponentVisible(boolean visible) {
if (visible)
showComponent();
else
hideComponent();
}
public static void main(String[] args) {
JFrame f = new JFrame();
f.setLayout(new GridLayout(2,0));
for (int n = 1; n < 10; n++) {
JButton b = new JButton(n + "");
final Placeholder placeholder = new Placeholder(b);
f.add(placeholder);
b.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
placeholder.hideComponent();
}
});
}
f.pack();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.setVisible(true);
}
}

I suggest instead of adding and removing buttons, you change their visibility property (setVisible). Some layout manager ignore non-visible components, so you may need to add a non-opqaue (or matching background) component in the same position that follows the preferred/minimum/maximum sizes of the original component. More simple you may want to use OverlayLayout.

You could use GridBagLayout to keep them in a grid, but allow them to resize

Extending what the others said...
Instead of making the components invisible, you can also disable them...
Of course, it depends on your application and your preferences.
Beside the above mentioned layouts, there are some freely available ones, like TableLayout and the versatile MigLayout.

It probably depends on the development environment you are using.
In Netbeans you can right-click the control container (form/panel) and select from a number of layout options. 'Absolute Layout' will let you position the controls without them all being resized automatically, although it won't let you place controls on top of each other.

I suggest you to check A Visual Guide to Layout Managers. Depending on the look and functionality you want there are different layouts that could work for you. If the main problem is the resize operation (of the parent JFrame o JPanel I assume), the Spring Layout allows you to establish constraints regarding the relative place of components.

Related

Using Java pack() method

I can't make the pack() method work. I tried several things. My code looks like this at the moment:
Class 1:
public static void main( String[] args )
{
java.awt.EventQueue.invokeLater(new Runnable() {
public void run()
{
JavaGui mygui = new JavaGui();
// mygui.setSize(1154, 753);
mygui.setVisible(true);
mygui.pack();
Class 2:
public class JavaGui extends javax.swing.JFrame
{
public JavaGui()
{
getContentPane().setLayout(null);
..
getContentPane().add(panelLeft);
...
getContentPane().add(panelRight);
I tried putting the pack method in everywhere, but it's not going to work with this way of adding gui elements. Any suggestions why? I also tried adding everything to a JFrame instead of the getContentPane(), but I can't make that work either.
Don't use null layouts together with pack(). The pack method tells the layout managers and components to size themselves optimally, and if you instead use null layouts, then the gui risks shrinking to a minimal size, since there is no layout to hold it together.
Don't use null layouts at all for the most part. Using these risk your creating rigid GUI's that are almost impossible to extend, improve, debug.
Don't use setSize(...) and pack(). The layouts mostly respect the preferred sizes of components, not their sizes.
Instead:
Use a pleasing and sensible combination of nested JPanels, each using its own layout manager.
Let the components and the layout managers size themselves.
Then pack should help.
The general order that I do is to add all components to the GUI, then call pack(), then setLocationByPlatform(true) (I think), then setVisible(true).
For better help, please check out the Swing Layout Manager Tutorials.
Here are a couple examples to other questions on this site that use various layout managers:
A combination of BorderLayout and GridLayout to create a calculator
BorderLayout and BoxLayout Combination for labels and JTextFields
Using GridBagLayout to create flexible label/textfield grid
I would recommened beginners on building up swing guis to use a good ide with a builtin gui designer like eclipse and windowbuilder or netbeans with matisse. It will help you building up a prototype of your desired gui and gives you an insight how the layouting is done in the source code.
Experiment with the differenet layouts and what is happening when some values are changed.
one does not simply build up a well behaving gui without understanding how the layout works, so doing the recommended tutorials and looking at examples as already posted by Hovercraft Full Of Eels is absolutely necessary.
For your case i just guess what you were up to. Because youre mentioning left and right panels i suggest a JSplitPane which let you divide your screen in two areas which are customizable in size and orientation.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
public class JavaGui extends JFrame {
//SerialVersionId http://stackoverflow.com/questions/285793/what-is-a-serialversionuid-and-why-should-i-use-it
private static final long serialVersionUID = 1L;
public static void main(String[] args) {
//Calls to Gui Code must happen on the event dispatch thread that the gui does not get stuck
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new JavaGui().setVisible(true);
}
});
}
public JavaGui() {
// Set the desired size of the frame to determine the maximum size of its components
setPreferredSize(new Dimension(1024, 768));
// Set the default close operation, if press x on frame, destroy the frame and exit the application - others are just destroy the frame or just hide the
// frame
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// BorderLayout because we just need a centric gui with one component, here JSplitPane in full size
getContentPane().setLayout(new BorderLayout(0, 0));
// JsplitPane is a bit special as it depends on the divider location between both panels, for the sake of a small example we take the default -1,
JSplitPane splitPane = new JSplitPane();
// 0.5 divides extra space equally to left and right component when resizing the frame - so specifiying a size for the left and right component is not
// necessary
// use the divider location default -1 to let the width of the left component decide where the right component begins, in that case because of the
// resize weight half and half
splitPane.setDividerLocation(-1);
splitPane.setResizeWeight(0.5);
getContentPane().add(splitPane, BorderLayout.CENTER);
// For the panels the same layout as default as the intention is not stated in your question
JPanel leftPanel = new JPanel();
splitPane.setLeftComponent(leftPanel);
leftPanel.setLayout(new BorderLayout(0, 0));
JPanel rightPanel = new JPanel();
splitPane.setRightComponent(rightPanel);
rightPanel.setLayout(new BorderLayout(0, 0));
// Add a button Panel to the south for doing something - flow layout for letting the components flow to the right side
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
getContentPane().add(buttonPanel, BorderLayout.SOUTH);
// Close Button for closing the frame
JButton btnExit = new JButton("Destroy this frame, but let application run");
btnExit.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
dispose();
}
});
buttonPanel.add(btnExit);
// Set every component to its preferred size
pack();
// Make it visible
setVisible(true);
}
}
If you want your JFrame to work with a null layout, rearrange your code so that it looks like this:
public class JavaGui extends javax.swing.JFrame
{
public JavaGui()
{
setMinimumSize(1154, 753); // Make sure you do setMinimumSize() instead of setSize() when using pack() so that the JFrame does not shrink to 0 size
setLayout(null);
add(panelLeft);
add(panelRight);
pack();
}
// Next is main method
Main:
public static void main(String[] args)
{
java.awt.EventQueue.invokeLater(new Runnable() {
public void run()
{
new JavaGui().setVisible(true);
// Do not do any formatting for your JFrame here
}
});
Before, you were modifying the JFrame after it was set visible, so that usually does not work, except for pack(). All components and settings for your JFrame should not be in the main method if you are using an anonymous inner class.
You can also use other layouts. Null layouts are for getting pixels in precise locations, which is used for advanced GUI design such as creating a custom GUI, but it seems that you are making a generic GUI with JPanels. For this, I would recommend using a GridBagLayout, which keeps everything centered if the frame is resized and is easy to use. To use a GridBagLayout, you have to replace setLayout(null); with setLayout(new GridBagLayout()); and set GridBagConstraints. Here is some example code of making a panel with a component and a GridBagLayout:
JPanel pane = new JPanel(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
if (shouldFill) {
//natural height, maximum width
c.fill = GridBagConstraints.HORIZONTAL;
}
//For each component to be added to this container:
//...Create the component...
//...Set instance variables in the GridBagConstraints instance...
pane.add(theComponent, c);
// Source: Oracle Docs

My jscorllpane doesn't work

public class Gui extends JFrame{
JTextField tf_input;
JTextArea ta_output;
JScrollPane sp_taop;
private class klis implements KeyListener{
#Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
if (e.getKeyCode()==KeyEvent.VK_ENTER){
//System.out.println("enter"); //debug
if (!tf_input.getText().isEmpty()){
String input = tf_input.getText();
ta_output.setText(ta_output.getText()+input+'\n');
System.out.println(input); //debug
System.out.println("ok!"); //debug
sp_taop.validate();
tf_input.setText(null);
}
}
}
}
Gui(){
System.out.println("hello");
inti();
render();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
private void inti() {
setSize(800, 600);
//text field input
tf_input = new JTextField();
tf_input.setSize(550, 24);
tf_input.addKeyListener(new klis());
//text Area
ta_output = new JTextArea("hello World\n",30,60);
ta_output.setSize(ta_output.getPreferredSize());
ta_output.setLineWrap(true);
ta_output.setEditable(false);
sp_taop = new JScrollPane();
sp_taop.add(ta_output);
sp_taop.setSize(ta_output.getSize().width+20,ta_output.getSize().height);
sp_taop.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
}
private void render() {
Panel p = new Panel(null);
ta_output.setLocation(0, 0);
sp_taop.setLocation(10, 10);
tf_input.setLocation(10, (sp_taop.getSize().height+20));
ta_output.repaint();
//adding
p.add(sp_taop);
p.add(tf_input);
add(p);
}
}
I'm running this on java 1.7.0_25 on vbox 4.3.4 lUbuntu
The problem is that the jscroll is not updating itself! What should i do?
I've been searching google for hours now and i have not found anything close to what I am looking for.
By the way sorry about the plain code it was hard to explain the problem without it.
Don't use JScrollPane#add, this is not how scroll panes work, instead try using JScrollPane#setViewportView
Take a look at How to use Scroll Panes
Don't add you text field to another container. Components can only have a single parent, so adding it to a second container will remove it from the first.
On a side note. I would avoid the use of KeyListener, text components have the capacity to consume key events, meaning that your listener may not actually be notified. Also, if it is called, the component will be in the middle of of mutation operation (it will be trying to update it's model) which can cause unexpected issues and possibly dirty updates.
I might be tempted to use a DocumentListener in this case, but you would need to test it.
I would also avoid the use of null layouts. To start with, you won't be able to effect the position of the text field within the context of the scroll pane as the scroll pane uses it's own layout manager.
Moreover, the differences between OS, video drivers and fonts makes the potential rendering outputs endless. Layout managers take out the guess work when dealing with these situations

Widgets behaving strangely when using "setlayout(null)"

I'm doing the following call in my code:
...
setLayout(null);
...
I'm trying to place a button and a textfield by specifying their x and y coordinates.
The problem when I run the program (either with Eclipse or BlueJ) is that I need to run on the panel up to the position of the button and the textfield in order to see respectively the button and the textfield.
When I find the textfield, it is small. Only when I start writing it assumes the size I specified.
Does anyone know how to solve it?
Avoid setLayout (null), unless you have a very good reason for it. You can learn about layout managers here: http://docs.oracle.com/javase/tutorial/uiswing/layout/using.html
If you still want to use a null layout, you have to set the width and height of the component, not just its x and y position (see the setSize method).
From the link mentioned above:
Although we strongly recommend that you use layout managers, you can
perform layout without them. By setting a container's layout property
to null, you make the container use no layout manager. With this
strategy, called absolute positioning, you must specify the size and
position of every component within that container. One drawback of
absolute positioning is that it does not adjust well when the
top-level container is resized. It also does not adjust well to
differences between users and systems, such as different font sizes
and locales.
I'd recommend using the setBounds method instead of the setLocation
JTextField tf = new JTextField(10);
Dimension d = tf.getPreferredSize();
tf.setBounds(x, y, d.width, d.height);
Of course, if you're using a null Layout manager, you also need to take care of your preferredSize. Here's an example that incorporates all the major aspects:
import java.awt.*;
import javax.swing.*;
public class TestProject extends JPanel{
public TestProject(){
super(null);
JTextField tf = new JTextField(10);
add(tf);
Dimension d = tf.getPreferredSize();
tf.setBounds(10, 20, d.width, d.height);
}
#Override
public Dimension getPreferredSize(){
//Hard coded preferred size - but you'd probably want
//to calculate it based on the panel's content
return new Dimension(500, 300);
}
public static void main(String args[])
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
JFrame frame = new JFrame();
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.setContentPane(new TestProject());
frame.pack();
frame.setVisible(true);
}
});
}
}

Controlling another class from a different class

I am having a bit of problem regarding Swing. I have a JFrame called FrameMain. Inside it is a JPanel called panelChoices.
When FrameMain is called/created, it fills up the panelChoices object with a number of PanelEntries objects, which is a JPanel with a number of JButtons in it (it is a different class that I wrote).
What I want to do is when I click one of the buttons inside the PanelEntries object, I want to destroy/remove FrameMain, along with the rest of it components (including the PanelEntries object that contains the JButton).
I've tried using super but it returns the JPanel (the PanelEntries object) that holds the JButton and not FrameMain that holds them all together. How can I achieve this?
EDIT: It seems that I am not clear enough, so here's a bit more information from my work. I don't have the actual code right now because I am on a different machine but I hope this will help elaborate my question.
public class FrameMain() {
private JFrame frameMain;
private JPanel panelChoices;
public FrameMain(args) {
createGUI();
loadData();
}
private void createGUI() {
JFrame frameMain = new JFrame();
JPanel panelChoices = new JPanel(new GridLayout(1,1));
frameMain.add(panel);
// removed formatting and other design codes since they are not important.
pack();
}
private void loadData() {
boolean available;
for (int i = 1; i <= 10; i++) {
// do some if/else and give value to boolean available
PanelEntries panel = new PanelEntries(i, available);
frameMain.add(panel);
// more code here to handle data.
}
}
}
public class PanelEntries() extends JPanel {
public PanelEntries(int num, boolean avb) {
JButton button = new JButton("Button Number " + num);
button.setEnabled(avb);
add(button);
// add action listener to created button so that it calls 'nextScreen()' when clicked.
// more code
pack();
}
private void nextScreen() {
// destroy/dispose MainFrame here.
// See Notes.
AnotherFrame anotherFrame = new AnotherFrame();
}
}
Notes:
All classes are inside their own .java file.
I need to know how to dispose FrameMain from the button inside the PanelEntries object, not just disposing a JFrame.
As per the given information,
If you want to exit the application, its not a big deal use System.exit(0); :)
If you mean to dispose the frame, jframe.dispose();
If you want to remove a componet / all components you can use .remove(Component) / .removeAll() etc
If this did not help, please re-write your question with more information.

Can't add components in VerticalSplitPanel

I am building an application using the VAADIN framework.
I am trying to add a panel in a view containing a VerticalSplitPanel which contains two components (a button and a label for the moment).
Pretty straight forward but I'm having big problems getting it done.
I can identify that something happens, because I see the "split-divider" show when I run it in a browser, but no components inside the split-panel.
This is how I initialize the panel for the moment.
public class M2MInventory_SubscriptionsView extends AbstractView {
private Panel panel = new Panel();
private VerticalSplitPanel vSplit = new VerticalSplitPanel();
private Button upperButton = new Button("Upper Button");
private Button lowerButton = new Button("Lower Button");
public M2MInventory_SubscriptionsView() {
panel.setContent(vSplit);
vSplit.setFirstComponent(new Button("Upper"));
vSplit.setSecondComponent(new Label("Lower"));
addComponent(panel);
}
Can anyone spot an error in my ways?
Try to set panel height first. It will work but I'm not sure why.
The default layout of Panel is VerticalLayout with undefined height. It's strange, because I thought If you insert enough components in such a layout, it will grow.

Categories

Resources