Come portare una finestra in primo piano?


90

Abbiamo un'applicazione Java che deve essere portata in primo piano quando un meccanismo di telecontrollo attiva qualcosa nell'applicazione.

Per ottenere ciò, abbiamo realizzato nel metodo chiamato della classe che rappresenta il frame della nostra applicazione (estensione di a JFrame) seguente implementazione:

setVisible(true);
toFront();

In Windows XP, funziona la prima volta che viene richiamato, la seconda volta lampeggia solo la scheda nella barra delle applicazioni, la cornice non viene più in primo piano. Lo stesso vale per Win2k. Su Vista sembra funzionare bene.

Hai qualche idea?


hai un campione per questo comportamento?
OscarRyz

3
La risposta corretta è chiamare toFront()l'EDT utilizzando invokeLater. Di seguito è inclusa una risposta semplice, ma non è la risposta accettata. Però funziona. Perfettamente.
Erick Robertson

So che è vecchio, ma succede anche su OSX
ferdil

Sto riscontrando questo problema, ma nessuna delle risposte seguenti sembra risolverlo. Sono sicuro che sia causato da Windows che non mi consente di "rubare" il focus per la mia prima finestra nell'applicazione.
Craig Warren

Risposte:


69

Una possibile soluzione è:

java.awt.EventQueue.invokeLater(new Runnable() {
    @Override
    public void run() {
        myFrame.toFront();
        myFrame.repaint();
    }
});

8
Forse si dovrebbe avviare TUTTO il codice dell'interfaccia utente all'interno di invokeLater in primo luogo? ;)
java.is.for.desktop.indeed

2
Non ha funzionato per me in Java 7 su KDE 4.9.5, la finestra sarebbe ancora nascosta sotto altri programmi. Ciò che mi ha aiutato è stato cambiare l'ordine di portare le finestre in primo piano. Invece di nascondere una finestra e mostrare la seconda finestra, mostra la seconda finestra e poi nascondi la prima finestra (JFrame).
Lekensteyn

1
Funziona con Windows 10 che esegue Java 1.8 in un'applet
Elliott

Quale sarebbe il metodo inverso?
Cardinale - Ripristina Monica il

33

Ho avuto lo stesso problema portando a JFramein primo piano in Ubuntu (Java 1.6.0_10). E l'unico modo per risolverlo è fornire un file WindowListener. In particolare, ho dovuto impostare my JFramein modo che rimanga sempre in primo piano ogni volta che toFront()viene invocato e fornire un windowDeactivatedgestore di eventi a setAlwaysOnTop(false).


Quindi, ecco il codice che potrebbe essere inserito in una base JFrame, che viene utilizzato per derivare tutti i frame dell'applicazione.

@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);
}

Ogni volta che la tua cornice deve essere visualizzata o portata in prima chiamata frame.setVisible(true).

Da quando sono passato a Ubuntu 9.04 non sembra esserci bisogno di avere un WindowListenerinvocare super.setAlwaysOnTop(false)- come si può osservare; questo codice è stato spostato nei metodi toFront()e setVisible().

Si noti che il metodo setVisible()deve essere sempre invocato su EDT.


Grazie! Anche in relazione a questa domanda è: stackoverflow.com/questions/2315560/...
rogerdpack

Non viene compilato da me a causa del metodo setDisposed (). Non può essere trovato
ka3ak

1
@ ka3ak Questo è un setter protetto che potrebbe essere introdotto nella classe JFrame-base suggerita per tenere traccia della situazione con il frame che viene eliminato. Il metodo dispose () dovrebbe essere sovrascritto con una chiamata a setDisposed (true). Questo non è strettamente necessario per tutti.
01

1
L' .setAlwaysOnTop(true);unico che ha funzionato per me quando si utilizza un JWindow.
DGolberg

setAlwaysOnTop(true)è l'unico modo per farlo funzionare sotto Windows 10 - grazie!
Hartmut P.

22

Windows ha la funzione di impedire alle finestre di rubare il focus; invece lampeggia l'icona sulla barra delle applicazioni. In XP è attivo per impostazione predefinita (l'unico posto che ho visto per cambiarlo è usare TweakUI, ma c'è un'impostazione del registro da qualche parte). In Vista potrebbero aver modificato l'impostazione predefinita e / o mostrata come un'impostazione accessibile all'utente con l'interfaccia utente predefinita.

Impedire alle finestre di forzarsi in primo piano e di concentrarsi è una funzionalità da Windows 2K (e io, per esempio, ne sono grato).

Detto questo, ho una piccola app Java che uso per ricordarmi di registrare le mie attività mentre lavoro, e si rende la finestra attiva ogni 30 minuti (configurabile, ovviamente). Funziona sempre in modo coerente con Windows XP e non lampeggia mai la finestra della barra del titolo. Utilizza il codice seguente, chiamato nel thread dell'interfaccia utente in seguito all'attivazione di un evento timer:

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

(la prima riga ripristina se minimizzata ... effettivamente la ripristinerebbe anche se ingrandita, ma non l'ho mai così).

Sebbene di solito questa app sia ridotta a icona, molto spesso è semplicemente dietro il mio editor di testo. E, come ho detto, funziona sempre.

Ho un'idea su quale potrebbe essere il tuo problema - forse hai una condizione di gara con la chiamata setVisible (). toFront () potrebbe non essere valido a meno che la finestra non sia effettivamente visualizzata quando viene chiamata; Ho già avuto questo problema con requestFocus (). Potrebbe essere necessario inserire la chiamata toFront () in un listener dell'interfaccia utente su un evento attivato dalla finestra.

07/09/2014: Ad un certo punto nel tempo il codice sopra ha smesso di funzionare, forse in Java 6 o 7. Dopo alcune indagini e sperimentazioni ho dovuto aggiornare il codice per sovrascrivere il toFrontmetodo della finestra (insieme al codice modificato da cosa è sopra):

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 partire da Java 8_20, questo codice sembra funzionare correttamente.


1
+1 per il supporto che non consente alle finestre di rubare il focus. Odio quando ciò accade quando sto digitando un documento.
Ken Paul,

1
Sono completamente d'accordo con te contro il furto del focus, ma in questo caso preciso l'utente si aspetta che l'applicazione venga in primo piano. Ma non sarebbe bello modificare le impostazioni del registro e modificare il comportamento completo di Windows.
circa il

Immagino che super.setAlwaysOnTop(false);sia così che la finestra non sia sempre in primo piano, il che è necessario per eliminare ciò che trueabbiamo impostato in precedenza per portare la finestra in primo piano, giusto? Te lo chiedo perché con il tuo codice la finestra è ancora sempre in primo piano nel mio caso, cosa che ovviamente non voglio. Esecuzione di jre1.8.0_66 su Windows 10.
Bram Vanroy

@Bram: Sì, è corretto. Sto eseguendo il codice sulla stessa versione di Java e Windows e non finisce sempre sopra le altre finestre. Potrebbe non essere necessario impostare sempre in primo piano, ma penso che altrimenti Windows lampeggi semplicemente la barra del titolo, almeno in alcune condizioni.
Lawrence Dol,

Hm, strano. Potresti dare un'occhiata a una domanda simile a cui mi collego a questa risposta? Forse che mostra codice più chiaramente il problema: stackoverflow.com/questions/34637597/...
Bram Vanroy

11

Ecco un metodo che funziona DAVVERO (testato su Windows Vista): D

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

La variabile a schermo intero indica se desideri che l'app venga eseguita a schermo intero o in finestra.

Questo non fa lampeggiare la barra delle applicazioni, ma porta la finestra in primo piano in modo affidabile.


Grazie per il suggerimento setExtendedState. L'ho usato insieme alle soluzioni toFront () e repaint () per portare la finestra in primo piano anche se era ridotta a icona.
rapina il

1
Confermato: questa soluzione funziona in Windows XP, utilizzando toFront si ottiene un messaggio lampeggiante nella barra delle applicazioni. Grazie!
Eric Lindauer

5

Hj, tutti i tuoi metodi non funzionano per me, in Fedora KDE 14. Ho un modo sporco per portare una finestra in primo piano, mentre aspettiamo che Oracle risolva questo 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);
      }
    }
  }

  //...

}

E questo funziona perfettamente nella mia Fedora KDE 14 :-)


Un po 'hacky, funziona per noi, ma solo per la prima chiamata :-). (Kubuntu 12.04) - altra soluzione non riuscita
user85155

Questa era l'unica soluzione che ha funzionato per me (Windows Server 2012 R2) per un problema in cui un JFrame (login) è aperto ma non è attivo finché l'utente non fa clic su di esso.
glenneroo

4

Questo semplice metodo ha funzionato perfettamente per me in Windows 7:

    private void BringToFront() {
        java.awt.EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                if(jFrame != null) {
                    jFrame.toFront();
                    jFrame.repaint();
                }
            }
        });
    }

2
Non repaint()è necessario, lo ha invokeLater()fatto. Grazie.
Matthieu

4

Ho testato le tue risposte e solo quella di Stefan Reich ha funzionato per me. Anche se non sono riuscito a ripristinare la finestra al suo stato precedente (ingrandito / normale). Ho trovato meglio questa mutazione:

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

Questo è setState invece di setExtendedState.


3

Il modo più semplice che ho scoperto che non presenta incongruenze tra le piattaforme:

setVisible (false); setVisible (true);


1
provoca qualche battito di ciglia, non è vero? bello e semplice però :)
rogerdpack

non ha funzionato per il mio processo in background. Anche la finestra appare bianca per il primo aggiornamento se chiamata dal processo in primo piano. Non può essere utilizzato per catture di schermate.
DragonLord

lampeggiare può essere evitato controllando se la finestra è iconizzata o meno
totaam

2

Le regole che governano cosa succede quando si .toFront () un JFrame sono le stesse in Windows e in Linux:

-> se una finestra dell'applicazione esistente è attualmente la finestra focalizzata, lo stato attivo passa alla finestra richiesta -> in caso contrario, la finestra lampeggia semplicemente nella barra delle applicazioni

MA :

-> le nuove finestre si concentrano automaticamente

Quindi sfruttiamo questo! Vuoi portare una finestra in primo piano, come si fa? Bene :

  1. Crea una finestra senza scopo vuota
  2. Mostralo
  3. Aspetta che venga visualizzato sullo schermo (setVisible lo fa)
  4. Quando viene visualizzato, richiedere lo stato attivo per la finestra su cui si desidera effettivamente portare lo stato attivo
  5. nascondi la finestra vuota, distruggila

Oppure, in codice 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);
  }
});

Non ha funzionato su Win7, entrambe le finestre lampeggiano (se non nascondo la seconda).
NateS

Creativo. Non ha funzionato per il mio processo in background su Win7, quando coperto. Il nuovo telaio non si trova in cima. Vecchi JDK 6u21.
DragonLord

0

Ci sono numerosi avvertimenti nel javadoc per il metodo toFront () che potrebbero causare il tuo problema.

Ma cercherò comunque di indovinare, quando "lampeggia solo la scheda nella barra delle applicazioni", l'applicazione è stata ridotta a icona? In tal caso, potrebbe essere applicata la seguente riga del javadoc:

"Se questa finestra è visibile, porta questa finestra in primo piano e può renderla la finestra focalizzata."


0

Per evitare che la finestra perda il focus quando ritorna visibile dopo essere stata nascosta, tutto ciò che serve è:

setExtendedState(JFrame.NORMAL);

Così:

defaultItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                showWindow();
                setExtendedState(JFrame.NORMAL);
            }
});
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.