Find "real" height/width of Swing/AWT object - java

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.

Related

How to make JInternalFrame(s) in JDesktopPane non-overlapping

I have multiple JInternalFrame(s) inside a JDesktopPane. All the JInternalFrame(s) are undecorated and I manage the dragging using mouse listeners. However, I want the internal frames to be non overlapping, i.e., one internal frame should not intersect with another.
Till now, I can check if two internal frames are overlapping by using getBounds().intersects() method. However, I am clueless as to what should I do next.
I tried Google but got no satisfactory answer. Please suggest a solution.
Thank you !
Two answers:
If you want to set which internal frame overlaps the other, use the setComponentZOrder(Component comp, int index) function.
e.g.
JDesktopPane desktopPane = new JDesktopPane();
contentPane.add(desktopPane);
JInternalFrame internalFrame = new JInternalFrame("New JInternalFrame");
desktopPane.add(internalFrame);
desktopPane.setComponentZOrder(internalFrame, 0);
Place the setComponentZOrder function in the JInternalFrame component listener.
internalFrame.addComponentListener(new ComponentAdapter() {
#Override
public void componentMoved(ComponentEvent e) {
desktopPane.setComponentZOrder(internalFrame, 0);
}
});
2. If you want them to strictly not overlap, place them in separate jframes.

Resize code does not correspond with mouse movement

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).

The contents of my JComponent only refresh after a manual resize

I am trying to figure out why my JComponent refreshes when I manually drag my window, but it doesn't refresh when I call repaint or revalidate. The data is ready to be displayed, but it just won't show until I manually resize. Can anybody give some suggestions about what I can do or does this sound like it isn't a Swing problem since I tried repaint and revalidate?
One weird things I've noticed is that if I have this code:
sp.setSize(sp.getSize().width, sp.getSize().height+1);
sp.setSize(sp.getSize().width, sp.getSize().height-1);
If the first line is used, then the JComponent will refresh itself. If I use none or both of these lines it will not, which seems bizarre to me.
I am basically just putting a JPanel in a JInternalFrame in a JDesktopPane. There are two main functions for what I am trying to do. One adds the new JPanel and the other tries to refresh it so the new data will show:
public void addNewSP()
{
sp = new JInternalFrame("SP");
sp.setClosable(true);
sp.setLocation(700, 400); //this should be changed to something based on screen size
sp.setResizable(true);
sp.add(popUp);
this.parentContainer.add(sp, JLayeredPane.DRAG_LAYER);
sp.pack();
sp.show();
sp.setSize(500, 500);
sp.setPreferredSize(new Dimension(500, 500));
}
public void refreshSP()
{
sp.repaint();
sp.validate();
sp.repaint();
sp.validate();
parentContainer.validate();
parentContainer.repaint();
sp.setSize(sp.getSize().width, sp.getSize().height+1);
sp.setSize(sp.getSize().width, sp.getSize().height-1);
}
}
BTW parentContainer is the JDesktopPane
When changing the container's content, you have to call both:
revalidate() to make it recompute the layout for its content
repaint() to request a repaint for this container
but it just won't show until I manually resize.
We don't know the context of your question, which is why a SSCCE should always be posted as suggested earlier.
In general a JComponent, does not have a preferred size, so I'm guessing Swing doesn't think it needs to paint the component. When you resize the frame, chances are the component was added to the center of a BorderLayout so it automatically gets sized to fill the entire space of the frame.
The solution is to give your component a "preferred size" so that any layout manager can use this information to display the component properly.
if your are modifying container's subcomponents you should call jcomponent.validate();
I assume parentContainer is the JDesktopPane?
What kind of changes are you making to sp that are not showing up?
Changing the size of sp will cause Swing to repaint from scratch. That's why the setSize() is fixing the display.
Most likely, the changes you are making are either not happening on the EDT, or are not invalidating the right container. For example, if you change the visibility of a component in sp, you'll need to call sp.invalidate() to rerun the layout manager.
Have you checked that you're only changing components (or their models) on the EDT?
A quick test for that is to run with the Substance LAF as it will complain if you change things on another thread.

Size miscalculated when dynamically adding JPanels

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.

JSplitPane splitting 50% precisely

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

Categories

Resources