views:

214

answers:

3

Our application displays a 2D view of our data (mainly maps) and then allows the user to change to a 3D view. The 2D and 3D views are generated by custom C++ code that is SWIG'ed into our Swing GUI and wrapped within a JComponent. These JComponents are then displayed within another parent JComponent.

Our problem is that when we change from the 2D to the 3D view and then back to the 2D view, when we resize the window the 2D view does not get resized. The resize events don't get sent to the 2D view.

Our application runs under Linux (Fedora 11). We're running Java version 1.6.0_12.

Here is some sample code in which I've replaced the 2D view and 3D view with two 2 JButtons, that produces the same behaviour. Once you go to 3D and then back to 2D, resizing the window does not cause the 2D view to be resized.

/* TestFrame.java
 * Compile with: $ javac TestFrame.java
 * Run with: $ java TestFrame
 */

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import javax.swing.JButton;

public class TestFrame extends javax.swing.JFrame {

    private boolean mode2D = true;
    private JButton view2D = null;
    private JButton view3D = null;
    private Container parent = null;

    public TestFrame() {
        initComponents();
        containerPanel.setLayout(new BorderLayout());
        view2D = new JButton("2D View");
        view2D.addComponentListener(new MyListener("2D VIEW"));
        containerPanel.add(view2D);
    }

    private void changerButtonActionPerformed(java.awt.event.ActionEvent evt) {        
        if (parent == null) {
            parent = view2D.getParent();
        }
        if (mode2D) {
            System.out.println("Going from 2D to 3D");

            view2D.setVisible(false);

            if (view3D != null) {
                view3D.setVisible(true);
            } else {
                view3D = new JButton("3D View");
                view3D.addComponentListener(new MyListener("3D VIEW"));   
                parent.add(view3D);
            }

            ((JButton) evt.getSource()).setText("Change to 2D");
            mode2D = false;
        } else {
            System.out.println("Going from 3D to 2D");
            view3D.setVisible(false);
            view2D.setVisible(true);
            ((JButton) evt.getSource()).setText("Change to 3D");
            mode2D = true;
        }
    }

    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new TestFrame().setVisible(true);
            }
        });
    }
    private javax.swing.JPanel containerPanel;
    private javax.swing.JButton changerButton;

    private class MyListener implements ComponentListener {
        private String name;
        public MyListener(String name) {
            this.name = name;
        }
        @Override
        public void componentHidden(ComponentEvent event) {
            System.out.println("@@@ [" + name + "] component Hidden");
        }
        @Override
        public void componentResized(ComponentEvent event) {
            System.out.println("@@@ [" + name + "] component Resized");
        }
        @Override
        public void componentShown(ComponentEvent event) {
            System.out.println("@@@ [" + name + "] component Shown");
        }
        @Override
        public void componentMoved(ComponentEvent event) {
            System.out.println("@@@ [" + name + "] component Moved");
        }
    };

    @SuppressWarnings("unchecked")
    private void initComponents() {
        containerPanel = new javax.swing.JPanel();
        changerButton = new javax.swing.JButton();
        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        containerPanel.setBorder(new javax.swing.border.MatteBorder(null));
        javax.swing.GroupLayout containerPanelLayout = new javax.swing.GroupLayout(containerPanel);
        containerPanel.setLayout(containerPanelLayout);
        containerPanelLayout.setHorizontalGroup(
            containerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 374, Short.MAX_VALUE)
        );
        containerPanelLayout.setVerticalGroup(
            containerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 239, Short.MAX_VALUE)
        );
        changerButton.setText("Change to 3D");
        changerButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                changerButtonActionPerformed(evt);
            }
        });
        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(containerPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addComponent(changerButton))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(containerPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(changerButton)
                .addContainerGap())
        );
        pack();
    }
}

(My apologies for the Netbeans generated GUI code)

I should mention that when we call parent.remove(view2D) and parent.add(view3D) to change the views the X Windows ID of our 3D view changes and we're unable to get our 3D view back. Therefore parent.remove(view2D) and parent.add(view3D) is not really a solution and we have to call setVisible(false) and setVisible(true) on the JComponents that contain our 2D and 3D views in order to hide and show them.

Any help will be greatly appreciated.

A: 

I've found a solution. You need to add/remove the components from the layout manager:

private void changerButtonActionPerformed(java.awt.event.ActionEvent evt) {        
        if (parent == null) {
            parent = view2D.getParent();
        }

        LayoutManager layoutMgr = parent.getLayout();

        if (mode2D) {
            System.out.println("Going from 2D to 3D");

            view2D.setVisible(false);
            layoutMgr.removeLayoutComponent(view2D);

            if (view3D != null) {
                view3D.setVisible(true);                
                if (layoutMgr != null && layoutMgr instanceof LayoutManager2) {
                    ((LayoutManager2) layoutMgr).addLayoutComponent(view3D, null);
                }

            } else {
                view3D = new JButton("3D View");
                view3D.addComponentListener(new MyListener("3D VIEW"));   
                parent.add(view3D);
            }

            ((JButton) evt.getSource()).setText("Change to 2D");
            mode2D = false;
        } else {
            System.out.println("Going from 3D to 2D");
            view3D.setVisible(false);
            layoutMgr.removeLayoutComponent(view3D);

            view2D.setVisible(true);            
            if (layoutMgr != null && layoutMgr instanceof LayoutManager2) {
                ((LayoutManager2) layoutMgr).addLayoutComponent(view2D, null);
            }

            ((JButton) evt.getSource()).setText("Change to 3D");
            mode2D = true;
        }
    }
iWerner
That sounds overly complicated, it should be enough just to remove and add the components from/to 'parent'. Or use the CardLayoutManager suggested in the other reply.
fish
+2  A: 

Why don't you use a CardLayout to switch from the 2D to 3D component? CardLayout is exactly done for this kind of purpose in my understanding. Another benefit would be that it will simplify your code.

Matthieu BROUILLARD
Card layout has tendency to keep the size of the biggest element, even if a smaller is displayed. It can be annoying if sizes are very different. However, this is based on what I remember, maybe I was doing something wrong when I tried it.
Gnoupi
Yes but I guess it is a normal behaviour. I would not like to have my window redimensionned each time I switch from one view to another. Even if one component is smaller than another (in different cards I mean), I prefer either to center it or to stick it on top (or elsewhere).
Matthieu BROUILLARD
+2  A: 

After your remove() and add() methods to change the component, you should call:

parent.revalidate(); //To make the layout manager do its work.
parent.repaint(); //This could be necessary, to suggest a repaint of the panel

Javadoc for JComponent#revalidate() :

Supports deferred automatic layout.

Calls invalidate and then adds this component's validateRoot to a list of components that need to be validated. Validation will occur after all currently pending events have been dispatched. In other words after this method is called, the first validateRoot (if any) found when walking up the containment hierarchy of this component will be validated. By default, JRootPane, JScrollPane, and JTextField return true from isValidateRoot.

This method will automatically be called on this component when a property value changes such that size, location, or internal layout of this component has been affected. This automatic updating differs from the AWT because programs generally no longer need to invoke validate to get the contents of the GUI to update.

Gnoupi