On a project, I have a JFrame A starting a JDialog B, which itself starts a JDialog C (all using buttons). However, when following one of these procedures :
I click on A to start B ; or
I click on A to start B, then on B to start C, then click on C's cancel button,
what is displayed in B is not the same (the second procedure gives a weird ugly thing).
I don't get why that could be, since in both ways my updateAll method is called.
I tried to recreate this with a little program so that it's easier to see what's going on. Solving this may or may not solve it on my actual project, but it will surely help.
Since I don't know where it could come from, here's the full code of my (test) program. Brace yourselves.
The 'A' Frame
public class MyFrame extends JFrame {
private static final long serialVersionUID = 7073064926636937881L;
public MyFrame() {
this.setSize(200, 300);
JButton button = new JButton("Click me");
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
new MyDialog1().setVisible(true);
}
});
this.getContentPane().add(button);
}
public static void main(String args[]) {
new MyFrame().setVisible(true);
}
}
The 'B' Dialog
public class MyDialog1 extends JDialog {
private static final long serialVersionUID = 9181006217120036637L;
private JScrollPane scrollPane;
public String text = "aaaaaaaaaaa\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\naaaaaaaa\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\naaaaaaaa";
public MyDialog1() {
this.setVisible(false);
this.setSize(800, 600);
this.initComponent();
this.updateAll();
}
private void initComponent() {
this.getContentPane().setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
this.scrollPane = new JScrollPane();
c.gridx = 0;
c.gridy = 0;
this.getContentPane().add(this.scrollPane, c);
c.gridx = 0;
c.gridy = 1;
JButton b = new JButton("Supposedly edit stuff");
final MyDialog1 caller = this;
b.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
new MyDialog2(caller).setVisible(true);
}
});
this.getContentPane().add(b, c);
c.gridx = 1;
c.gridy = 1;
b = new JButton("Leave");
b.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
setVisible(false);
}
});
this.getContentPane().add(b, c);
}
public void updateAll() {
JPanel mainPanel = new JPanel();
for (int i = 0 ; i < 5 ; i++) {
JPanel subPanel = new JPanel();
JTextArea t = new JTextArea(this.text);
t.setSize(60, 30);
t.setVisible(true);// Useful ? What about setSize ?
subPanel.add(t);
mainPanel.add(subPanel);
}
this.scrollPane.setSize(150, 150); // FIXME When in initComponent, doesn't do anything, and when in updateAll, behavior is inconsistent
this.scrollPane.setViewportView(mainPanel); // Replacing previous JPanel
}
}
The 'C' Dialog
public class MyDialog2 extends JDialog {
private static final long serialVersionUID = 5676648412234106581L;
private MyDialog1 caller;
public MyDialog2(MyDialog1 c) {
this.setSize(100, 150);
this.caller = c;
JButton cancelButton = new JButton("Cancel");
cancelButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
setVisible(false);
caller.text += "\nbbbbbbbbbbbbb\nbbbbbbbbbbbbbbbbbb\nbbbbbbbbbbb\nbbbbbbbbbbbbbb\ncccccccccccccccccccccccccccccccccccccccccccc\ncccccccccc";
caller.updateAll();
}
});
this.getContentPane().add(cancelButton);
}
}
Thanks for your help.
I suggest to use
c.gridx = 0;
c.gridy = 0;
c.fill = GridBagConstraints.BOTH; // make the component fill its display area entirely
c.ipady = 150; //height
c.anchor = GridBagConstraints.FIRST_LINE_START; // component start from the left top corner
this.getContentPane().add(this.scrollPane, c);
for definition of JScrollPane constraints.
In additition, add validate() and repaint() after modifying elements
this.scrollPane.setViewportView(mainPanel); // Replacing previous JPanel
this.validate();
this.repaint();
Easy answer which ignores good practice:
Replace this:
this.scrollPane.setSize(150, 150);
with this:
this.scrollPane.setMinimumSize(new Dimension(150, 150));
Calling setSize on a component which is in a Container with a layout usually does nothing; at best, it will set the size until the next time the container is validated, since the size will be overwritten by the layout manager. However, setting a minimumSize (or preferredSize or maximumSize) sets a persistent property that is respected by (most) layout managers.
Why does setMinimumSize make a difference? Because you have not set any weightx or weighty properties on any of your GridBagConstraints, so there is not enough room for the GridBagLayout to display your JScrollPane at its preferred size. When GridBagLayout determines there isn't enough room to display everything at the size it requires, the layout "punts" and forces everything to revert to its minimum size.
The first time you display the B dialog, you are seeing the JScrollPane at its minimum size (that is, just large enough to display the scrollbars and viewport border). After canceling the C dialog, the setSize(150, 150) takes effect, but it's likely that any subsequent changes to any descendant of the GridBagLayout would cause that (150, 150) to be overwritten with the JScrollPane's minimum size again.
More complex answer which is good practice:
Remove all calls to setSize in every class. Usually you should not set explicit sizes at all, but if you must do so, use setPreferredSize.
Components which are made to grow and shrink, like JScrollPanes, should be assigned positive weightx and weighty values in their corresponding GridBagConstraints, along with fill being set to GridBagConstraints.BOTH as pcej suggested.
Related
I've hit a problem in getting a JPanel to update.
My simple program uses a custom JPanel which displays a label and a textfield. A Jbutton on the main panel is used to replace the JPanel with a new JPanel. The initial panel shows up fine but when the button is pressed the panel is not updated with a new MyPanel. I can tell that a new object is being created as count is being incremented.
public class SwingTest extends JFrame{
private JPanel mp;
private JPanel vp;
private JButton button;
public static void main(String[] args) {
SwingTest st = new SwingTest();
}
public SwingTest() {
vp = new MyPanel();
mp = new JPanel(new BorderLayout());
mp.add(vp, BorderLayout.CENTER);
button = new JButton("Change");
button.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent ae) {
vp = new MyPanel();
vp.revalidate();
}
});
mp.add(button, BorderLayout.SOUTH);
this.add(mp);
this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
setLocationRelativeTo(null);
setSize(250, 150);
pack();
setVisible(true);
}
}
and my custom panel....
public class MyPanel extends JPanel{
private JLabel label;
private JTextField tf;
static int count = 0;
public MyPanel(){
count++;
setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
setPreferredSize(new Dimension(400, 200));
c.gridx = 0;
c.gridy = 0;
label = new JLabel(String.valueOf(count));
tf = new JTextField(10);
add(label,c);
c.gridx = 1;
add(tf, c);
}
}
You state:
A Jbutton on the main panel is used to replace the JPanel with a new JPanel.
And yet this code:
button.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent ae) {
vp = new MyPanel();
vp.revalidate();
}
});
and yet this code does not do this at all. All it does is change the JPanel referenced by the vp variable, but has absolutely no effect on the JPanel that is being displayed by the GUI, which suggests that you're confusing reference variable with reference or object. To change the JPanel that is displayed, you must do exactly this: add the new JPanel into the container JPanel into the BorderLayout.CENTER (default) position, then call revalidate() and repaint() on the container.
e.g.,
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
// vp = new MyPanel();
// vp.revalidate();
mp.remove(vp); // remove the original MyPanel from the GUI
vp = new MyPanel(); // create a new one
mp.add(vp, BorderLayout.CENTER); // add it to the container
// ask the container to layout and display the new component
mp.revalidate();
mp.repaint();
}
});
Or better still -- use a CardLayout to swap views.
Or better still -- simply clear the value held by the JTextField.
For more on the distinction between reference variable and object, please check out Jon Skeet's answer to this question: What is the difference between a variable, object, and reference?
I have a JPanel inside of a JScrollPane inside of a GridBagLayout
that is not scrolling properly. What should happen is that every time the
xxx button is pressed, a new line is added inside of the scrolling pane.
What actually happens is that if xxx is pressed, say 10 times, only the
first seven lines show up and the rest can't be scrolled to. Can anyone
suggest changes to the source code below that will make scrolling behave
properly? I have spent hours on this with no success, trying strategies all
over the Web.
Notes:
The text is split in paintComponent because drawString does
not handle end-of-line characters.
The JPanel inside of JScrollPane inside of GridBagLayout configuration is a necessary part of a much larger
piece of software with the same problem, so I have kept it here.
Thanks.
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class question {
public static void main(String[] args) {
new DrawingGUI();
}
private static class ScrollingPane extends JPanel {
private static final long serialVersionUID = 1L;
String text;
public ScrollingPane() {
setPreferredSize(new Dimension(300, 100));
text = "";
}
public void SetText(String text_x) {text = text + System.lineSeparator() + text_x;}
public void paintComponent(Graphics g) {
super.paintComponent(g);
FontMetrics fm = g.getFontMetrics();
int y = -fm.getHeight();
for (String text : text.split("\n"))
g.drawString(text, 0, y += fm.getHeight());
}
}
static class DrawingGUI extends JFrame implements ActionListener {
private static final long serialVersionUID = 1L;
JPanel jp;
JScrollPane js;
ScrollingPane sp;
int LineNum;
DrawingGUI() {
LineNum = 0;
JFrame frame = new JFrame("xxx");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
addComponentsToPane(frame.getContentPane());
frame.setSize(800,800);
frame.setVisible(true);
}
public void addComponentsToPane(Container pane) {
pane.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
JButton button = new JButton("xxx");
button.setActionCommand("add_text");
button.addActionListener(this);
c.gridx = 0;
c.gridy = 0;
pane.add(button, c);
jp = new JPanel();
c.gridx = 0;
c.gridy = 1;
c.ipadx = 600;
c.ipady = 450;
pane.add(jp, c);
sp = new ScrollingPane();
js = new JScrollPane(sp);
js.getViewport().setPreferredSize(new Dimension(300,100));
c.gridx = 0;
c.gridy = 2;
c.ipady = 50;
pane.add(js, c);
}
public void actionPerformed(ActionEvent e) {
if ("add_text".equals(e.getActionCommand())) {
LineNum++;
sp.SetText("LineNum = " + LineNum);
sp.revalidate();
sp.repaint();
}
}
}
}
You're short circuiting the ability of your ScrollingPane JPanel from correctly sizing itself with this line:
setPreferredSize(new Dimension(300, 100));
This will fix the size of the ScrollingPane JPanel. I see that you have several possible solutions:
Difficult: override getPreferredSize() for the ScrollingPane JPanel, and calculate the appropriate preferred size based on the size of the text it holds and draws using FontMetrics.
Easier: Don't add text as you're doing, but rather have ScrollingPane use a GridLayout(0, 1) (one column, variable number of rows), and add JLabels to the ScrollingPane when new text is needed to be added. Then call revalidate() and repaint() on it.
Easier still: Don't use a ScrollingPane JPanel but rather a JTextArea, that looks like a JPanel and that can't be edited. Add that to the JScrollPane, and again, do not restrict its size
Easiest: Just use a JList as that's the functionality you are using here.
For instance, either of these would work and would look similar:
private static class ScrollingPane2 extends JPanel {
private static final long serialVersionUID = 1L;
private JTextArea textArea = new JTextArea(6, 20);
public ScrollingPane2() {
setLayout(new BorderLayout());
add(textArea, BorderLayout.CENTER);
textArea.setEditable(false);
textArea.setFocusable(false);
textArea.setBackground(null);
}
public void SetText(String text_x) {
textArea.append(text_x + "\n");
}
}
private static class ScrollingPane3 extends JPanel {
private static final long serialVersionUID = 1L;
private DefaultListModel<String> listModel = new DefaultListModel<>();
private JList<String> jList = new JList<>(listModel);
public ScrollingPane3() {
setLayout(new BorderLayout());
add(jList, BorderLayout.CENTER);
jList.setBackground(null);
}
public void SetText(String text_x) {
listModel.addElement(text_x);
}
}
I have a class called 'Panel' that extends JPanel and it is inside another class called 'Main'. The constructor instantiates JFrame, and all the GUI components, and sets it all up, such as size.
The class 'Panel' which extends JPanel has a method public void paintComponent(Graphics g){} and inside it I added a few JButtons and used g.drawString's.
Then in the 'Main' class, I added the 'Panel' to the JFrame.
My question is, I am trying to implement an actionListener to a button added inside the 'Panel' class. The actionListener function would add more buttons and use g.drawString's as well. Now where would I place the ActionListener in order to do so? How can I use g.drawString for a particular panel and the g.drawString line is inside another class, which is the ActionListener class? I would need to use Graphics g of paintComponent inside the actionPerformed.
Thank you!
EDIT -
CODE EXAMPLE:
public class Main{
private JFrame jf;
private JTextField jtf1;
private JTextField jtf2;
private Panel p;
private JComboBox jcb1;
private JComboBox jcb2;
private JButton button;
private Object options[];
//ActionListener Variables
private int string1 = 150;
private int string2 = 150;
private int yJtf1 = 150;
private int yJtf2 = 160;
private int cb1 = 140;
private int cb2 = 165;
private int count = 0;
public Main(){
jf= new JFrame();
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setSize(700, 700);
p = new Panel();
jtf1 = new JTextField("", 20);
jtf2= new JTextField("", 20);
Object options[] = {""};
jcb1 = new JComboBox(tools);
jcb2 = new JComboBox(tools);
button = new JButton("+");
jf.add(p);
jf.setVisible(true);`
}
public class Panel extends JPanel{
public Panel(){
this.setLayout(null);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
/*button.addActionListener(new ActionListener(){ //Would this work or should the ActionListener be a class as shown below?
public void actionPerformed(ActionEvent e){
if(count < 3){ //Won't be allowed to add anymore after 3 times
string1 += 50;
string2 += 50;
jtf1 += 50;
jtf2 += 50;
cb1 += 50;
cb2 += 45;
//Would like to add the following components to the 'Panel' (which is a JPanel) whenever the JButton 'button' already added to 'Panel' is clicked.
p.add(jtf1); //Would doing p.add really add to the panel when the ActionListener is called?
jtf1.setBounds(60, yJtf1, 50, 40);
p.add(jtf2);
jtf2.setBounds(60, yJtf2, 50, 40);
add(jcb1);
jcb1.setBounds(250, cb1, 50, 40);
add(left2);
jcb2.setBounds(250, cb2, 50, 40);
Font font = new Font("TimesRoman", Font.BOLD, 18);
g.setFont(font); //Getting error on 'g' regardless
g.drawString("Hi", 15, string1); //This is the main problem, how would I be able to add this strings to the 'Panel' (which is a JPanel)
g.drawString("There", 330, string1);
}
count++;
}
});*/
add(jtf1);
jtf1.setBounds(100, 30, 120, 30);
add(jtf2);
ljtf2.setBounds(100, 60, 120, 30);
add(button);
plusButton.setBounds(200,150, 50, 50);
//button.addActionListener(new ButtonClicked()); if doing ActionListener via class like below
add(jcb1);
jcb1.setBounds(300, 350, 100, 50);
add(ljcb2);
jcb2.setBounds(300, 350, 100, 25);
Font font = new Font("Arial", Font.BOLD, 12);
g.setFont(font);
g.drawString("Item:", 40, 45);
g.drawString("Cost:", 40, 75);
}
}
public static void main(String [] args){
new Main();
}
class ButtonClicked implements ActionListener{ //Action Listener: The follow is what I am trying to implement
public void actionPerformed(ActionEvent ae){
if(count < 3){ //Won't be allowed to add anymore after 3 times
string1 += 50;
string2 += 50;
jtf1 += 50;
jtf2 += 50;
cb1 += 50;
cb2 += 45;
//Would like to add the following components to the 'Panel' (which is a JPanel) whenever the JButton 'button' already added to 'Panel' is clicked.
p.add(jtf1); //Would doing p.add really add to the panel when the ActionListener is called?
jtf1.setBounds(60, yJtf1, 50, 40);
p.add(jtf2);
jtf2.setBounds(60, yJtf2, 50, 40);
mp.add(jcb1);
jcb1.setBounds(250, cb1, 50, 40);
mp.add(left2);
jcb2.setBounds(250, cb2, 50, 40);
Font font = new Font("TimesRoman", Font.BOLD, 18);
g.setFont(font);
g.drawString("Hi", 15, string1); //This is the main problem, how would I be able to add this strings to the 'Panel' (which is a JPanel)
g.drawString("There", 330, string1);
}
count++;
}
}
}
I would need to use Graphics g of paintComponent inside the actionPerformed."`
No, you wouldn't since Swing graphics is passive not active. The Graphics object would reside in the JPanel's paintComponent method and would be passed to it from the JVM when it and only it calls the paintComponent method. The actionPerformed method would then change the value of a String variable or perhaps an ArrayList<String>, call repaint(), and then the JPanel's paintComponent method would use the changed String to draw the appropriate text.
If you need more specific help, consider telling us a few more details and posting a minimal example program.
Edit
On review of your code I have several suggestions:
Please try to fix your code a bit so that it is either compilable or as close to compilable as possible.
Never add components or do anything but painting inside of your paintComopnent(...) method. You don't have full control over when or even if that method will be called, and placing something in inside of this method will result in unwanted side effects, like combo boxes that simply won't work.
If you ever wanted text shown in your program, you can always add a JLabel.
Your program uses null layout and setBounds(...) something that results in a rigid GUI that may look good on one system but will usually look poor on any other system or screen resolution. Also programs created this way are very hard to debug, maintain and upgrade. Instead use the layout managers as this is what they excel at: at creating complex flexible GUI's that can be enhanced and changed easily.
Edit
I'm guessing what you really want to do, and that possibly is to add more components to allow the user to enter more data into the GUI. You also want to add text perhaps to guide the user in what the purpose of the components may be. If this is so, then the best solution is not to add Strings to the GUI in paintComponent but rather to add Strings/Components in an organized fashion with the Strings being displayed in JLabels, and the components and the labels all held in a JPanel or JPanels, and added to the GUI using layout managers.
For example, if you want the user to add data in two JTextfields and have two JComboBoxes, and then allow the user to add 3 more of these guys if need be, a GUI could look something like this:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
#SuppressWarnings("serial")
public class Main2 extends JPanel {
private List<DataPanel> dataPanelList = new ArrayList<>();
private JPanel dataPanelHolder = new JPanel();
public Main2() {
DataPanel dataPanel = new DataPanel();
dataPanelList.add(dataPanel);
setLayout(new BorderLayout());
dataPanelHolder.setLayout(new BoxLayout(dataPanelHolder, BoxLayout.PAGE_AXIS));
dataPanelHolder.add(dataPanel);
JPanel innerBorderLayoutPanel = new JPanel(new BorderLayout());
innerBorderLayoutPanel.add(dataPanelHolder, BorderLayout.PAGE_START);
JScrollPane scrollPane = new JScrollPane(innerBorderLayoutPanel);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
int w = dataPanel.getPreferredSize().width;
int h = dataPanel.getPreferredSize().height * 4;
Dimension viewPortSize = new Dimension(w, h);
scrollPane.getViewport().setPreferredSize(viewPortSize);
JPanel buttonPanel = new JPanel(new GridLayout(1, 0, 5, 0));
buttonPanel.add(new JButton(new AddDatatAction("Add")));
buttonPanel.add(new JButton(new ExitAction("Exit", KeyEvent.VK_X)));
add(scrollPane, BorderLayout.CENTER);
add(buttonPanel, BorderLayout.PAGE_END);
}
private class AddDatatAction extends AbstractAction {
private int maxCount = 4;
public AddDatatAction(String name) {
super(name);
int mnemonic = (int)name.charAt(0);
putValue(MNEMONIC_KEY, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
if (dataPanelList.size() < maxCount) {
DataPanel dataPanel = new DataPanel();
dataPanelList.add(dataPanel);
dataPanelHolder.add(dataPanel);
dataPanelHolder.revalidate();
dataPanelHolder.repaint();
}
}
}
private class ExitAction extends AbstractAction {
public ExitAction(String name, int mnemonic) {
super(name);
putValue(MNEMONIC_KEY, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
Window win = SwingUtilities.getWindowAncestor(Main2.this);
win.dispose();
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("Main2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new Main2());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
#SuppressWarnings("serial")
class DataPanel extends JPanel {
private static final String[] TOOLS = {"Tool 1", "Tool 2", "Tool 3", "Tool 4"};
private static final String[] FIELD_LABELS = {"Item", "Cost"};
private static final String[] COMBO_LABELS = {"Foo", "Bar"};
private JTextField[] fields = new JTextField[FIELD_LABELS.length];
private List<JComboBox<String>> comboList = new ArrayList<>();
public DataPanel() {
setBorder(BorderFactory.createTitledBorder("Data"));
setLayout(new GridBagLayout());
for (int i = 0; i < FIELD_LABELS.length; i++) {
add(new JLabel(FIELD_LABELS[i]), createGbc(0, i));
fields[i] = new JTextField(10);
add(fields[i], createGbc(1, i));
JComboBox<String> combo = new JComboBox<>(TOOLS);
comboList.add(combo);
add(combo, createGbc(2, i));
add(new JLabel(COMBO_LABELS[i]), createGbc(3, i));
}
}
public static GridBagConstraints createGbc(int x, int y) {
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = x;
gbc.gridy = y;
gbc.gridwidth = 1;
gbc.gridheight = 1;
gbc.weightx = 1.0;
gbc.weighty = 1.0;
int ins = 4;
gbc.insets = new Insets(ins, ins, ins, ins);
return gbc;
}
}
And could look like:
one datapanel added
four added:
I am trying to position two buttons to be on the left top side. They are always in the center top though.
I have tried this:
jp = new JPanel();
jp.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
//c.anchor = GridBagConstraints.BASELINE_TRAILING;
c.anchor = GridBagConstraints.WEST;
c.gridx = 0;
c.gridy = 0;
jp.add(test, c);
c.gridy++;
jp.add(atest, c);
add(jp);
But its still at center, not on left side (http://i.imgur.com/MYF8dqr.png).
This is an image I took. The red is a scetch of how I wish the buttons to be.
Updated:
ArrayList<String> atest = new ArrayList<String>();
JLabel[ ] asd = new JLabel[100];
int temp = 0;
GridBagConstraints c = new GridBagConstraints();
c.anchor = GridBagConstraints.FIRST_LINE_START;
c.weightx = 1.0;
c.weighty = 1.0;
c.gridx = 0;
c.gridy = 0;
atest.add("Hello");
atest.add("haelp");
atest.add("yummy");
atest.add("whats wrong");
for(String server : servers)
{
asd[temp] = new JLabel();
asd[temp].setText(server);
jp.add(asd[temp], c);
c.weighty++;
c.gridy++;
temp++;
}
Im trying to read string from array and add it as label one after other on left side.
Doesn't work out too good, here's the result:
http://prntscr.com/26a9rb
If gridbaglayout is bad way of doing it, which way should I go for?
Read the section from the Swing tutorial on How to Use GridBagLayout for an explanation of how the contraints work. The section on weightx, weighty should solve your problem.
If you are using Eclipse you can use a plugin called WindowBuilder to build the frames:
http://www.eclipse.org/windowbuilder/
You have to apply spring layout to your window in WindowBuilder to drag and drop buttons from the toolbar like in the .NET designer in VisualStudio.
To solve your problem:
set the weightx and weighty of GridBagConstraint to some other value than 0 as #camickr mentioned.
you should set the anchor to FIRST_LINE_START. The WEST anchor will put the component on the left side of its display area but the components will be centered vertically. It is equivalent to LINE_START for horizontal, left-to-right orientations of components
Edit:
Try to look into the BoxLayout. Cleaver use of preferredSize with alignment setting can achieve what you are expecting. I have written a SSCCE for you. However, reading about the size behavior with BoxLayout will help you understand the example better.
class AComponent extends JPanel
{
public AComponent(Color c) {
setBackground(c);
setAlignmentX(LEFT_ALIGNMENT);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 100);
}
#Override
public Dimension getMinimumSize() {
return new Dimension(getPreferredSize());
}
#Override
public Dimension getMaximumSize() {
Dimension dim = getPreferredSize();
return new Dimension(200, dim.height);
}
}
class MyWindow extends JFrame
{
public MyWindow ()
{
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel container = new JPanel()
{
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 350);
}
};
container.setLayout(new BoxLayout(container, BoxLayout.Y_AXIS));
container.setBackground(Color.WHITE);
container.add(new AComponent(new Color(0xFFAA00)));
container.add(new AComponent(new Color(0x359DBD)));
container.add(new AComponent(new Color(0xFFD47E)));
add(container);
pack();
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new MyWindow().setVisible(true);
}
});
}
}
You need to set jp like this: jp.setLayout(null);
Picture included I have another issue where my buttons go to the top-right after the user inputs their name. At this point, text shows up in the GUI on the LEFT side of the center which seems it would be "WEST" when I put "CENTER". Code:
public TheDungeon()
{
setTitle("InsertGameNameHere");
setSize(750, 600);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new BorderLayout());
setLocationRelativeTo(null);
buildButtonPanel();
characterInfoPanel = new JLabel("<html>Character information will go here</html>");
gameScreen = new JLabel();
inventoryPanel = new JLabel("<html>This is for the inventory</html>");
add(gameScreen, BorderLayout.CENTER);
add(buttonPanel, BorderLayout.SOUTH);
setVisible(true);
//Program run
userName();
gameScreen.setText("<html>Welcome "+name+", to the game that has no name!</html>");
classWindow();
}
private void buildButtonPanel()
{
// Create a panel for the buttons.
buttonPanel = new JPanel();
// Create the buttons.
b1 = new JButton("Button 1");
b2 = new JButton("Button 2");
b3 = new JButton("Button 3");
b4 = new JButton("Button 4");
b5 = new JButton("Button 5");
// Add the buttons to the button panel.
buttonPanel.add(b1);
buttonPanel.add(b2);
buttonPanel.add(b3);
buttonPanel.add(b4);
buttonPanel.add(b5);
}
private void userName() {
name = JOptionPane.showInputDialog("What will your name be?");
}
I'm not sure why your program is behaving as it seems to be since when I ran it, it did not do this. You may wish to check your code to make sure that it's the code you're posting here. But regardless, I do have some suggestions:
Best to not set the sizes of anything, but rather to let the components and the layout managers do this for you.
Consider if you must overriding getPreferredSize() if you need to control the size of a component more fully.
Call pack() on your top level window after adding all components and before calling setVisible(true). This will tell the layout managers to do their things.
Avoid extending JFrame since you will rarely need to override one of its innate behaviors.
If you do add or remove components, or change their preferredSizes somehow after rendering your top-level window, you will want to call revalidate() and then repaint() on the component's container to have the container re-layout the components it holds and then redraw them.
For example:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridLayout;
import javax.swing.*;
public class TheDungeon2 extends JPanel {
private static final int PREF_W = 750;
private static final int PREF_H = 600;
private static final String[] BUTTON_LABELS = {"Button 1", "Button 2",
"Button 3", "Button 4", "Button 5"};
private static final String WELCOME_TEXT = "Welcome %s to the game that has no name!";
private JLabel welcomeLabel = new JLabel("", SwingConstants.CENTER);
private String name;
public TheDungeon2() {
JPanel buttonPanel = new JPanel(new GridLayout(1, 0, 5, 0));
for (String buttonLabel : BUTTON_LABELS) {
JButton button = new JButton(buttonLabel);
buttonPanel.add(button);
}
setLayout(new BorderLayout());
add(welcomeLabel, BorderLayout.CENTER);
add(buttonPanel, BorderLayout.PAGE_END);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
public void getAndSetName() {
name = JOptionPane.showInputDialog(this, "What will your name be?");
welcomeLabel.setText(String.format(WELCOME_TEXT, name));
}
private static void createAndShowGui() {
TheDungeon2 dungeon2 = new TheDungeon2();
JFrame frame = new JFrame("Nameless Game");
dungeon2.getAndSetName();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(dungeon2);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
I tested your code with an empty classWindow() method and the buttons are correctly placed in south,
for the CENTER issue, you should place something in WEST to have your text centred (even an empty panel) otherwise CENTER will take all the place,
look at this , i added this line :
add(new JButton("a button for test"),BorderLayout.WEST);
and here is the result :