Problem resizing JScrollPane and JTabbedPane - java

I have a problem with resizing some of my components in my java application. When I resize my main JFrame they keep their previous size instead of filling out the surrounding component.
For example I have in my program a JTabbedPane within a tab of another JTabbedPane (which is located within the main JFrame). I make tabs added to the inner JTabbedPane fill the whole surrounding space via various .setPreferredSize(). And then when I resize the JFrame by dragging the corner of the window, the tabs out the outer JTabbedPane resizes just fine but the JTabbedPane located within it stays the same old size.
The same thing with happens when I test it with a JScrollPane: the outer JTabbedPane resizes by the JScrollPane stays the same size.
Here below is the code for creating the tabbed panels in case you can see something obvious I missed. It's the tabPane/jsp objects in createEndGamePane() than wont resize when the JFrame is resized. mainTabPane resizes as it should.
class MyFrame extends JFrame {
private JTabbedPane mainTabPane;
private JPanel endGameInfo;
//....
private void createTabbedCenterPane(){
this.mainTabPane = new JTabbedPane();
this.endGameInfo = new JPanel();
this.createEndGamePane();
this.mainTabPane.addTab("Turneringschema", null, this.scheduleHolder, "Spelschemat för turneringen");
this.mainTabPane.addTab("Grupper & Pooler", null, this.poolInfo, "Information om grupper, lag och pooler");
this.mainTabPane.addTab("Slutspelsträd", null, this.endGameInfo, "Information om slutspelen");
this.add(this.mainTabPane, BorderLayout.CENTER);
}
private void createEndGamePane(){
JTabbedPane tabPane = new JTabbedPane();
tabPane.setPreferredSize(this.endGameInfo.getSize());
for (int i = 0; i < this.tournament.data.getGroups().size(); i++) {
EndGame e = this.tournament.data.getEndGames().get(i);;
//Create the gameTree
EndGameTreeCanvas gameTree = new EndGameTreeCanvas(e, this.endGameInfo.getSize());
//Create the ScrollPane
JScrollPane jsp = new JScrollPane(gameTree);
jsp.setPreferredSize(tabPane.getSize());
jsp.setBorder(BorderFactory.createEmptyBorder());
//Add to the endGameIndo panel
tabPane.addTab(this.tournament.data.getGroups().get(i).getName(), jsp);
}
this.endGameInfo.add(tabPane);
}
}

If you want the components to resize dynamically, you should avoid telling Swing what their size is. Instead, how about this:
JTabbedPane tabs = new JTabbedPane();
JScrollPane inner1 = new JScrollPane(myOtherComponent);
tabs.add("Scroll", inner1);
JTabbedPane inner2 = new JTabbedPane();
tabs.add("Tabs", inner2);
And that's all. No setPreferredSize().

You should read the swing layout tutorial :
http://java.sun.com/docs/books/tutorial/uiswing/layout/index.html
The layout manager i prefer is the gridbaglayout that allow maximum flexibility
( http://java.sun.com/docs/books/tutorial/uiswing/layout/gridbag.html ).
In particular, you should not use the BorderLayout.CENTER if you wish the component to fill all the available space around him.
an example with a gridbaglayout manager :
this.add(this.mainTabPane,
new GridBagConstraints(
0,0, // position
1,1, // size
1.0,1.0, // fill ratio
GridBagConstraints.CENTER, GridBagConstraints.BOTH, // position inside the cell
new Insets(2,2,2,2),0,0)); // margins

As others have said, you need to be using layout managers to handle your GUI's layout requirements.
However I recommend that your primary layout manager be a table-based layout like MatrixLayout, MigLayout, TableLayout, or any of a number of other free ones.
Using a table-based layout will simplify your GUI coding by about an order of magnitude, because it will vastly reduce the amount of layout nesting required. I find that 90% of my GUI is done with one layout manager, and the remaining 10% draws on the simpler layout managers provided with Java.
I have never had cause to use GridBagLayout and I absolute do not recommend it - it's unnecessarily complicated and make ongoing maintenance harder.

Related

Set height but not width of JTextArea

I have multiple JTextAreas inside a JPanel. I am using a BoxLayout to make them align vertically and fill the width of the container.
It works, but they seem to expand to fill the entire height as well.
What I really want is simple - a text area that wraps text where I can control the width but allow the height to scale dynamically as more lines are added. The above method was just my best attempt at it. If there is a solution that uses a different layout manager, different text component, etc, that works.
minimal verifiable example below:
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setPreferredSize(new Dimension(300, 300));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel textAreas = new JPanel();
textAreas.setLayout(new BoxLayout(textAreas, BoxLayout.Y_AXIS));
JTextArea area1 = new JTextArea();
area1.append("this is a string");
area1.setLineWrap(true);
area1.setWrapStyleWord(true);
textAreas.add(area1);
JTextArea area2 = new JTextArea("and another that is much longer, so that it wraps to the next line");
area2.setLineWrap(true);
area2.setWrapStyleWord(true);
textAreas.add(area2);
JScrollPane scrollPane = new JScrollPane();
scrollPane.setViewportView(textAreas);
frame.add(scrollPane);
frame.pack();
frame.setVisible(true);
}
I have done research on this topic on my own, including looking at different layout managers (http://docs.oracle.com/javase/tutorial/uiswing/layout/visual.html), and checking other questions on the site, but I haven't had much luck.
TLDR: Can I make it so each element of a layout has a height that scales to its content but a fixed width? If so how?
What I really want is simple - a text area that wraps text where I can control the width but allow the height to scale dynamically as more lines are added.
The BoxLayout respects the maximum size so the text area grows to fill all the space available in the panel. You can override the getMaximumSize() method to return the preferred height by using something like:
JTextArea area1 = new JTextArea()
{
public Dimension getMaximumSize()
{
Dimension d = super.getMaximumSize();
d.height = getPreferredSize().height;
return d;
}
};
It works...
Not really. Make the frame wider and the text will unwrap. Then shrink the frame and the scrollbar will appear. That is the text will not wrap again
What you need to do is force the panel added to the scroll pane to be the same width as the viewport. This will allow wrapping to work properly.
You do this by implementing the Scrollable interface on the panel. Specifically you need to override the getScrollableTracksViewportWidth() method to return true.
Or an easier solution is to use the Scrollable Panel class which allows you to set properties of the panel to control this behaviour.
You can replace a JPanel with the ScrollablePanel:
//JPanel textAreas = new JPanel();
ScrollablePanel textAreas = new ScrollablePanel();
textAreas.setScrollableWidth( ScrollablePanel.ScrollableSizeHint.FIT );
Edit:
If there is a solution that uses a different layout manager
Without overriding the getMaximumSize() method of the text areas and when using the Scrollable Panel you should be able to use the following layout managers.
The GridBagLayout allows you to specify the "weightx" constraint. This will allow the component to fill all the available space in the panel.
Or if you don't like specifying all the constrains of the GridBagLayout you could use the Relative Layout which support vertical/horizontal layout of components at their preferred size.
You would just need to use the following to force the component to fill the horizontal space:
//textAreas.setLayout(new BoxLayout(textAreas, BoxLayout.Y_AXIS));
RelativeLayout rl = new RelativeLayout(RelativeLayout.Y_AXIS);
rl.setFill( true );
textAreas.setLayout(rl);

Resizing GridLayout area

I have a 4x4 gridlayout and I'm trying to add 2 title bars above the tables I've created. I'm creating panels for title bars but because of the gridlayout it fills up all the space. Here's the code:
private WebScrollPane createPosRiskPanelWindow(int w, int h)
{
//Content area
posRiskPanel = new WebPanel((new GridLayout(2,2)));
posRiskPanel.setBackground(Color.WHITE);
//Add title bars
WebPanel posTitle = new WebPanel(new WebLabel("Current Positions"));
posRiskPanel.add(posTitle);
WebPanel riskTitle = new WebPanel(new WebLabel("Risk Exposure"));
posRiskPanel.add(riskTitle);
//Panels
CreatePositionPanel(posRiskPanel);
CreateRiskParamsPanel(posRiskPanel);
//Scroll content
posRiskScroll = new WebScrollPane (posRiskPanel, false);
posRiskScroll.setPreferredSize (new Dimension (w, h));
return posRiskScroll;
}
And here's what it looks like:
Is there a way I can resize those top 2 boxes?
Don't use GridLayout for the overall layout, since the only thing it knows to do is to create cells that are all exactly the same size. Since you don't want this, your GUI will require a different layout. Instead use GridBagLayout or else you could use nested JPanels with differing layouts, but I think that GridBagLayout for the overall layout is likely your best bet here. Other considerations include MigLayout if you're willing to use a 3rd party library.
I found it easier to just put the tables in their own panel and a titled border like so:

java swing multiple jpanels

Basically all I want to do is draw two rows of buttons for a lights out game (homework), but I don't know how to make both panels show up. I've pretty much almost no graphics before, and I don't really understand anything I'm doing.
The panels them selves work, but it just shows whichever I add second (I assume it's overwriting the previous panel)
public static void main(String[] args) {
String nButtonsString = JOptionPane.showInputDialog("How many buttons would you like?");
int nButtons = Integer.parseInt(nButtonsString);
JFrame myFrame = new JFrame();
myFrame.setTitle("Linear Lights Out Game.");
myFrame.setSize(FRAME_SIZE);
JPanel control_buttons = new Linear_Controls();
myFrame.add(control_buttons);
JPanel lights = new LinearLightsPanel(nButtons);
myFrame.add(lights);
myFrame.pack();
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.setVisible(true);
myFrame.add(control_buttons);
myFrame.add(lights);
By default a JFrame uses a BorderLayout. Also, by default components get added to the CENTER of the BorderLayout. However the CENTER can only contain a single component so only the last component added is displayed. Try:
myFrame.add(control_buttons, BorderLayout.NORTH);
Now the two components should show up.
Read the section from the Swing tutorial on Using Layout Managers for more information and examples. Also take a look at the Trail/Table of Contents link to see other useful topics for basic Swing usage.
You have to create the 2 panels A and B and add them to another panel C. Then you add C to your frame.
A bit better explained: This is what you have now:
JPanel lights = new LinearLightsPanel(nButtons);
myFrame.add(lights);
But you would like something like:
JPanel lightsA = new LinearLightsPanel(nButtonsA);
JPanel lightsB = new LinearLightsPanel(nButtonsB);
JPanel lightsC = new JPanel();
lightsC.add(lightsA);
lightsC.add(lightsB);
myFrame.add(lightsC);

Adding JScrollPane to my JList

I am having issues getting a vertical scrollbar on my jlist.
listInfo is a vector with data in it.
right panel is a panel on the EAST side of the frame.
If I add just the JList, it appears. If I add scrollpane, nothing happens.
data = new JList(listInfor);
data.setVisible(true);
data.setVisibleRowCount(5);
data.setPreferredSize(new Dimension(400,400));
data.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
JScrollPane scroll = new JScrollPane(data);
rightPanel.add(scroll);
For some reason, this isn't working.
Don't set the preferred size of the JList, this is preventing it from calculating the size of the list based on it's contents, which will mean that it will never exceed the preferred size you have supplied, meaning that it will never show any scroll bars.
Instead, use a combination of JList#setVisibleRowCount and Jlist#setPrototypeCellValue to adjust how the JList calculates it's preferred size.
IF you REALLY want more control, you should take a look at the Scrollable interface that JList implements, but even then, I'd be careful
Tested this
public static void main(String[] args) {
String[] ar = {"one", "two", "three"};
JList data = new JList(ar);
data.setVisible(true);
data.setVisibleRowCount(5);
data.setPreferredSize(new Dimension(400,400));
data.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
JScrollPane scroll = new JScrollPane(data);
JFrame frame = new JFrame("StackOverflow Test");
frame.add(scroll);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null); // to center
frame.setVisible(true);
}
and all it's okay
You should specify a layout manager for your rightPanel variable, if it hasn't any. Absence of a layout manager may give you unexpected results, as you don't set the bounds of your components manually.
Besides, as pointed out by MadProgrammer's answer, you shouldn't set preferred sizes on your components, if possible. There are other ways to determine appealing dimensions. For instance, with JList, using setVisibleRowCount.
In order for the scroll to appear only as needed, you can specify the scroll bar policies on the JScrollPane constructor, or use its set methods to do so. Example:
jlist.setVisibleRowCount(5);
JScrollPane scroll = new JScrollPane(jlist);
scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
scroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
Also, make sure the container of the scroll pane respects its preferred size, as the scroll pane respects the list's preferred size.

Placing JTextFields in a JFrame of a Java GUI

I have:
public class BaseStationFrame1 extends JFrame
{
JButton activateButton;
JButton deactivateButton;
BaseStation bs;
JTextField networkIdField;
JTextField portField;
public BaseStationFrame1(BaseStation _bs){
bs = _bs;
setTitle("Base Station");
setSize(600,500);
setLocation(100,200);
setVisible(true);
activateButton = new JButton("Activate");
deactivateButton = new JButton("Deactivate");
Container content = this.getContentPane();
content.setBackground(Color.white);
content.setLayout(new FlowLayout());
content.add(activateButton);
content.add(deactivateButton);
networkIdField = new JTextField("networkId : "+ bs.getNetworkId());
networkIdField.setEditable(false);
content.add(networkIdField);
portField = new JTextField("portId : "+ bs.getPort());
portField.setEditable(false);
content.add(portField);}
}
My problem is that i don't want the two TextFields to appear on the right of Activate and Deactivate buttons but below them. How can i fix that?
Specify your layout manager, like this:
content.setLayout(new GridLayout(2,2));
That would use the Grid Layout Manager to establish a grid with 2 columns and 2 rows, that your components would then be placed in.
The layout manager you are currently using, FlowLayout, only adds contents onto the end of the current row. it will wrap around once it reaches the constrained edge of the pane, though.
You should also check the other layout managers here
You could alternatively use GridBagLayout , but you will have to specify a GridBagConstraints object you then add alongside the individual elements, like so:
content.add(networkIdField, gridConstraints);
see more on that in the linked tutorial.
can I suggest that you use a Null Layout for the parent component?
setLayout(null);
then use a setBounds(xPos,yPos, Width, Height);
to position the components on the panel etc?
Doing this will prevent Java's UI Manager to manage the components to the Frame, Panel etc.
That seems to be the easiest and less painful way.
Regards

Categories