JFrame se mueve al fondo por alguna razón
Después de que hice una pregunta muy vaga sobre este tema ayer aquí (que voté para cerrar ahora), pude identificar el problema y crear un MCVE que muestra este comportamiento.
El escenario se ve así:
Si bien alguna operación está en curso en segundo plano, se proporciona un diálogo modal de "espera" en primer plano, también se configura el JFrame para que esté desactivado, solo para estar seguro. Una vez finalizada la tarea en segundo plano, el marco se habilita de nuevo y el cuadro de diálogo se elimina.
El problema es que después de que se habilita el JFrame y se elimina un cuadro de diálogo modal, el JFrame se mueve repentinamente a un segundo plano. Con el significado de "fondo", se mueve detrás de la ventana que tenía el foco antes del JFrame. ¿Por qué pasó esto?
Este código debería replicar el problema:
private static JFrame frame;
private static JDialog dialog;
public static void main(String[] args) {
try {
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
buildFrame();
buildDialog();
}
});
} catch (InvocationTargetException | InterruptedException e) {
e.printStackTrace();
}
}
protected static void buildDialog() {
dialog = new JDialog(frame);
dialog.getContentPane().add(new JLabel("This is the dialog"));
dialog.setLocationRelativeTo(frame);
javax.swing.Timer t = new javax.swing.Timer(1000, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
dialog.dispose();
frame.setEnabled(true);
}
});
t.setRepeats(false);
t.start();
dialog.pack();
dialog.setVisible(true);
}
protected static void buildFrame() {
frame = new JFrame();
frame.setMinimumSize(new Dimension(400, 400));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new JLabel("This is the Frame"));
frame.setEnabled(false);
frame.pack();
frame.setVisible(true);
}
¿Alguien sabe por qué sucede esto y cómo se podría prevenir?
Respuestas
El problema son los métodos frame.setEnabled()
. No sé por qué, pero oculta el marco. Mi sugerencia es eliminarlo y usar el concepto de modalidad: dialog.setModal(true)
(también hace que el marco principal no esté disponible cuando se muestra el cuadro de diálogo. Para que el marco no esté disponible, puede colocar un cristal sobre él. Aquí está el código:
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseAdapter;
import java.lang.reflect.InvocationTargetException;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
/**
* <code>DialogFrameTest</code>.
*/
public class DialogFrameTest {
private static JFrame frame;
private static JDialog dialog;
private static Component oldGlassPane;
public static void main(String[] args) {
try {
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
buildFrame();
buildDialog();
}
});
} catch (InvocationTargetException | InterruptedException e) {
e.printStackTrace();
}
}
protected static void buildDialog() {
dialog = new JDialog(frame);
dialog.getContentPane().add(new JTextField("This is the dialog"));
dialog.setLocationRelativeTo(frame);
dialog.setModal(true);
javax.swing.Timer t = new javax.swing.Timer(1000, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
dialog.dispose();
frame.setGlassPane(oldGlassPane);
oldGlassPane.setVisible(false);
}
});
t.setRepeats(false);
t.start();
dialog.pack();
dialog.setVisible(true);
}
protected static void buildFrame() {
frame = new JFrame();
frame.setMinimumSize(new Dimension(400, 400));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new JTextField("This is the Frame"));
oldGlassPane = frame.getGlassPane();
frame.setGlassPane(new SplashGlassPane());
frame.getGlassPane().setVisible(true);
frame.pack();
frame.setVisible(true);
}
private static class SplashGlassPane extends JPanel implements FocusListener {
/** Holds the id of this panel. The creator of this object can submit this id to determine whether it's the owner of this object. */
private String typeId;
/**
* Creates new GlassPane.
*/
public SplashGlassPane() {
addMouseListener(new MouseAdapter() {});
addMouseMotionListener(new MouseAdapter() {});
addFocusListener(this);
setOpaque(false);
setFocusable(true);
setBackground(new Color(0, 0, 0, 100));
}
@Override
public final void setVisible(boolean v) {
// Make sure we grab the focus so that key events don't go astray.
if (v) {
requestFocus();
}
super.setVisible(v);
}
// Once we have focus, keep it if we're visible
@Override
public final void focusLost(FocusEvent fe) {
if (isVisible()) {
requestFocus();
}
}
/**
* {@inheritDoc}
*/
@Override
public final void paint(Graphics g) {
final Color old = g.getColor();
g.setColor(getBackground());
g.fillRect(0, 0, getSize().width, getSize().height);
g.setColor(old);
super.paint(g);
}
@Override
public void focusGained(FocusEvent fe) {
// nothing to do
}
}
}