JInternalFrame trava ao clicar 2 vezes na opção abrir - Resolvido

Olá pessoal. Sou novo na programação em JAVA e uso o Netbeans 8.2.
Estou criando um sistema com um Frame principal, onde contém um JDesktopPane, e nesse, abrem outros JInternalFrame (usuário, cliente, etc).
Tudo abrindo e funcionando, mas notei que, quando clico 2 vezes no menu, para abrir o mesmo JInternalFrame, esse trava e apresenta um erro no Netbeans.

Segue mensagem de erro:

 `Exception in thread "AWT-EventQueue-0" java.lang.IllegalArgumentException: illegal component position
	at java.awt.Container.addImpl(Container.java:1100)
	at javax.swing.JLayeredPane.addImpl(JLayeredPane.java:231)
	at javax.swing.JDesktopPane.addImpl(JDesktopPane.java:484)
	at java.awt.Container.add(Container.java:417)
	at visao.TelaPrincipal.abrirJanelaUsuario(TelaPrincipal.java:49)
	at visao.TelaPrincipal.MenuItemUsuarioActionPerformed(TelaPrincipal.java:220)
	at visao.TelaPrincipal.access$400(TelaPrincipal.java:19)
	at visao.TelaPrincipal$5.actionPerformed(TelaPrincipal.java:173)
	at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2022)
	at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2348)
	at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:402)
	at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:259)
	at javax.swing.AbstractButton.doClick(AbstractButton.java:376)
	at javax.swing.plaf.basic.BasicMenuItemUI.doClick(BasicMenuItemUI.java:833)
	at javax.swing.plaf.basic.BasicMenuItemUI$Handler.mouseReleased(BasicMenuItemUI.java:877)
	at java.awt.AWTEventMulticaster.mouseReleased(AWTEventMulticaster.java:289)
	at java.awt.Component.processMouseEvent(Component.java:6533)
	at javax.swing.JComponent.processMouseEvent(JComponent.java:3324)
	at java.awt.Component.processEvent(Component.java:6298)
	at java.awt.Container.processEvent(Container.java:2236)
	at java.awt.Component.dispatchEventImpl(Component.java:4889)
	at java.awt.Container.dispatchEventImpl(Container.java:2294)
	at java.awt.Component.dispatchEvent(Component.java:4711)
	at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4888)
	at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4525)
	at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4466)
	at java.awt.Container.dispatchEventImpl(Container.java:2280)
	at java.awt.Window.dispatchEventImpl(Window.java:2746)
	at java.awt.Component.dispatchEvent(Component.java:4711)
	at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:758)
	at java.awt.EventQueue.access$500(EventQueue.java:97)
	at java.awt.EventQueue$3.run(EventQueue.java:709)
	at java.awt.EventQueue$3.run(EventQueue.java:703)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:80)
	at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:90)
	at java.awt.EventQueue$4.run(EventQueue.java:731)
	at java.awt.EventQueue$4.run(EventQueue.java:729)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:80)
	at java.awt.EventQueue.dispatchEvent(EventQueue.java:728)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
	at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
	at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)`

Segue códigos do sistema:

 private void abrirJanelaUsuario() {
        if (telaCadUsuario == null) {
            telaCadUsuario = new TelaCadastroUsuario();
        }
        jdpAreaTrabalho.add(telaCadUsuario);
        telaCadUsuario.setLocation(jdpAreaTrabalho.getWidth() / 2 - telaCadUsuario.getWidth() / 2, jdpAreaTrabalho.getHeight() / 2 - telaCadUsuario.getHeight() / 2);
        telaCadUsuario.setVisible(true);
    }`
private void MenuItemUsuarioActionPerformed(java.awt.event.ActionEvent evt) {                                                
    try {
        conecta.executaSql("SELECT * FROM usuario WHERE usuario='" + nomeUsuario.getText() + "'");
        conecta.rs.first();
        if (conecta.rs.getString("tipo").equals("Administrador")) {
            abrirJanelaUsuario();
        } else {
            JOptionPane.showMessageDialog(null, "Você não tem permissão para acessar essa tela.\nContate o Administrador do sistema!");
        }
    } catch (SQLException ex) {
        JOptionPane.showMessageDialog(null, "Você não tem permissão para acessar essa tela.\nContate o Administrador do sistema!");
    }
}

Exceção relacionada à posição do componente swing:

Pelo que compreendi, você não pode ficar adicionando o componente de forma inadvertida, como na instrução abaixo, constante em sua codificação.

Depois de adicionar um componente, você deve chamar tela.setVisivble(false), tela.setVisible(true).

Veja:

Obrigado por responder addller
Tentei alterar a posição dos códigos, e parou de dar os erros, mas agora quando fecho a tela do JInternalFrame, não abre mais…

Então, o JInternalFrame, é um JFrame “enlatado”, mas ainda assim é um JFrame.
Faz o seguinte, vai na classe que está estendendo o JInternalFrame e o altere o método de fechamento para setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE).
Se estiver chamando o dispose() em algum evento, use o hide().

Peguei um exemplo da Oracle e fiz algumas alterações, procure pelo método createFrame():

https://docs.oracle.com/javase/tutorial/displayCode.html?code=https://docs.oracle.com/javase/tutorial/uiswing/examples/components/InternalFrameDemoProject/src/components/InternalFrameDemo.java

import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.beans.PropertyVetoException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.KeyStroke;

public class InternalFrameDemo extends JFrame implements ActionListener {

    /*
 * InternalFrameDemo.java requires:
 *   MyInternalFrame.java
     */
    JDesktopPane desktop;
    MyInternalFrame frame;

    public InternalFrameDemo() {
        super("InternalFrameDemo");

        //Make the big window be indented 50 pixels from each edge
        //of the screen.
        int inset = 50;
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        setBounds(inset, inset, screenSize.width - inset * 2, screenSize.height - inset * 2);

        //Set up the GUI.
        desktop = new JDesktopPane(); //a specialized layered pane
        createFrame(); //create first "window"
        setContentPane(desktop);
        setJMenuBar(createMenuBar());

        //Make dragging a little faster but perhaps uglier.
        desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
    }

    protected final JMenuBar createMenuBar() {
        JMenuBar menuBar = new JMenuBar();

        //Set up the lone menu.
        JMenu menu = new JMenu("Document");
        menu.setMnemonic(KeyEvent.VK_D);
        menuBar.add(menu);

        //Set up the first menu item.
        JMenuItem menuItem = new JMenuItem("New");
        menuItem.setMnemonic(KeyEvent.VK_N);
        menuItem.setAccelerator(KeyStroke.getKeyStroke(
                KeyEvent.VK_N, ActionEvent.ALT_MASK));
        menuItem.setActionCommand("new");
        menuItem.addActionListener(this);
        menu.add(menuItem);

        //Set up the second menu item.
        menuItem = new JMenuItem("Quit");
        menuItem.setMnemonic(KeyEvent.VK_Q);
        menuItem.setAccelerator(KeyStroke.getKeyStroke(
                KeyEvent.VK_Q, ActionEvent.ALT_MASK));
        menuItem.setActionCommand("quit");
        menuItem.addActionListener(this);
        menu.add(menuItem);

        return menuBar;
    }

    //React to menu selections.
    @Override
    public void actionPerformed(ActionEvent e) {
        if ("new".equals(e.getActionCommand())) { //new
            createFrame();
        } else { //quit
            quit();
        }
    }

    //Create a new internal frame.
    protected final void createFrame() {
        try {
            if (this.frame == null) {
                frame = new MyInternalFrame();
                desktop.add(frame);
            }
            frame.setSelected(true);
            frame.setVisible(true);
        } catch (PropertyVetoException ex) {
            Logger.getLogger(InternalFrameDemo.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    //Quit the application.
    protected void quit() {
        System.exit(0);
    }

    /**
     * Create the GUI and show it. For thread safety, this method should be
     * invoked from the event-dispatching thread.
     */
    private static void createAndShowGUI() {
        //Make sure we have nice window decorations.
        JFrame.setDefaultLookAndFeelDecorated(true);

        //Create and set up the window.
        InternalFrameDemo frame = new InternalFrameDemo();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        //Display the window.
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        //Schedule a job for the event-dispatching thread:
        //creating and showing this application's GUI.
        javax.swing.SwingUtilities.invokeLater(() -> createAndShowGUI());
    }
}

Classe que ajuda no teste:

import java.awt.BorderLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JTextField;

class MyInternalFrame extends JInternalFrame {

    public MyInternalFrame() {
        setTitle("My internal Frame");
        
        setSize(300, 200);
        setLayout(new BorderLayout());
        add(new JTextField("I am: "+hashCode()), BorderLayout.CENTER);
        JButton sair = new JButton("Sair");
        sair.addActionListener(actionEvent -> hide());
        add(sair, BorderLayout.SOUTH);
        setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);//se vai alternar com setVisible, troque para -> JFrame.HIDE_ON_CLOSE
    }   

}

Observação: o tutorial da Oracle, permitiu fazer várias inclusões, como a implementação que fiz tem como base o tutorial, mas possui pequenas diferenças, veja no site da Oracle como eles construiram o método createFrame().

Eu manifesto preferência pela alteração que fiz, caso queira manipular um objeto em particular.
Caso seja obrigatório se desfazer o objeto, use dispose() ou JFrame.EXIT_ON_CLOSE mesmo, tendo como base o tutorial completo da Oracle, promovendo as alteração que você considerar satisfatórias.

Té+

Tem que cuidar com essas “lendas”, não é má prática utilizar singleton, desde que você utilize da forma correta e sem abusar, aqui tem uma discussão interessante.
Embora alguns autores sugiram utilizar o enum do Java para criar singletons, isso sim é uma puta gambiarra pois o propósito do enum é fornecer enumerações fortemente tipadas.

1 curtida

Meu foco principal era na verdade:

Depois vi que seria possível fazer várias inclusões:

Quanto a:

Eu não fiz nenhuma ponderação pois não teria argumentos, já que comecei a estudar padrões de projeto a relativamente pouco tempo.

O pouco que aprendi de Singleton, foi no vídeo abaixo, que me pareceu ser uma construção robusta:

Quanto a testes e segurança, creio que seja uma questão de:

Concordo! :smile_cat:

Particularmente, ainda que me falte entender muita coisa, gosto bastante de Design Patterns, embora tenha tido conhecimento deles há pouco tempo por intermédio da universidade, já que não atuo em nenhum ramo de tecnologia e comecei a participar do fórum por ser recomendação do curso, o que me trouxe novos conhecimentos, claro, não é muito, mas para mim está sendo interessante.

Te+

Obrigado a ajuda de vocês, addller e staroski.
Minha intenção era apenas fazer com que o JInternalFrame não abrisse várias vezes, pois quando clicava no menu referente ao mesmo, ele abria várias telas. Então, como falei, sou novo no assunto, ainda estudando JAVA, e esse é o meu primeiro projeto. Consegui pesquisar e cheguei até essa solução, onde consegui fazer com que a tela não abrisse várias vezes repetidamente, mas depois percebi que ao fechar, não abria mais. E pelo que percebi, só falta corrigir esse erro pra completar o projeto (aparentemente…).

Poste a classe TelaCadastroUsuario

Depois poste novamente o método:
private void abrirJanelaUsuario()

Pois no exemplo que passei, estava abrindo e fechando normalmente.

Na verdade só inverti a posição dos códigos. Agora ele abre a tela só uma única vez, mas se fechar, não abre novamente.
Abrir JInternalFrame:

private void abrirJanelaUsuario() {
        if (telaUsuario == null) {
            telaUsuario = new TelaCadastroUsuario();
            AreaDeTrabalho.add(telaUsuario);
            telaUsuario.setVisible(true);
        }             
        telaUsuario.setLocation(AreaDeTrabalho.getWidth() / 2 - telaUsuario.getWidth() / 2, AreaDeTrabalho.getHeight() / 2 - telaUsuario.getHeight() / 2);           
    }

Ação do menu:

private void MenuItemUsuarioActionPerformed(java.awt.event.ActionEvent evt) {                                                
    try {
        conecta.executaSql("SELECT * FROM usuario WHERE usuario='" + nomeUsuario.getText() + "'");
        conecta.rs.first();
        if (conecta.rs.getString("tipo").equals("Administrador")) {
            abrirJanelaUsuario();
        } else {
            JOptionPane.showMessageDialog(null, "Você não tem permissão para acessar essa tela.\nContate o Administrador do sistema!");
        }
    } catch (SQLException ex) {
        JOptionPane.showMessageDialog(null, "Você não tem permissão para acessar essa tela.\nContate o Administrador do sistema!");
    }
}

Faltou postar a classe:

Ela (a classe TelaCadastroUsuario) tem as informações de fechamento.
Tem que usar: setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); para o fechamento.
Além disso tem o método hide(), que pode ser usado em situações particulares, ex.: clicar em um botão e esconder a tela.

Desculpa, tentei colocar aqui, mas não copiou os códigos todos.
TeleCadastroUsuario:

 package visao;

import controle.Conexao;
import java.sql.SQLException;
import java.util.ArrayList;
import javax.swing.JOptionPane;
import javax.swing.ListSelectionModel;
import modeloBean.ModeloTabela;
import modeloBean.Usuario;
import modeloDao.UsuarioDAO;

 public class TelaCadastroUsuario extends javax.swing.JInternalFrame {

     Conexao conecta = new Conexao();
     Usuario user = new Usuario();
     UsuarioDAO udao = new UsuarioDAO();
     int flag = 0;

     public TelaCadastroUsuario() {
         initComponents();
         preencherTabela("SELECT * FROM usuario ORDER BY nome");
     }

 private void jbtSairActionPerformed(java.awt.event.ActionEvent evt) {                                        
         this.dispose();

Ajuste 1:

private void abrirJanelaUsuario() {
        if (telaUsuario == null) {
            telaUsuario = new TelaCadastroUsuario();
            AreaDeTrabalho.add(telaUsuario);
        }          

        telaUsuario.setLocation(AreaDeTrabalho.getWidth() / 2 - telaUsuario.getWidth() / 2, AreaDeTrabalho.getHeight() / 2 - telaUsuario.getHeight() / 2);        
        telaUsuario.setVisible(true);   //estava dentro do if, só seria visível uma vez   
    }

Ajuste 2:

    public TelaCadastroUsuario() {
         initComponents();
         setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);//esconde a tela ao clicar em fechar
         preencherTabela("SELECT * FROM usuario ORDER BY nome");
     }

Ajuste 3:

private void jbtSairActionPerformed(java.awt.event.ActionEvent evt) {                                        
        setVisible(false);//esconde a tela
}

Tente as alterações acima.
Obs.: editei o meu código, pois o hide() esta “deprecado”, alterando para setVisible(false).
XD

O código que passei anteriormente está bem resumido.
Troquei a opção de fechamento da tela Usuario, de this.dispose(); para setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);, e mudei na propriedade do InternalFrame do Netbeans a opção de DISPOSE para HIDE, mas continua não abrindo após fechar.

Opa, fiz os ajustes e agora foi bem…
Vou corrigir nas outras telas e aviso aqui, desde já agradeço a ajuda addller.

E as alterações nos métodos a seguir, você fez?

private void jbtSairActionPerformed(java.awt.event.ActionEvent evt) {                                        
        setVisible(false);//esconde a tela
}

private void abrirJanelaUsuario() {
        if (telaUsuario == null) {
            telaUsuario = new TelaCadastroUsuario();
            AreaDeTrabalho.add(telaUsuario);
        }          

        telaUsuario.setLocation(AreaDeTrabalho.getWidth() / 2 - telaUsuario.getWidth() / 2, AreaDeTrabalho.getHeight() / 2 - telaUsuario.getHeight() / 2);        
        telaUsuario.setVisible(true);   //estava dentro do if, só seria visível uma vez   
    }

Exatamente.
Vou corrigir nas outras telas e aviso aqui.

addller, RESOLVIDO…
Agora sim, deu tudo certo:

  • as janelas abrem sem repetir tela;
  • não dá mais erro;
  • abre só uma vez sem travar;
  • ao clicar no menu da janela já aberta, ela apenas abre a mesma janela centralizada, sem duplicar.

Muito obrigado pela ajuda.
Agora finalizar os pequenos detalhes…

Good job

É porque o seu setVisible(true) está dentro do if (telaUsuario == null).
O if (telaUsuario == null) é só pra verificar se precisa inicializar a variável.
O setVisible(true) tem que ficar fora do if.