I have a JTextArea wrapped in a JScrollPane, which I use to log my application's output. I'm using the default, plain font with a size of 9 for the text area, and the scroll pane's height is 48 px. This results in an even distribution of lines in the scroll pane view, but there's a problem: if you scroll all the way up or all the way down, this happens:
As you can see, the top line got cut off, which is why I'm wondering if there's a way to limit the scroll pane's scroll range so it, for example, can't reach the top or bottom 6 pixels. Alternative solutions are also welcome.
You could change the margin (top/bottom) of your JTextArea by setting a custom Border using the method setBorder inherited from JComponent. The documentation for JComponent suggests the following:
Although technically you can set the border on any object that
inherits from JComponent, the look and feel implementation of many
standard Swing components doesn't work well with user-set borders. In
general, when you want to set a border on a standard Swing component
other than JPanel or JLabel, we recommend that you put the component
in a JPanel and set the border on the JPanel.
That would yield the same result as limiting the scroll range, while being more straight forward.
EDIT:
OP reported that the following solution worked for him:
textAreaLog.setBorder(BorderFactory.createEmptyBorder(0, 6, 0, 6));
Place the JTextArea in a JPanel with empty borders where top and bottom insets are 6 pixels?
Related
I want to add JLabels to JFrame directly. I don't want use JPanel. But I have a position problem. I set layout as null.
I tryed draw line to see what's going on.
#Override
public void paint(Graphics g){
g.setColor(Color.red);
g.drawLine(0, 31, super.getWidth(), 31);
}
And the zero is actually 31.
Drawing screenshot
Why? And how can I fix that?
I want to add JLabels to JFrame directly. I don't want use JPanel.
If you're adding the JLabel "to the JFrame" then you're adding it to the contentPane which is almost always a JPanel, so 99% of the time, you're still using a JPanel, even without trying to.
But I have a position problem. I set layout as null.
Which is almost always a bad thing to do. This makes for GUI's that don't work on all platforms, fighting against the Java philosophy and structure.
And the zero is actually 31.
Why? And how can I fix that?
Because of the top part of the JFrame is taken up by the OS window's menu bar. The contentPane, starts 31 pixels below the top of the JFrame (in your case -- different for different OS's and screen resolutions).
Best to avoid drawing directly on the JFrame, which is actually composed of many sub-components -- the content pane, the root pane, the glass pane,... and instead draw within the paintComponent method of a JPanel that you either add to the contentPane or make as the contentPane. Then 0,0 is the top left of the usable portion of your main window.
Also, please elaborate more on the underlying reason why you're trying to avoid use of a JPanel. Your issue may in fact be an XY Problem type issue.
Positions in a JFrame are relative to the edge of the window, not the content pane. To get the dimensions of the content pane, use getContentPane().getWidth() and getContentPane().getHeight().
I want to make a JFrame with multiple components above the other - it should look like this:
Everything centered
GridLayout scrollable (if x is a huge number)
What Layout should I use? How do I keep it as minimal as possible?
Thanks in advance!
You don't really give enough information, but what you give looks like BorderLayout to me -- image in the NORTH section, GridLayout in the CENTER, and a panel with your Label, TextField, and Button in the SOUTH. The CENTER will shrink and grow with the size of the frame. The bottom panel appears to have a BoxLayout with y-axis, and you can set x-axis centering on each component.
This doesn't seem like it should be very hard but I can't figure out how to do this:
I have a subclass of JPanel. It has a fixed height, but can be any width. The subclasses, on construction, set their preferred size using setPreferredSize(), which means I have to provide a width in addition to the height.
I would like to make a scrolling list of some number of my subclass, where the subclasses all fill the available horizontal space.
Right now I have a scroll view containing a JPanel containing my subclasses. The containing JPanel uses a BoxLayout with a vertical orientation.
Vertically, it looks great. Horizontally, my custom panels are just stuck at the preferred size. What would be the easiest way to make my panels fill the available horizontal space? I tried writing some layout listeners for them, but the performance was flakey (it seems that sometimes the event messages get dropped?) and the code looked hacky. Other views, such as the JList, JTree, and scroll views seem to resize automatically to fill the available space in a BoxLayout, so I feel like there must be something I can do in my JPanel subclass that I haven't thought of.
I'm open to using another Layout Manager if something else is better suited for this. I looked at GridBagLayout, but that seemed more geared towards static layouts where components aren't added and removed at runtime.
Edit: I found this on Oracle's Documentation for BoxLayout which has an example that looks exactly like what I want:
What if none of the components has a maximum width? In this case, if all the components have identical X alignment, then all components are made as wide as their container. If the X alignments are different, then any component with an X alignment of 0.0 (left) or 1.0 (right) will be smaller. All components with an intermediate X alignment (such as center) will be as wide as their container. Here are two examples:
Could someone show me the code that will produce the same results? The example code in the documentation doesn't look like it covers this particular picture.
When you put your component in a JScrollPane, your component may implement the Scrollable interface to adjust the scrollpane’s behaviour. By doing this you can implement the method getScrollableTracksViewportWidth() to return true so your component will always have the available width and be scrollable in vertical direction only.
This is how JList, JTable, JTree, and all the text components of Swing do it.
Alright - I found the problem. It appears that the default implementation of getMinimumSize() and getMaximumSize() will simply return the value of getPreferredSize() if it is set. So by setting a preferred size without a maximum size, my maximum size was the preferred size. By overriding getMaximumSize() to return (99999, preferredHeight), it now works exactly as I want.
I have a JPanel which I'm placing inside of a JScrollPane. I am manually painting it using paintComponent(), since it's being used as a canvas, and I want the panel to automatically fit to the width of the scroll pane. I can then use getWidth() in the painting code to automatically scale to fit the container. However, I want to be able to manually set the preferred height so the I make use of the scroll pane's vertical scrolling capabilities.
What's the best way to do this? This would obviously still work if I was just able to get the width of the scroll pane in the painting code, but I don't want to break encapsulation too much with hacky code like getParent().
Let your panel implement Scrollable: the getScrollableTracksViewportWidth/Height control how the viewport handles the sizing in the horizontal/vertical dimension, respectively. A value of true indicates that the component is forced to the same size as viewport (that is, no scrolling in that direction), so getWidth() can be consistently used for scaling.
I would get your paint code to use the horiz width of itself as the basis for painting.
Then set the scroll pane so it never scrolls horizontally, only vertically.
Is this good enough ? Seems too simple now I've typed it out !
Is it possible to tell JPanel to set its size to fit all components that it contains? Something like pack() for JFrame.
edit: The trick with preferredSize didn't help. I've got JSplitPane, where in one part there is GridBagLayout with many labels (see screenshot) and labels overlap each other.
screenshot http://foto.darth.cz/pictures/screen.png
After looking at the source code for pack(), I came up with:
panel.setPreferredSize(panel.getPreferredSize());
This forces the panel to recalculate its preferred size based on the preferred sizes of its subcomponenents.
You may or may not have to call validate() afterward; in my tiny example, it seemed to make no difference, but the Javadoc says:
The validate method is used to cause a container to lay out its subcomponents again. It should be invoked when this container's subcomponents are modified (added to or removed from the container, or layout-related information changed) after the container has been displayed.
So I guess it depends on why you're having to repack your JPanel.
By default Containers have a preferred size that matches the preferred layout size given by the container. So literally all you have to do is:
panel.setSize(panel.getPreferredSize());
Presumably you are doing something odd with the parent to stop the parent's layout manager doing the equivalent of this.
maybe you can do something like that by removing from your panel
setResizable(false);
I would try:
panel.revalidate();
panel.repaint();
This will not necessarily set the panel to its preferred size, that is more dependent on what the layout manager decides to use.
This is useful in cases where you have added/removed components from a panel that is currently displayed and visible.
Update:
Based on your screenshot I can say the following:
1) Consider programatically changing the divider location.
2) Consider programatically resizing the window itself horizontally since it seems to be a little tight to show both sides of the split pane.
Or both.
You can set the divider location by doing
splitPane.setDividerLocation(newSize);
Keep in mind that there are two overloaded methods for this, one taking a float one taking an int. The float does a percentage of the size while the int is the size in pixels. The size is for the left hand panel (or top panel for that orientation).
I would consider possibly changing the divider location based on the preferred width of the panels.
The javax.swing mysteries reveal themselves only gradually, and only to those who are prepared to offer many libations (particularly torn out clumps of hair, hours burning the midnight oil, etc.) to the gods of Swing.
However, for this case in point I would suggest the following as a sort of Swiss army knife which usually does what you think the framework should do anyway:
myJPanel.getTopLevelAncestor().validate()
As the sacred text says, "Validates this container and all of its subcomponents." (Container.validate). NB getTopLevelAncestor() is a JComponent method.
Can't remember how JSplitPane fits into this: try it and you'll probably find that it validates both components (right and left, top and bottom), but I would be surprised if changing the divider doesn't do this for you anyway.
I had a similar issue using Netbeans GUI Builder. My inner panels were getting weird sizes; I was trying to adjust the minimum and preferred sizes manually, which was a frustrating exercise.
The problem was solved when I reset all the minimum and preferred sizes back to default (In Netbeans GUI Builder: right click JPanel component -> Properties -> preferredSize -> Reset to Default). When there is no imposed size, the jpanel takes the size of the inner component.
Note: GridBaLayout was used in my case
JSplitPanes are a bit fussy when it comes to its children's sizes, have a look at the Java tutorial. Are you using the GridBagLayout correctly? Looks like it's not setting the right JPanel's minimum size properly.
Here's an example of a panel which:
Resizes with it's parent.
Sets the width to the width of the parent.
Sets the height according to sum of the height of all of it's children.
JPanel panel = JPanel(new GridBagLayout())
panel.setMaximumSize(new Dimension(panel.getMaximumSize().width, panel.getPreferredSize().height))
panel.validate()
panel.repaint()