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.
Related
I have a window, containing a frame and JScrollBar. JScrollBar has panel, whose size i want to change at run time if any child component is added with it. Child component size is constant and should not change.
I have Horizontal scrollBar disabled. So if placement of Child Component exceeds panel width, it should go to next row and panel height should change automatically.
Here is code snippet
JScrollPane scrollPane = new JScrollPane();
menuPane = new JPanel();
menuPane.setLayout(new FlowLayout(FlowLayout.CENTER));
scrollPane.setViewportView(menuPane);
MenuTray mtFile = new MenuTray("File"); // MenuTray extends JPanel
menuPane.add(mtFile);
Menu mNew = new Menu((new ImageIcon(MenuScreen.class.getResource("/com/srinar/res/New.png"))), "New"); // Menu extends JLabel
mtFile.add(mNew);
frame.getContentPane().add(scrollPane);
. So if placement of Child Component exceeds panel width, it should go to next row and panel height should change automatically
The FlowLayout will wrap components automatically but it doesn't recalculate the preferred size with the components on the new row.
Instead you can use the Wrap Layout which extends FlowLayout so it can recalculate the preferred size correctly when components wrap.
Note, the wrapping will occur even if you don't use a scroll pane.
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);
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
I'm trying to create a very simple window using Java Layouts. I have got three elements to arrange: a button, a progress bar and a label. The button has to be vertically centered, the progress bar has to take full width, and the label has to be left aligned.
Here's some code (just assume pane is the content pane of a JFrame, and button, progressBar and label have been created before):
BoxLayout layout = new BoxLayout(pane, BoxLayout.Y_AXIS);
pane.setLayout(layout);
button.setAlignmentX(Component.CENTER_ALIGNMENT);
pane.add(button);
progressBar.setAlignmentX(Component.CENTER_ALIGNMENT);
pane.add(progressBar);
label.setAlignmentX(Component.LEFT_ALIGNMENT);
pane.add(label);
When I test the application I see everything misaligned and screwed up: the button and the label are randomly indented, and if I resize the window the indentation amount changes in a strange way.
The progress bar looks good (full width).
I just don't understand what's happening. Can you give me a clue?
BoxLayout cannot handle different alignments: see http://download.oracle.com/javase/tutorial/uiswing/layout/box.html
quoting from that article: "In general, all the components controlled by a top-to-bottom BoxLayout object should have the same X alignment. Similarly, all the components controlled by a left-to-right Boxlayout should generally have the same Y alignment."
Sometimes you need to get a little creative and use nested panels. But I like this approach better then trying to learn and memorize all the constraints required when using other layout managers (GridBagLayout, GroupLayout) there where designed to be used by IDE's that generate code for you.
import java.awt.*;
import javax.swing.*;
public class BoxLayoutVertical extends JFrame
{
public BoxLayoutVertical()
{
Box box = Box.createVerticalBox();
JButton button = new JButton("A button");
button.setAlignmentX(Component.CENTER_ALIGNMENT);
box.add(button);
JProgressBar progressBar = new JProgressBar(0, 100);
progressBar.setAlignmentX(Component.CENTER_ALIGNMENT);
box.add(progressBar);
JPanel panel = new JPanel( new BorderLayout() );
JLabel label = new JLabel("A label");
label.setAlignmentX(Component.LEFT_ALIGNMENT);
panel.add(label);
box.add(panel);
add(box, BorderLayout.NORTH);
}
public static void main(String[] args)
{
BoxLayoutVertical frame = new BoxLayoutVertical();
frame.setDefaultCloseOperation( EXIT_ON_CLOSE );
frame.setSize(300, 200);
frame.setLocationRelativeTo( null );
frame.setVisible(true);
}
}
To complement my comment to the original question, here is a snippet that uses DesignGridLayout:
JButton button = new JButton("Button");
JProgressBar progressBar = new JProgressBar();
JLabel label = new JLabel("Label");
// The interesting stuff is in the next 4 lines
DesignGridLayout layout = new DesignGridLayout(getContentPane());
layout.row().center().add(button).withOwnRowWidth();
layout.row().center().fill().add(progressBar);
layout.row().left().add(label);
pack();
It does exactly what wou describe in your question and doesn't require any specific call of any of the components.
Maybe your code is just a snippet, but I'm missing a call to pack().
Coding swing layout by hand can be very frustrating with the standard Layout managers. I use MiG Layout for that purpose. It is straight forward and you have a nice layout with just a few lines of code. If you're not forced to use BoxLayout I would suggest you give it a try.
Don't use BoxLayout. It works only for very simple cases.
For your case, I would recommend either GridBagLayout or (my favorite) GroupLayout.
For GroupLayout, I created a subclass (LayoutHelper) with some utility methods and useful constructors, which makes writing the Layout much easier.
Of course, usually I align all components in a group the same way, so it is not as short in your case as it would be in the simple case.
LayoutHelper h = new LayoutHelper(pane);
h.setVerticalGroup
( h.sequential( button, progressBar, label));
h.setHorizontalGroup
( ((ParallelGroup)h.parallel())
.addComponent(button, Alignment.CENTER)
.addComponent(progressBar)
.addComponent(label, Alignment.TRAILING));
Here is a screenshot:
For a simple "everything aligned the same way", the horizontal group would look like this:
h.setHorizontalGroup
( h.parallel (button, progressBar, label));
(optionally with a first argument indicating the alignment).
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.