How to expand the window when you click a button?

4

Is it possible to expand a window with more information when the user clicks a "Details" button? The behavior is similar to some error screens, which show the data of an error, as shown in the image below:

My window has a JButton which, when clicked, should expand JDialog and display the contents of a JTextArea or ScrollPane .

    
asked by anonymous 03.03.2015 / 19:26

2 answers

4

This screen can be entirely built in the hand, if you make a point of having it in your application, follow an example that I developed, the code is with the appropriate comments:

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.JToggleButton;

/**
*
* @author diego.felipe
*/
public class DialogUnhandledException {

    // algumas IDE's podem reclamar de codificação de texto,
    // por causa destes simbolos, por isso deve-se manter
    // a codificação UTF-8 ou alterar os simbolos
    private static final String BUTTON_ARROW_NORTH = "▲";
    private static final String BUTTON_ARROW_SOUTH = "▼";
    private static final String MESSAGE_DEFAULT = "An unhandled exception has occurred in your application. "
            + "Click on Details for more information or click Quit to close the application immediately.";

    private DialogUnhandledException() {

    }

    public static void showException(String shortError, String strStackTrace) {

        // - fixedWith é um tamanho fixo para o frame não ficar redimensionando
        // ao abrir o JTextPane contendo a stacktrace.
        // - minHeight é o tamanho minimo do frame, quando o JTextPane da
        // StackTrace não estiver visível.
        // - maxHeight é a altura máxima do frame, comportando o JTextPane da
        // stracktrace aberto.
        int fixedWidth = 450;
        int minHeight = 135;
        int maxHeight = 300;

        JFrame frame = new JFrame();
        frame.getContentPane().setLayout(new BorderLayout());
        // obrigatório, para que o BorderLayout não redimensione automaticamente
        frame.setPreferredSize(new Dimension(fixedWidth, minHeight));
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // painel principal que comportará os paineis de botoes
        // e da mensagem padrao
        JPanel principalPane = new JPanel();
        principalPane.setLayout(new BoxLayout(principalPane, BoxLayout.Y_AXIS));

        // painel onde será comportado o JTextPane com a mensagem
        // padrão mais a mensagem de erro sem o stacktrace
        JPanel messageDefaultPane = new JPanel(new BorderLayout(0, 5));
        messageDefaultPane.setBorder(BorderFactory.createEmptyBorder(3, 3, 0, 0));
        // componente onde será exibida a mensagem de erro padrao
        JTextPane tp_messageDefault = new JTextPane();
        tp_messageDefault.setBackground(frame.getBackground());
        tp_messageDefault.setEditable(false);
        tp_messageDefault.setText(MESSAGE_DEFAULT + "\n\n" + shortError);

        messageDefaultPane.add(tp_messageDefault, BorderLayout.CENTER);

        // scrollPane comportará o JTextPane que exibirá o stacktrace quando o
        // botao for clicado
        JScrollPane scrollPane = new JScrollPane();
        JTextPane tp_stackTrace = new JTextPane();
        tp_stackTrace.setBackground(frame.getBackground());
        tp_stackTrace.setEditable(false);

        // painel contendo os botoes Detais, Quit e Continue
        JPanel buttonPane = new JPanel();
        buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS));
        buttonPane.setBorder(BorderFactory.createEmptyBorder(5, 0, 5, 10));

        // Como o estado do botão Details servirá para exibir ou ocultar
        // o JToggleButton faz mais sentido aqui
        JToggleButton btn_showDetails = new JToggleButton("Details " + BUTTON_ARROW_SOUTH);
        btn_showDetails.addItemListener(new ItemListener() {

            @Override
            public void itemStateChanged(ItemEvent e) {
                // quando o status do botao for "selecionado", a stacktrace será
                // atribuida no JTextPane, o frame terá sua altura alterada
                // para maxHeight e a posição da barra de scroll
                // ficará no topo do erro
                if (e.getStateChange() == ItemEvent.SELECTED) {
                    tp_stackTrace.setText(strStackTrace);
                    frame.setPreferredSize(new Dimension(fixedWidth, maxHeight));
                    tp_stackTrace.setCaretPosition(0);
                    frame.pack();
                    btn_showDetails.setText("Detalhes " + BUTTON_ARROW_NORTH);
                    // quando o status anterior do botao for desfeito, remove a
                    // stacktrace do componente de texto e redefine
                    // a altura minima para o frame
                } else if (e.getStateChange() == ItemEvent.DESELECTED) {
                    tp_stackTrace.setText(null);
                    frame.setPreferredSize(new Dimension(fixedWidth, minHeight));
                    frame.pack();
                    btn_showDetails.setText("Detalhes " + BUTTON_ARROW_SOUTH);
                }
            }
        });

        // funcionalidades não implementadas
        JButton btn_continue = new JButton("Continue");
        JButton btn_quit = new JButton("Quit");

        buttonPane.add(btn_showDetails);
        buttonPane.add(Box.createHorizontalGlue());
        buttonPane.add(btn_continue);
        buttonPane.add(Box.createRigidArea(new Dimension(10, 0)));
        buttonPane.add(btn_quit);

        scrollPane.setViewportView(tp_stackTrace);

        principalPane.add(messageDefaultPane);
        principalPane.add(buttonPane);

        frame.add(principalPane, BorderLayout.NORTH);
        frame.add(scrollPane, BorderLayout.CENTER);
        frame.pack();
        frame.setLocationRelativeTo(null);

        frame.setVisible(true);

    }

}

Its use is very simple, and following the @bigown response, can be used in conjunction with the class suggested in its response, but for this, it is necessary to convert error messages and stackTrace to string:

import java.io.PrintWriter;
import java.io.StringWriter;

public final class ExceptionHandler implements Thread.UncaughtExceptionHandler {

    @Override
    public void uncaughtException(Thread thread, Throwable throwable) {

        StringWriter strStackTrace = new StringWriter();
        throwable.printStackTrace(new PrintWriter(strStackTrace));
        DialogUnhandledException.showException(throwable.toString(), strStackTrace.toString());
    }

}

To work, simply set the class as the default handler for uncaught exceptions. The line below should be the first in the application:

public class Programa{

    public static void main(String[] args) {
        // registra nossa classe como manipulador padrao de 
        // exceções não capturadas   
        Thread.setDefaultUncaughtExceptionHandler(new ExceptionHandler());

            String a = null;
            System.out.println(a.equals(""));
            System.out.println(Integer.parseInt(a));
    }
}

A less drastic solution (also suggested by the @bigown in the other answer), is to put in main , surrounding the excerpt that starts its application with a block try-catch :

public class Programa{

    public static void main(String[] args) {

        try {
            // define o look And Feel de acordo com o tema do SO em execução
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());

            String a = null;
            System.out.println(a.equals(""));
            System.out.println(Integer.parseInt(a));
        } catch (Exception e) {
            StringWriter strStackTrace = new StringWriter();
            e.printStackTrace(new PrintWriter(strStackTrace));
            SwingUtilities.invokeLater(() -> DialogUnhandledException.
                    showException(e.getClass() + " " + e.getMessage(),strStackTrace.toString()));
        }
    }
}

The result for both forms would look similar to the image below:

Remember that to look like this, it needs to be running on windows, if the swing application runs in linux or OSx, the appearance will be according to the operating system theme being executed.

    
08.02.2017 / 13:00
3

Apparently this message box appeared by catching the exception made by Java itself. So you have nothing to do.

What you can do is to catch the exception in the right place - it can be in main() - and treat it as you want. There you show on a screen that you have control to do as you want programmatically, probably a JFrame .

You can choose the type of window you want and you can configure it as you see fit.

You can also create a global handle, something like this:

public final class ExceptionHandler implements Thread.UncaughtExceptionHandler {
    @Override  public void uncaughtException(Thread thread, Throwable throwable) {
        JOptionPane.showMessageDialog(null, "Erro: " + throwable.toString(), 
            "Erro", JOptionPane.ERROR_MESSAGE);
    }
}

It's just an example showing the overall catch of exceptions, of course this way using JOptionPane.showMessageDialog is just control.

    
03.03.2015 / 19:40