¿Cómo llevar una ventana al frente?

90

Tenemos una aplicación Java que se debe traer a un primer plano cuando un mecanismo de telecontrol activa algo en la aplicación.

Para ello, nos hemos dado cuenta en el método llamado de la clase que representa el marco de nuestra aplicación (extensión de a JFrame) siguiente implementación:

setVisible(true);
toFront();

En Windows XP, esto funciona la primera vez que se llama, la segunda vez solo parpadea la pestaña en la barra de tareas, el marco ya no aparece al frente. Lo mismo ocurre con Win2k. En Vista, parece funcionar bien.

¿Tienes alguna idea?

boutta
fuente
¿tiene una muestra de este comportamiento?
OscarRyz
3
La respuesta adecuada es llamar toFront()al EDT usando invokeLater. A continuación se incluye una respuesta simple, pero no es la respuesta aceptada. Sin embargo, funciona. Perfectamente.
Erick Robertson
Sé que esto es antiguo, pero esto también sucede en OSX
ferdil
Estoy experimentando este problema, pero ninguna de las respuestas a continuación parece resolverlo. Estoy seguro de que se debe a que Windows no me permite 'Robar' el foco para mi primera ventana en la aplicación.
Craig Warren

Respuestas:

69

Una posible solución es:

java.awt.EventQueue.invokeLater(new Runnable() {
    @Override
    public void run() {
        myFrame.toFront();
        myFrame.repaint();
    }
});
algo
fuente
8
¿Quizás uno debería iniciar TODO el código de la interfaz de usuario dentro de invokeLater en primer lugar? ;)
java.is.for.desktop.indeed
2
No funcionó para mí en Java 7 en KDE 4.9.5, la ventana aún se ocultaba debajo de otros programas. Lo que me ayudó fue cambiar el orden de llevar las ventanas al frente. En lugar de ocultar una ventana y mostrar la segunda ventana, muestre la segunda ventana y luego oculte la primera ventana (JFrame).
Lekensteyn
1
Funciona con Windows 10 que ejecuta Java 1.8 en un subprograma
Elliott
¿Cuál sería el método inverso?
Cardenal - Reincorpora a Monica
33

Tuve el mismo problema al traer un JFrameal frente en Ubuntu (Java 1.6.0_10). Y la única forma en que podría resolverlo es proporcionando un WindowListener. Específicamente, tuve que configurar mi JFramepara que permaneciera siempre en la parte superior cada vez que toFront()se invocaba y proporcionar un windowDeactivatedcontrolador de eventos a setAlwaysOnTop(false).


Entonces, aquí está el código que podría colocarse en una base JFrame, que se usa para derivar todos los marcos de la aplicación.

@Override
public void setVisible(final boolean visible) {
  // make sure that frame is marked as not disposed if it is asked to be visible
  if (visible) {
      setDisposed(false);
  }
  // let's handle visibility...
  if (!visible || !isVisible()) { // have to check this condition simply because super.setVisible(true) invokes toFront if frame was already visible
      super.setVisible(visible);
  }
  // ...and bring frame to the front.. in a strange and weird way
  if (visible) {
      toFront();
  }
}

@Override
public void toFront() {
  super.setVisible(true);
  int state = super.getExtendedState();
  state &= ~JFrame.ICONIFIED;
  super.setExtendedState(state);
  super.setAlwaysOnTop(true);
  super.toFront();
  super.requestFocus();
  super.setAlwaysOnTop(false);
}

Siempre que su marco deba mostrarse o llevarse al frente, llame frame.setVisible(true).

Desde que me mudé a Ubuntu 9.04, parece que no hay necesidad de tener una WindowListenerinvocación super.setAlwaysOnTop(false), como se puede observar; este código se movió a los métodos toFront()y setVisible().

Tenga en cuenta que el método setVisible()siempre debe invocarse en EDT.

01es
fuente
¡Gracias! También se relaciona esta pregunta: stackoverflow.com/questions/2315560/…
rogerdpack
No lo compilo debido al método setDisposed (). No se puede encontrar.
ka3ak
1
@ ka3ak Este es un establecedor protegido que podría introducirse en la clase base JFrame sugerida para rastrear la situación con el marco que se está eliminando. El método dispose () debería anularse con una llamada a setDisposed (true). Esto no es estrictamente necesario para todos.
01es
1
El .setAlwaysOnTop(true);era el único que trabajó para mí cuando se utiliza un JWindow.
DGolberg
setAlwaysOnTop(true)es la única forma en que lo hago funcionar bajo Windows 10, ¡gracias!
Hartmut P.
22

Windows tiene la capacidad de evitar que las ventanas roben el foco; en su lugar, parpadea el icono de la barra de tareas. En XP está activado por defecto (el único lugar que he visto para cambiarlo es usando TweakUI, pero hay una configuración de registro en alguna parte). En Vista, es posible que hayan cambiado el valor predeterminado y / o lo hayan expuesto como una configuración accesible para el usuario con la interfaz de usuario lista para usar.

Evitar que las ventanas se fuercen al frente y se enfoquen es una característica desde Windows 2K (y yo, por mi parte, estoy agradecido por ello).

Dicho esto, tengo una pequeña aplicación de Java que uso para recordarme que registre mis actividades mientras trabajo, y se convierte en la ventana activa cada 30 minutos (configurable, por supuesto). Siempre funciona de manera uniforme en Windows XP y nunca muestra la ventana de la barra de título. Utiliza el siguiente código, llamado en el hilo de la IU como resultado de la activación de un evento de temporizador:

if(getState()!=Frame.NORMAL) { setState(Frame.NORMAL); }
toFront();
repaint();

(la primera línea se restaura si se minimiza ... en realidad la restauraría si se maximizara también, pero nunca la tengo así).

Si bien normalmente tengo esta aplicación minimizada, a menudo está simplemente detrás de mi editor de texto. Y, como dije, siempre funciona.

Tengo una idea de cuál podría ser su problema; tal vez tenga una condición de carrera con la llamada setVisible (). toFront () puede no ser válido a menos que la ventana se muestre realmente cuando se llama; He tenido este problema con requestFocus () antes. Es posible que deba poner la llamada toFront () en un detector de IU en un evento activado por ventana.

2014-09-07: En algún momento, el código anterior dejó de funcionar, tal vez en Java 6 o 7. Después de investigar y experimentar, tuve que actualizar el código para anular el toFrontmétodo de la ventana para hacer esto (junto con el código modificado de lo que está arriba):

setVisible(true);
toFront();
requestFocus();
repaint();

...

public @Override void toFront() {
    int sta = super.getExtendedState() & ~JFrame.ICONIFIED & JFrame.NORMAL;

    super.setExtendedState(sta);
    super.setAlwaysOnTop(true);
    super.toFront();
    super.requestFocus();
    super.setAlwaysOnTop(false);
}

A partir de Java 8_20, este código parece funcionar bien.

Lawrence Dol
fuente
1
+1 por no permitir que las ventanas roben el foco. Odio cuando eso sucede cuando estoy escribiendo un documento.
Ken Paul
1
Estoy completamente de acuerdo con usted en contra de robar el foco, pero en este caso concreto el usuario espera que la aplicación pase al frente. Pero no sería genial cambiar la configuración del registro y cambiar el comportamiento completo de Windows.
Combate el
Supongo que super.setAlwaysOnTop(false);es para que la ventana no siempre esté en la parte superior, lo cual es necesario para deshacerse de lo trueque configuramos anteriormente para llevar la ventana al frente, ¿correcto? Lo pregunto porque con su código, la ventana todavía está siempre arriba en mi caso, lo que obviamente no quiero. Ejecutando jre1.8.0_66 en Windows 10.
Bram Vanroy
@Bram: Sí, eso es correcto. Estoy ejecutando el código en la misma versión de Java y Windows y no siempre termina encima de otras ventanas. Puede que no sea necesario establecer siempre en la parte superior, pero creo que, de lo contrario, Windows simplemente muestra la barra de título, al menos en algunas condiciones.
Lawrence Dol
Hm, extraño. ¿Podrías echar un vistazo a una pregunta similar en la que enlazo a esta respuesta? Tal vez ese código muestra más claramente el problema: stackoverflow.com/questions/34637597/…
Bram Vanroy
11

Aquí hay un método que REALMENTE funciona (probado en Windows Vista): D

   frame.setExtendedState(JFrame.ICONIFIED);
   frame.setExtendedState(fullscreen ? JFrame.MAXIMIZED_BOTH : JFrame.NORMAL);

La variable de pantalla completa indica si desea que la aplicación se ejecute en pantalla completa o en ventana.

Esto no hace parpadear la barra de tareas, pero trae la ventana al frente de manera confiable.

Stefan Reich
fuente
Gracias por el consejo de setExtendedState. Lo usé junto con la solución toFront () y repaint () para traer la ventana al primer plano incluso si estaba minimizada.
robar el
1
Confirmado: esta solución funciona en Windows XP, el uso de toFront da como resultado un mensaje parpadeante en la barra de tareas. ¡Gracias!
Eric Lindauer
5

Hj, todos sus métodos no funcionan para mí, en Fedora KDE 14. Tengo una forma sucia de traer una ventana al frente, mientras esperamos que Oracle solucione este problema.

import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Robot;
import java.awt.event.InputEvent;

public class FrameMain extends javax.swing.JFrame {

  //...
  private final javax.swing.JFrame mainFrame = this;

  private void toggleVisible() {
    setVisible(!isVisible());
    if (isVisible()) {
      toFront();
      requestFocus();
      setAlwaysOnTop(true);
      try {
        //remember the last location of mouse
        final Point oldMouseLocation = MouseInfo.getPointerInfo().getLocation();

        //simulate a mouse click on title bar of window
        Robot robot = new Robot();
        robot.mouseMove(mainFrame.getX() + 100, mainFrame.getY() + 5);
        robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
        robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);

        //move mouse to old location
        robot.mouseMove((int) oldMouseLocation.getX(), (int) oldMouseLocation.getY());
      } catch (Exception ex) {
        //just ignore exception, or you can handle it as you want
      } finally {
        setAlwaysOnTop(false);
      }
    }
  }

  //...

}

Y esto funciona perfectamente en mi Fedora KDE 14 :-)


fuente
Un poco de hacky, funciona para nosotros, pero solo para la primera llamada :-). (Kubuntu 12.04) - otra solución falló
user85155
Esta fue la única solución que funcionó para mí (Windows Server 2012 R2) para un problema en el que se abre un JFrame (inicio de sesión) pero no se enfoca hasta que el usuario hace clic en él.
glenneroo
4

Este método simple funcionó para mí perfectamente en Windows 7:

    private void BringToFront() {
        java.awt.EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                if(jFrame != null) {
                    jFrame.toFront();
                    jFrame.repaint();
                }
            }
        });
    }
Sr. Ed
fuente
2
No repaint()es necesario, lo invokeLater()hizo. Gracias.
Matthieu
4

Probé tus respuestas y solo la de Stefan Reich funcionó para mí. Aunque no pude restaurar la ventana a su estado anterior (maximizada / normal). Encontré mejor esta mutación:

view.setState(java.awt.Frame.ICONIFIED);
view.setState(java.awt.Frame.NORMAL);

Eso es en setStatelugar de setExtendedState.

Jarekczek
fuente
3

La forma más sencilla que he encontrado que no tiene inconsistencias entre plataformas:

setVisible (falso); setVisible (verdadero);

Rico
fuente
1
causa algunos parpadeos, ¿no es así? aunque agradable y simple :)
rogerdpack
no funcionó para mi proceso de antecedentes. También la ventana aparece en blanco para la primera actualización si se llama desde el proceso de primer plano. No se puede usar para capturas de pantalla.
DragonLord
el parpadeo se puede evitar comprobando si la ventana está iconificada o no
totaam
2

Las reglas que rigen lo que sucede cuando .toFront () un JFrame son las mismas en Windows y en Linux:

-> si una ventana de la aplicación existente es actualmente la ventana enfocada, entonces el enfoque cambia a la ventana solicitada -> si no, la ventana simplemente parpadea en la barra de tareas

PERO :

-> las nuevas ventanas se enfocan automáticamente

¡Explotemos esto! Quieres traer una ventana al frente, ¿cómo hacerlo? Bien :

  1. Crea una ventana vacía sin propósito
  2. Enséñalo
  3. Espere a que aparezca en la pantalla (setVisible lo hace)
  4. Cuando se muestre, solicite el enfoque para la ventana a la que realmente desea llevar el enfoque
  5. esconde la ventana vacía, destrúyela

O, en código java:

// unminimize if necessary
this.setExtendedState(this.getExtendedState() & ~JFrame.ICONIFIED);

// don't blame me, blame my upbringing
// or better yet, blame java !
final JFrame newFrame = new JFrame();
newFrame.add(new JLabel("boembabies, is this in front ?"));

newFrame.pack();
newFrame.setVisible(true);
newFrame.toFront();

this.toFront();
this.requestFocus();

// I'm not 100% positive invokeLater is necessary, but it seems to be on
// WinXP. I'd be lying if I said I understand why
SwingUtilities.invokeLater(new Runnable() {
  @Override public void run() {
    newFrame.setVisible(false);
  }
});
cristóbal
fuente
No funcionó en Win7, ambas ventanas parpadean (si no oculto la segunda).
NateS
Creativo. No funcionó para mi proceso en segundo plano en Win7, cuando estaba cubierto. El nuevo marco no sube por encima. JDK 6u21.
DragonLord
0

Existen numerosas advertencias en el javadoc para el método toFront () que pueden estar causando su problema.

Pero, de todos modos, haré una conjetura, cuando "solo parpadee la pestaña de la barra de tareas", ¿se ha minimizado la aplicación? Si es así, se puede aplicar la siguiente línea del javadoc:

"Si esta ventana es visible, trae esta ventana al frente y puede convertirla en la ventana enfocada".

Brendan Cashman
fuente
0

Para evitar que la ventana pierda el foco cuando vuelva a ser visible después de estar oculta, todo lo que se necesita es:

setExtendedState(JFrame.NORMAL);

Al igual que:

defaultItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                showWindow();
                setExtendedState(JFrame.NORMAL);
            }
});
Martin Sansone - MiOEE
fuente