I am having quite a problem in regards to a proper way to handle 'packing' a JPanel and allowing a scroll bar.
I left out a bit of the code, but I believe what is provided should suffice.
The issue, is that the JScrollBar either doesn't show up, or can't be interacted with (if I set the scroll bar to always have the vertical bar).
Here is an image depicting this:
![No Scroll Bar Present][1]
The ProjectPanel (extends JPanel) are of fixed size and, as you can see, extend farther than the visible view port. There is NO way of getting the calculated height of the JPanel (ProjectSelector), as the ProjectPanels can also be transitioned as so:
![They need to fill as a grid][2]
If anyone could help provide some insight on how to do this, that would be great. As of now, I would like at all costs not to use an external API, as that would cause more harm than good.
Question:
How can I set the height for the preferred size to be 'flexible', so as I add components it can expand? If that wouldn't be ideal / no possible, how could I properly allow the JScrollPane to show all components of the JPanel efficiently?
if I do not, then the Flow Layout organizes them horizontally
Maybe you should be using a GridLayout.
If not then you can try the Wrap Layout which is a FlowLayout that wraps to a new line as required.
If you are looking for a different layout, why don't you try MiGLayout? It is a very powerful and flexible layout manager.
See a detailed example here.
From that link, rewrite the initUI method to see the behavoir interesting to your problem:
public void initUI() {
this.setPreferredSize(new Dimension(100, 100));
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setJMenuBar(initMenuBar());
this.getContentPane().add(new JScrollPane(mainPanel = initMainPanel()));
this.setLocationByPlatform(true);
this.pack();
}
Related
I am trying to create a JScrollPane that contains a JPanel that will be increasing and decreasing in height. When it becomes larger than the size of the JScrollPane, it should create a vertical scroll bar which will allow me to scroll through the entire JPanel. However, I am having difficulty achieving this. Yes, I know I am not using LayoutManagers. No, I will not be using them, and I need a solution that does not involve their usage.
Here are the two button's AbstractActions that add and subtract from the JPanel:
class AddACT extends AbstractAction
{
public void actionPerformed(ActionEvent e)
{
info.setSize(420,info.getHeight() + 40);
info.add(new SubPanel); // Adds another JPanel into the main JPanel (for content input)
gui.repaint();
infoS.validate();
}
}
class RemoveACT extends AbstractAction
{
public void actionPerformed(ActionEvent e)
{
info.remove(subPanel()); // This would remove the last JPanel added to the main JPanel
info.setSize(420,info.getHeight() - 40);
gui.repaint();
infoS.validate();
}
And here is the code for the main JPanel and the JScrollPane:
final JPanel info = new JPanel();
final JScrollPane infoS = new JScrollPane(info, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
info.setLayout(null);
info.setSize(420,600);
infoS.setLocation(10,80);
infoS.setSize(420,490);
gui.add(infoS); // gui is the frame's content pane (the overall JPanel)
This is the second project I've been trying to learn GUI by doing. I am a complete novice in Swing and am only intermediate in Java. Sorry if I am making a blindingly obvious mistake.
1) Use LayoutManagers (+1 to #kleopatra and #GagandeepBali comments)
The absence of LayoutManagers only guarantees your GUI's will look very trashy (especially when run on other OSes/builds) and being a Novice you should rather learn the correct way than learn the wrong way and get into bad habits like calling setSize() etc.
Have a read on these links to get you started:
A Visual Guide to Layout Managers
Concurrency in Swing
2) See this example for how to use a JScrollPane, it simply adds a JPanel with buttons to a JScrollPane which in-turn is added to the JFrame.
3) Also see this example for how to make the JScrollPane vertically scroll-able only.
4) For more on JScrollPanes have a look here: How to Use Scroll Panes.
5) As for how it interacts with LayoutManager, if you do not explicitly set its size via setPreferredSize(Dimension d) the scroll pane computes it based on the preferred size of its nine components (the viewport, and, if present, the two scroll bars, the row and column headers, and the four corners)
6) On your usage of validate():
validate() is used when new JComponents are added to a visible component
revalidate() is used when JComponent is removed/added from a visible component
revalidate() covers validate() too
Thus always use this:
//add or remove component(s)
revalidate();
repaint();
References:
http://www.daniweb.com/software-development/java/threads/405568/validate-vs-revalidate
LayoutManager is not required to solve the problem. The problem in Thrfoot's example is in these lines:
final JScrollPane infoS = new JScrollPane(info, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
info.setLayout(null);
info.setSize(420,600);
The program appears to recognize there is a need for scroll bars (it would show the scroll bar if your setting was VERTICAL_SCROLLBAR_AS_NEEDED), but the actual scrolling does not work (the scroll bar slider is not there).
To fix this, first set the preferred size of info, then construct the infoS.
Example:
info.setPreferredSize(420,600);
final JScrollPane infoS = new JScrollPane(info, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
The idea is to set the preferred size of the info panel before it is used for the scroll pane. This is the same reason to set the size and location of infoS before adding to the gui:
infoS.setLocation(10,80);
infoS.setSize(420,490);
gui.add(infoS); // gui is the frame's content pane (the overall JPanel)
I need solution similar to GridLayout but without resizing components in JPanel.
Everything works great with JFrame, but I need to put those components into JPanel instead JFrame.
I've seen two approaches that may suit your requirement:
Nest each component in a JPanel having FlowLayout, which respects the component's preferred size, as shown here.
Use the HORIZONTAL_WRAP or VERTICAL_WRAP orientation of JList, as shown here.
The following link might help you to choose the most appropriate layout for your needs. Its the Java Tutorial called "A Visual Guide to Layout Manager", which shows nice pictures of each layout and what they look like...
http://docs.oracle.com/javase/tutorial/uiswing/layout/visual.html
Otherwise, if you say GridLayout is similar to what you need, you could always write your own MyGridLayout class (that extends GridLayout) and overwrite the method that does the autoresizing.
I stumbled upon this question myself and even though I think using z JList might be a good solution, there's an even simpler way of doing it with a customized FLowLayout, called WrapLayout, available here: https://tips4java.wordpress.com/2008/11/06/wrap-layout/
I ended up using it in my project and it works very well. The only issue I run into is when I set my window to the full screen mode the layout did not update correctly. I used a simple workaround, which was this:
//int targetWidth = target.getSize().width;
int targetWidth = target.getParent().getSize().width; // FIXME: this is a hack for getting the correct size when switching between full screen modes on Mac
With that small hack it works perfectly.
Just Override preferredLayoutSize() in flowlayout and set Maximum size to it. set Alignment as LEADING and set it to your JPanel. You'll get what you want
private FlowLayout getFlowLayout(int maximumSize)
{
if (flowLayout == null)
{
flowLayout = new FlowLayout()
{
#Override
public Dimension preferredLayoutSize(Container target)
{
Dimension dimension = super.preferredLayoutSize(target);
dimension.width = Math.min(maximumSize, dimension.width);
return dimension;
}
};
flowLayout.setAlignment(FlowLayout.LEADING);
}
return flowLayout;
}
I'm trying to get the Layout of a JDialog of mine to fit a particular look that a program in which I'm porting to Java has, I've used several LayoutManagers before with great success yet for some reason I cannot seem to get this working at all. My goal is to have the Right (East) side of the JDialog contain a "Find Next" and "Cancel" button in a top-down order and then any extra space below so that the two buttons are always at the top of the JDialog, yet for some reason BoxLayout is continously ignoring any attempts at changing (this is where I'm lost) the width of a JButton. Code follows.
JButton findNext = new JButton("Find Next");
JButton cancel = new JButton("Cancel");
cancel.setPreferredSize(new Dimension((int)findNext.getPreferredSize().getWidth(),
(int)cancel.getPreferredSize().getHeight()));
JPanel example = new JPanel();
example.setLayout(new BoxLayout(example, BoxLayout.Y_AXIS));
example.add(findNext);
example.add(cancel);
example.add(Box.createGlue());
No matter what I try, cancel always retains it's normal size. I've tried setMinimumSize() and setMaximumSize() with the same parameters as setPreferredSize with no luck. I've even tried cancel.setPreferredSize(new Dimension(500, 500)); and the buttons height was the only thing adjusted, it STILL retained the default width it was given.
To clear up any questions, here is what it looks like (now that I've finished it) and you'll see that the "Find Next" and "Cancel" buttons are not the same size.
I know this is an old question but I don't really see a good explanation. So for the sake of searchers that stumble upon this I will add my two cents.
There are three methods associated with sizing components in Swing: setPreferredSize(), setMinimumSize(), and setMaximumSize(). However, the important point is that it is up to the particular layout manager being used as to whether or not it honors any of these methods.
For BoxLayout (the layout the original poster is using):
setMinimumSize() -- BoxLayout honors this
setMaximumSize() -- BoxLayout honors this
setPreferredSize() -- if X_AXIS is being used width is honored, if Y_AXIS is being used height is honored
The OP is using a Y_AXIS BoxLayout which is why only his height was being changed.
Update: I put together a page with this same information for all of the layout managers. Hopefully it can help some searchers out: http://thebadprogrammer.com/swing-layout-manager-sizing/
You may not want Box.createGlue(), which "grows as necessary to absorb any extra space in its container." Instead, use Box.createVerticalStrut() between the buttons, as shown below and in the ControlPanel of this simulation.
example.setLayout(new BoxLayout(example, BoxLayout.Y_AXIS));
example.add(findNext);
Box.createVerticalStrut(10);
example.add(cancel);
Addendum:
adding in setMaximumSize() made it work.
This is the expected behavior for components having identical maximum widths in a vertical BoxLayout, as described in Box Layout Features. The preferred width of the container becomes that of the (equally wide) children, and the X alignment becomes irrelevant.
example.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
JButton findNext = new JButton("Find Next");
JButton cancel = new JButton("Cancel");
Dimension d = findNext.getMaximumSize();
cancel.setMaximumSize(new Dimension(d));
example.add(findNext);
example.add(cancel);
As mentioned in the comments on the question, you were able to fix it by switching to setMaximumSize(). However, as you noted, setPreferredSize() doesn't work. So, what's up with that?
With many things Swing, the properties used to determine the actual component size when using the BoxLayout are somewhat random (in my opinion). When determining how to render the components, Swing calls layoutComponent() on the layout manager, which is figures out where to position everything.
BoxLayout's implementation of layoutComponent() involves a call to a method that creates SizeRequirements objects for the width and height of each of the components you add to the JPanel, based on their getMinimum/Preferred/MaximumSize() methods.
Later, it calls SizeRequirements.calculateAlignedPositions() for determining the correct width values for each component, because your orientation is BoxLayout.Y_AXIS (The heights are calculated using a different method). Taking snippets from the source, the relevant implementation of this method is as follows:
for (int i = 0; i < children.length; i++) {
SizeRequirements req = children[i];
//...
int maxAscent = (int)(req.maximum * alignment);
int maxDescent = req.maximum - maxAscent;
//...
int descent = Math.min(totalDescent, maxDescent);
//...
spans[i] = (int)Math.min((long) ascent + (long)descent, Integer.MAX_VALUE);
}
Note that totalDescent is the available width, so descent is always set to maxDescent, which is based on SizeRequirements.maximum, which was taken from JButton.getMaximumSize(). The value of spans[i] is then used later in a call to JButton.setBounds() as the width. As you'll note, getPreferredSize() was never involved here, which is why setting it has no impact in this case.
Usually if want to ensure a size of the component in Swing you need to call setMinimumSize(), setMaximumSize(), and SetPrefferedSize() with the same value.
button.setMaximumSize(getMaximumSize());
If you put your buttons in a GridLayout panel they will be the same width.
I have a small "popup" like this:
But I don't want the padding around the button, I want it to be as small as it can be with the supplied text.
btn.setMargin(new Insets(1, 1, 1, 1));
panel.add(lbl, "wrap");
panel.add(btn);
frame.pack();
frame.setVisible(true);
At least this doesn't work...
Can this be achieved on MigLayout or should I use some other layout manager for this frame.
What's your MigLayout constraints that it was built with? You might want to try using novisualpadding if you aren't already. Also, the debug layout constraint will draw boxes around your components to show what room they take up. (I think the red dotted lines are the size of the components and the blue dotted lines are the "cells" that the component is in.)
// old version
// JPanel panel = new JPanel(new MigLayout());
// new verion
JPanel panel = new JPanel(new MigLayout("debug, novisualpadding"));
I use MigLayout pretty often and have found the "cheat sheet" pretty useful. http://migcalendar.com/miglayout/cheatsheet.html
A layout manager should not affect the way a component is painted.
The setMargin(...) works fine for me when using the Metal LAF. Maybe your LAF doesn't support the setMargin() method. Or maybe you have some code elsewhere that is overriding the preferred size of the button causing it to paint like this.
However, if the MIG layout turns out to be the problem, you could try the Wrap Layout.
Taking MIke's answer as a startup, I found in the MIG "CheatSheet" a possible solution to your problem.
When you set up a MIGLayout, you can specify the gap between component with an integer between the square brackets defining columns and rows.
E.g.
MigLayout buttonMig = new MigLayout("", "[139px,grow]0[179px,grow]50[142px,grow][143px,grow]", "0[60px:60px:60px,center]");
I've just been having this issue too. I discovered that the following worked for me:
this.add(okButton, "width pref:pref:pref");
or
this.add(okButton, "wmax pref");
Also when using the substance look and feel there is a feature where the minimum size is always too wide. This can be overridden with this obscure hack.
okButton.putClientProperty(
SubstanceLookAndFeel.BUTTON_NO_MIN_SIZE_PROPERTY, Boolean.TRUE);
I have a JScrollpane which contains a scrollable client that changes its size dynamically while using the application. I want the JScrollPane to be aware of the changes without moving the viewport when the client size changes.
To clarify what I mean:
Refer to the Java Webstart example ScrollDemo2 from the article How to use scroll panes by Sun. When clicking at the bottom of the window, a circle appears partly outside the window and the scrollbars move. It's the latter behavior I want to avoid.
My guess is that it's just a matter of setting a simple flag in one of the many components that are involved in a scroll pane solution, but I just can't find where it is. Does anyone know?
I managed to solve this problem by overriding the standard behavior of the viewport in my JScrollPane. This might be a solution that is not suitable for all, but in my GUI this works like a charm.
JScrollPane pane = new JScrollPane();
pane.setViewport(
new JViewport(){
/**
* An empty override implementation to prevent undesired scrolling on
* size changes of the client.
*/
#Override
public void scrollRectToVisible(Rectangle rect){}
});
I would try something like:
Point p = scrollPane.getViewport().getViewportPosition();
revalidate();
scrollPane.getViewport().setViewportPosition(p);
You may need to wrap the last line of code in a SwingUtilities.invokeLater.
If that doesn't work then maybe you can disable/enable the viewport before and after the revalidate()?