In Swing,
what's the best way to make the JSplitPane to split two jpanels with 50% size each.
It looks like if I don't set preferred sizes on the panels it always makes the first panel almost invisible (2%) and the second one (98%)
Thanks in advance
You should use setDividerLocation(double proportionalLocation) to determine the initial space distribution of the JSplitPane, and then call setResizeWeight(double) with the same value to ensure that the panes are resized in proportion.
Also, be aware: Calling setDividerLocation(double) before the JSplitPane is visible will not work correctly, as the space calculation is based on the Component's current size. Instead you need to involve a nasty hack, such as overriding the JPanel's paint method that contains the JSplitPane:
private boolean painted;
#Override
public void paint(Graphics g) {
super.paint(g);
if (!painted) {
painted = true;
splitPane.setDividerLocation(0.25);
}
}
Use
setResizeWeight(.5d);
[...] A value of 0, the default, indicates the right/bottom component gets all the extra space (the left/top component acts fixed), where as a value of 1 specifies the left/top component gets all the extra space (the right/bottom component acts fixed). [...]
I had a similar problem and I solved it by using a component listener in the parent container and set the divider location on the first resize. Initialise a variable firstResize to true and add this into the parent container constructor:
addComponentListener(new ComponentAdapter(){
#Override
public void componentResized(ComponentEvent e) {
if(firstResize){
splitPane.setDividerLocation(0.5);
firstResize = false;
}
}
});
This should cause the divider to be centred when the parent container size is first set.
The solutions here do not take into account the case where the user moves the divider (i.e. a variable divider location). A complete example that takes this into account is available here:
How do you get JSplitPane to keep the same proportional location if the user has moved the location of the divider
Related
I have some movable objects (JLabels) and the user will reposition them within the parent ChartPanel during runtime. To move the objects I've implemented the (really helpful) ComponentMover class, found here: http://tips4java.wordpress.com/2009/06/14/moving-windows/).
As they are moved, their (x,y) coordinates are displayed back to the user. I'm using a JTabbedPane and the coordinates of two objects are displayed per tab (I tried to post a screenshot but my lack of reputation won't allow it).
The problem is when I change tabs, the objects reposition themselves to the coordinates I had initially set them to. I think this is something to do with revalidate() or repaint()?
I tried to implement a ChangeListener
public static final ChangeListener
relocateObjects = new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
labelPole.setAlignmentX(polePositionX);
labelPole.setAlignmentY(polePositionY);
labelZero.setAlignmentX(zeroPositionX);
labelZero.setAlignmentY(zeroPositionY);
}
};
on the JTabbedPane
tabbedPane.addChangeListener(ComponentMover.relocateObjects);
to relocate the object to coordinates stored just before the event, but to no avail.
I tried to find a way to prevent the objects from listening (and thus responding) to a state change or simply update their default positions to override their initial coordinates, which you'd think would be an option, but I have failed.
I've literally spent all day on this, so I would love a solution or a push in the right direction. Thank you.
The problem is when I change tabs, the objects reposition themselves to the coordinates I had initially set them to.
You need to use a null layout on the panel that contains the components you are dragging:
panel.setLayout( null );
I have some code to resize a chatpanel dynamically, but it does not move according to the mouse. What happens is the mouse moves at a faster rate than the panel gets resized. For example, how I want it to be, is in any application, when you click on the border between two windows, and drag it, the mouse stays along with the piece you are clicking on, and currently this is not happening. here is my code. please let me know if you need more
public void mouseDragged(MouseEvent e) {
if(getCursor().getType() == Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR).getType()) {
owner.adjust((int)MouseInfo.getPointerInfo().getLocation().getY());
System.out.println("vertpanel: "+(int)MouseInfo.getPointerInfo().getLocation().getY());
}
}
public void adjust(int adjustment) {
int height = this.getHeight();
System.out.println((((double)(adjustment))/height)+":"+(((double)(height-adjustment))/height));
output.setHeightPercent((((double)(adjustment))/height));
output.componentResized(new ComponentEvent(this, 1));
input.setHeightPercent((((double)(height-adjustment))/height));
input.componentResized(new ComponentEvent(this, 2));
}
there is one main panel, a chatpanel, and within it, there are two smaller panels, a chat input and a chat output
Can't tell exactly what you are doing based on your code.
I would suggest that you should NOT be manually setting the dimensions of the output and input coponents. You should let the layout manager determine how to resize each component as the parent container is resized.
So in your resize code you would need to invoke revalidate() on the parent container as it is resized.
Check out Resizing Components. You should be able to use the ComponentResizer class as long as you use setAutoLayout(true).
I am writing an application that is a virtual notebook. The idea is to have panels with various content (which is based on an external file) added to a panel that acts as a page. Once that page is full, its specialized add method should return false.
The problem is that I can't figure out how to accurately determine the size of the panels when I'm adding them, so I end up adding too many. The preferredSize of the panels is generally too short, and the size has 0 height. Is there a way to determine the exact size that a component is taking up in a layout?
I've tried using doLayout(), but it doesn't seem to change the size or preferredSize of my components. Maybe I'm not using it right? Here is the add method: (The contentPanel has BoxLayout, and the content panel doesn't have a set size, but is added to a panel (this) that does. The class this method is in extends JPanel)
public boolean addSpecializedgPanel(SpecializedPanel sp) {
this.contentPanel.add(sp);
this.doLayout();
if (this.contentPanel.getSize().height > this.getHeight()) {
this.contentPanel.remove(sp);
return false;
}
return true;
}
Thanks for any help (even if it criticizes my whole design :) )! This has been a huge headache!
I don't think you should deal with the size like that, you should use a layout manager for that. Maybe you should have a look at How to use CardLayout
You want to use Container.validate() on the panel (possibly calling invalidate() first):
public void validate()
Validates this container and all of its subcomponents.
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.
This should cause layout to occur, and, consequently, resizing of the components. You will then need to invalidate() after removing your failing panel, before returning false.
I have a JComboBox which contains an MRU list combo-box and a for a directory tree panel. Together, the two form the left hand panel of my GUI (the MRU is above the tree panel) which is a JSplitPane, so the left panel is resizeable.
It has a problem in that the directory text is always longer than the required width to see that directory in the tree panel, so I have to size my left-hand pane much wider than is needed for the tree in order to stop the combo-box from shown a truncated directory name.
The combo-box is not very useful with the end of the filename truncated, but making the left-pane wide enough for the filename very often makes it obtrusively too wide for the overall window, esp. if not running maximized. And it's usually only the trailing part of the filename which is of interest. If I could only cause JComboBox to somehow show "start...end" instead of "start..." my problem would be solved.
Screen Shot http://www.freeimagehosting.net/uploads/da9810ed86.png
UPDATE: I have a solution which works (see self-answer below), but it's not perfect. If someone knows how I can improve it, that would be much appreciated.
mmHhh perhaps providing a custom renderer?... .
I think it's listcell renderer or something like that.
For what I understand, the default is being wrapped when there is no enough space available, by creating/or modifying the cell rendered you can know what's the component available width and modify the text to be rendered at your will.
mmmhh I have not been close from that API ( the cell render stuff ) for a couple of years, so I could be probably a bit confused.
:)
Working on the idea from Oscar of using a ListCellRenderer, I have come up with something that almost works... The component value is rendered correctly, but the list's values require an ugly hack.
The hack is needed because for the list items the renderer's size (from getSize()) reflects the text width of the longest item, not the width of the space available to render the value. I tried use the JComboBox itself, but its width includes the little drop-down button, so if there's a scrollbar present its width is not accounted for. The hack is to store the renderer's width if it is less than the width of the combo-box and use the stored width if the renderer's width is greater than the width of the combo box. This has a corner case where the renderer's width is between that of the internal JLabel and the width of the combo-box.
Since the rendering space will be the width of the combo-box, less the width of a scroll bar and insets, if anyone has a suggestion as to how I can know the list has a scroll bar and how to get the scrollbar so I can extract the width, I am all ears. Maybe I can do list.getParent() and expect it to be a JScrollPane (either the JComboBox or JList doco does state that it uses a scroll pane).
Other suggestions to do this better are welcome.
Code follows:
recentDirs.setRenderer(new ComboTextRenderer(recentDirs));
...
static private class ComboTextRenderer
extends DefaultListCellRenderer
implements SwingConstants
{
JComponent parent;
int renderWidth;
ComboTextRenderer(JComponent par) {
super();
parent=par;
renderWidth=-1;
}
public void paint(Graphics gc) {
String txt=getText();
int len=txt.length();
int wid=getSize().width;
Insets ins=getInsets();
FontMetrics met=gc.getFontMetrics();
if(renderWidth==-1 || wid<parent.getSize().width) { renderWidth=wid; }
else { wid=renderWidth; }
wid-=(ins.left+ins.right);
if(met.stringWidth(txt)>wid) {
String rpl=null;
for(int xa=0,pfx=Math.min(15,((len/2)-1)),sfx=(pfx+2); pfx>0 && sfx<len; xa++) {
rpl=(txt.substring(0,pfx)+" ... "+txt.substring(sfx));
if(met.stringWidth(rpl)<=wid) { break; }
rpl=null;
if ((len-sfx)>15) { sfx++; }
else if((xa%2)==0 ) { pfx--; }
else { sfx++; }
}
if(rpl!=null) { setText(rpl); }
}
super.paint(gc);
}
}
Because Canvas3D doesn't have the ability to resize dynamically with the parent frame, I would like to be able to track when a user resizes a window and then resize it manually myself. (If this ends up crashing Canvas3D, as some docs suggest, I will simply destroy and recreate it when the user resizes their window). Part of this procedure involves being able to accurately tell how big the container panel is to begin with.
The two methods I've tried:
panel.getHeight();
panel.getPreferredSize().height;
Don't seem to accurately report things: getHeight() is invariably zero, and getPreferredSize() returns numbers that don't actually have anything to do with the actual size of the panel.
Any ideas?
Edit: So, I took a debugger to the panel object and manually inspected the non-object properties and I didn't see anything that resembled width/height. Granted, there are sub-objects that I didn't look at. Also, maybe the window has to be visible (it isn't, at the point I'm interfacing the object) when I query for height/object?
Edit 2: So, Swing classes are subclasses of AWT classes, so I imagine if you're able to find the height/width of those, the approach would generalize. I've amended the title accordingly.
To determine the size of a component you have to either:
have set it manually at some point
run the layout manager responsible for layouting the component
Generally, you get the exact size of a component via the getSize() method, which returns a Dimension object containing width and height, but getWidth/Height() should work too. But this can only work, if one of the two preconditions are met. If a window has never been made visible, has no layout manager or the component (you want to know the size of) has been added after the window/container has been made visible, the size usually is zero.
So to get the correct size, you have to make the container/frame visible (after you have added the component) or call validate() or doLayout() on the container to recalculate the layout, if you added the component after the last layout was done. Another thing to keep in mind is setting and probably configuring a layout manager on the container. If no layout manager ist set (null), even making a container visible oder calling validate() does not set a size on its children.
The minimumSize/preferredSize/maximumSize properties are hints to the layout manager, how the component should be sized, but it does not have to obey them (most layout managers don't).
Edit 2: After I read your other question about the same subject, I think you should read Using Layout Managers from The Java Tutorials
Edit: I don't know if you already figured that out, but to react to the resizing of the window, you can do something like this:
public class WindowResizeTest extends JFrame {
public static void main(String[] args) {
new WindowResizeTest();
}
public WindowResizeTest() {
this.setSize(640, 480);
JPanel panel = new JPanel();
panel.setBackground(Color.RED);
this.add(panel);
this.addComponentListener(new ComponentListener() {
public void componentResized(ComponentEvent e) {
System.out.println(e.getComponent().getSize());
}
public void componentHidden(ComponentEvent e) {}
public void componentMoved(ComponentEvent e) {}
public void componentShown(ComponentEvent e) {}
});
this.setVisible(true);
}
}
I found out that if you extend by JFrame, this code can be used also to save time, effort and space.
int windowWidth = getWidth();
int windowHeight = getHeight();
I know you already got an answer but if you ever need an alternative, here it is.