user2143292
user2143292

Reputation: 47

JDialog not updating for new input

I am building my first gui and so far everything is working fine except for a malfunction with a JDialog. It accepts a list of names and processes accordingly when used the first time. But when I pull it back up to enter new input it remains irresponsive. I don´t think it´s a thread issue as I have tested the code using several System.out.println ( SwingUtilities.isEventDispatchThread() ); statements throughout the source code. Here is part of the code that could be originating the problem.

package testme;




import java.awt.BorderLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*; 

public class Test {    
    JDialog dialog;
    JButton horseList, ok, clear;
    JPanel jpDialog = new JPanel();
    JPanel buttonPanel = new JPanel();
    GridBagLayout gbLayout = new GridBagLayout(); 
    BorderLayout borderLayout = new BorderLayout();
    GridBagConstraints gbc = new GridBagConstraints();
    int fnh = 8;
    JTextField[] jtxt = new JTextField[fnh];
    int[] hNum = new int[fnh];
    int[] hVal = new int[fnh];
    String[] hNam = new String[fnh];
    JFrame jfr = new JFrame();

     public Test()  {

       jfr.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
       jfr.setTitle("My Alladin Lamp"); 
       jfr.setSize( 200, 80 ); 
       jfr.setVisible( true );
       jfr.setLayout( borderLayout );


       horseList = new JButton( "Enter Horse Names" ); 
       jfr.add( horseList, BorderLayout.CENTER );
       horseList.addActionListener( new ActionListener() {   
          @Override
          public void actionPerformed( ActionEvent e ) {      
           dialog = new JDialog( jfr, "Enter Horse Names", true );
           dialog.setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE ); 
           dialog.setSize( 260, 400 );                      
           jpDialog.setLayout( gbLayout );           
           JLabel label;
           String str;
           for( int i = 0; i < fnh; i++ )
           {                   
              gbc.gridx = 0;
              gbc.gridy = i;           
              str = new Integer( i+1 ) + ".";                  
              label = new JLabel( str ); 
              jpDialog.add( label, gbc );         

              gbc.gridx = 1;
              gbc.gridy = i;
              gbc.ipady = 4;
              gbc.insets = new Insets(4,0,0,0);
              jtxt[i] = new JTextField(15);  
              jpDialog.add( jtxt[i], gbc );               
           }
           buttonPanel = new JPanel();        
           ok = new JButton( "OK" );           
           ok.addActionListener( new ActionListener() {
              @Override
              public void actionPerformed( ActionEvent e ) {     
               for( int i = 0; i < fnh; i++ ) {               
                  hNam[i] = jtxt[i].getText();                                 
               }


               dialog.dispose();   
             }
           });          
           buttonPanel.add( ok ); 

           clear = new JButton ( "CLEAR" );        
           clear.addActionListener( new ActionListener() {
               @Override
               public void actionPerformed( ActionEvent e ) {
                  for( int i = 0; i < fnh; i++ )              
                     if ( !"".equals( jtxt[i].getText() ) )
                     jtxt[i].setText( "" );        
                  }
            });          

           buttonPanel.add( clear );       
           JScrollPane jscr = new JScrollPane( jpDialog );  
           dialog.add( jscr, BorderLayout.CENTER ); 
           dialog.add( buttonPanel, BorderLayout.SOUTH ); 
           dialog.setVisible( true );    
           }
       }); 

     }



// -------------------------------------------------------------------------


    public static void main( String args[] )  {                
        SwingUtilities.invokeLater( new Runnable()  {
            @Override
            public void run()
            {
                Test test = new Test();
            }
        });
    }

}                             

Upvotes: 2

Views: 1941

Answers (2)

Gene
Gene

Reputation: 46960

When dispose is called, resources for the dialog are released. You must either allocate a new one entirely from scratch or - better - call setVisible(false) to dismiss the dialog and then setVisible(true) when you need it again.

The second method is better because complex dialogs can take a noticeable time to construct. It's a better experience for the user to have the dialog pop up immediately. My applications build complex dialogs during application startup - before any user interface is made visible, while the splash is still showing - for this reason.

You can override setVisible to ensure the dialog is re-initialized each time it is shown.

If you still want to construct from scratch each time a dialog is needed and then dispose when the user makes a selection, then the best way to go is to subclass JDialog. Your code is failing because it's allocating some parts of the dialog (for example a layout) in the enclosing class and then assuming those parts still exist after dispose() is called. This is a big problem. If you subclass JDialog, there is almost no way you can make a mistake like this. The parts of the dialog will all be allocated in the constructor and be referenced within the subclass itself. After the dialog is displosed, no references to its fields/memebers can exist.

Okay, I will show an example that handles several common cases:

  1. Refresh component data every time dialog becomes visible.
  2. Clear selection every time dialog becomes visible.
  3. Boolean flag that's true if OK was pressed or an equivalent action like double-click.
  4. Flag is false if Cancel was pressed or user closed dialog manually.

This idiom has worked well for me on a number of fairly big applications. I hope it's helpful to you.

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class Test extends JFrame {

    // The dialog we'll create and use repeatedly.
    TestDialog testDialog;

    // Some words to fill a list.
    String [] words = ("Four score and seven years ago our fathers brought "
            + "forth on this continent a new nation conceived in liberty and "
            + "dedicated to the proposition that all men are created equal")
            .split("\\s+");

    // Start index of words to load next time dialog is shown.
    int wordIndex = 0;

    // A place we'll show what was done by the dialog.
    JLabel msg;

    public Test() {

        setSize(800, 600);
        setLocationRelativeTo(null);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // Add the "show dialog" button.
        JButton showDialog = new JButton("Press to show the dialog");
        add(showDialog, BorderLayout.NORTH);

        // Add the "dialog result" label.
        msg = new JLabel("  Dialog Result: --");
        add(msg, BorderLayout.CENTER);

        showDialog.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                // Create the dialog lazily.
                if (testDialog == null) {
                    testDialog = new TestDialog(Test.this);
                }
                // Load fresh data in the dialog prior to showing it.  
                // Here it's just an array of words into the dialog.
                String [] newWords = new String[5];
                for (int i = 0; i < newWords.length; i++) {
                    newWords[i] = words[wordIndex];
                    wordIndex = (wordIndex + 1) % words.length;
                }
                testDialog.initialize(newWords);

                // Show the dialog and block until user dismisses it.
                testDialog.setVisible(true);

                // Handle the result.  Here we just post a message.
                if (testDialog.getOkClicked()) {
                    msg.setText("Ok, we have: " + testDialog.getSelectedString());
                }
                else {
                    msg.setText("Cancelled!");
                }
            }
        });        
    }

    public static void main(String[] args) {
        // Don't forget Swing code must run in the UI thread, so
        // must invoke setVisible rather than just calling it.
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new Test().setVisible(true);
            }
        });
    }
}

// A test dialog with some common UI idioms. Subclass JDialog so
// that all dialog data is encapsulated.  Nice and clean.
class TestDialog extends JDialog {

    // A list of words that can be double-clicked to return a result.
    private final JList<String> list;

    // A design pattern that works well for all modal dialogs:
    // Boolean flag that's True if OK was clicked, list double-clicked, etc.
    // False if the dialog was cancelled or closed with no action.
    boolean okClicked;

    public TestDialog(JFrame owner) {

        super(owner, true); // true => modal!

        JPanel content = new JPanel(new GridBagLayout());

        // Initialize all dialog components and set listeners.

        // Hierarchy listener is a way to detect actual visibility changes.
        addHierarchyListener(new HierarchyListener() {
            @Override
            public void hierarchyChanged(HierarchyEvent e) {
                // Reset the ok clicked flag every time we become visible.
                // We could also do this by overriding setVisible, but this is cleaner.
                // Can also do other state settings like clearing selections.
                if (isVisible()) {
                    okClicked = false;
                    list.clearSelection();
                }
            }
        });

        // Set up child components.
        // The usual Java layout fiddling. Nothing special here.
        // Add the list first at position (0,0) spanning 2 columns.
        GridBagConstraints constraint = new GridBagConstraints();
        constraint.fill = GridBagConstraints.HORIZONTAL;
        constraint.gridwidth = 2;
        list = new JList<>(new String[]{});
        list.addMouseListener(new MouseAdapter() {

            @Override
            public void mouseClicked(MouseEvent e) {

                // Treat double click on list as select+OK press.
                if (e.getClickCount() == 2) {
                    okClicked = true;
                    setVisible(false);
                }
            }
        });
        content.add(list, constraint);

        // Add Cancel button below list and in left column.
        constraint.gridwidth = 1;
        constraint.fill = GridBagConstraints.NONE;
        constraint.gridy = 1;
        JButton cancel = new JButton("Cancel");
        cancel.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) { 

                // OK not clicked here! Let flag alone.
                setVisible(false);  
            }
        });
        content.add(cancel, constraint);

        // Add OK button below list and in right column.
        constraint.gridx = 1;
        JButton ok = new JButton("OK");
        ok.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                okClicked = true;
                setVisible(false);
            }
        });
        content.add(ok, constraint);

        // Replace default content pane with our JPanel.
        setContentPane(content);
    }

    // Fill the list in the dialog with fresh values.
    public void initialize(final String [] vals) {
        list.setModel(new AbstractListModel<String>() {
            @Override public int getSize() { return vals.length; }
            @Override public String getElementAt(int index) { return vals[index]; }
        });
        pack(); // Resize to fit contents.
        setLocationRelativeTo(getOwner());  // Position in middle of parent.
    }

    public boolean getOkClicked() {
        return okClicked;
    }

    public String getSelectedString() {
        String val =  list.getSelectedValue();
        return (val == null) ? "[none]"  : val;
    }
}

Upvotes: 2

Hovercraft Full Of Eels
Hovercraft Full Of Eels

Reputation: 285405

Even though you create a new JDialog, you're using the same JPanel each time, jpDialog, one that holds the original JTextFields. Create a new everything including the JPanel.

       dialog = new JDialog( jfr, "Enter Horse Names", true );
       dialog.setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE ); 
       dialog.setSize( 260, 400 );        

       jpDialog = new JPanel(); // !! added !! ***********

       jpDialog.setLayout( gbLayout );           
       JLabel label;
       String str;

Myself, though, I'd just keep one dialog and panel, and would clear it when desired rather than keep on creating the GUI anew.

Something like:

import java.awt.BorderLayout;
import java.awt.Dialog.ModalityType;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;

import javax.swing.*;

public class Test2 {
   private static final int HORSE_NAMES_FIELD_COUNT = 10;
   private JPanel mainPanel = new JPanel();
   private EnterHorseNames enterHorsesNames = new EnterHorseNames(
         HORSE_NAMES_FIELD_COUNT);
   private JDialog enterHorseNamesDialog = null;

   public Test2() {
      mainPanel.add(new JButton(new EnterHorsesAction("Enter Horse Names")));
   }

   public JComponent getMainComponent() {
      return mainPanel;
   }

   private class EnterHorsesAction extends AbstractAction {

      public EnterHorsesAction(String text) {
         super(text);
      }

      @Override
      public void actionPerformed(ActionEvent evt) {
         if (enterHorseNamesDialog == null) {
            Window mainWindow = SwingUtilities.getWindowAncestor(mainPanel);
            enterHorseNamesDialog = new JDialog(mainWindow,
                  "Enter Horses Name", ModalityType.APPLICATION_MODAL);
            enterHorseNamesDialog.getContentPane().add(enterHorsesNames.getMainComponent());
            enterHorseNamesDialog.pack();
            enterHorseNamesDialog.setLocationRelativeTo(mainWindow);

         }

         enterHorseNamesDialog.setVisible(true);

         System.out.println("Horse Names:");
         for (int row = 0; row < HORSE_NAMES_FIELD_COUNT; row++) {
            System.out.printf("%2d: %s%n", row + 1, enterHorsesNames.getTextFieldText(row));
         }

         // clear fields
         enterHorsesNames.clearFields();

      }

   }

   private static void createAndShowGui() {
      Test2 test2 = new Test2();

      JFrame frame = new JFrame("Test2");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(test2.getMainComponent());
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

class EnterHorseNames {
   private static final int FIELD_COLS = 18;
   private JPanel mainPanel = new JPanel();
   private JTextField[] textFields;

   public EnterHorseNames(int fieldCount) {
      textFields = new JTextField[fieldCount];
      JPanel centralPanel = new JPanel(new GridBagLayout());
      for (int i = 0; i < textFields.length; i++) {
         textFields[i] = new JTextField(FIELD_COLS);
         addField(centralPanel, textFields[i], i);
      }

      JPanel btnPanel = new JPanel(new GridLayout(1, 0, 5, 0));
      btnPanel.add(new JButton(new OkAction("OK")));
      btnPanel.add(new JButton(new ClearAction("Clear")));

      mainPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
      mainPanel.setLayout(new BorderLayout(5, 5));
      mainPanel.add(centralPanel, BorderLayout.CENTER);
      mainPanel.add(btnPanel, BorderLayout.PAGE_END);
   }

   public void clearFields() {
      for (int i = 0; i < textFields.length; i++) {
         textFields[i].setText("");
      }
   }

   public String getTextFieldText(int row) {
      if (row < 0 || row >= textFields.length) {
         throw new ArrayIndexOutOfBoundsException(row);
      }

      return textFields[row].getText();
   }

   private void addField(JPanel container, JTextField textField, int row) {
      GridBagConstraints gbc = new GridBagConstraints(0, row, 1, 1, 1.0, 1.0,
            GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets(5, 5,
                  5, 10), 0, 0);
      container.add(new JLabel(String.valueOf(row + 1)), gbc);

      gbc.gridx = 1;
      gbc.anchor = GridBagConstraints.EAST;
      gbc.fill = GridBagConstraints.HORIZONTAL;
      gbc.insets = new Insets(5, 10, 5, 5);
      container.add(textField, gbc);
   }

   public JComponent getMainComponent() {
      return mainPanel;
   }

   private class OkAction extends AbstractAction {
      public OkAction(String text) {
         super(text);
      }

      @Override
      public void actionPerformed(ActionEvent evt) {
         Window win = SwingUtilities.getWindowAncestor(mainPanel);
         win.setVisible(false);
      }
   }

   private class ClearAction extends AbstractAction {
      public ClearAction(String text) {
         super(text);
      }

      @Override
      public void actionPerformed(ActionEvent e) {
         clearFields();
      }
   }

}

Upvotes: 1

Related Questions