Como trazer uma janela para a frente?

90

Temos um aplicativo Java que precisa ser colocado em primeiro plano quando um mecanismo de telecontrole ativa algo no aplicativo.

Para conseguir isso, percebemos no método chamado da classe que representa a estrutura de nossa aplicação (extensão de a JFrame) após a implementação:

setVisible(true);
toFront();

No Windows XP isso funciona na primeira vez que é chamado, na segunda vez apenas a guia da barra de tarefas pisca, o quadro não vem mais para a frente. O mesmo vale para Win2k. No Vista parece funcionar bem.

Você tem alguma ideia?

boutta
fonte
você tem uma amostra para este comportamento?
OscarRyz
3
A resposta adequada é ligar toFront()para o EDT usando invokeLater. Há uma resposta simples incluída abaixo, mas não é a resposta aceita. Funciona, no entanto. Perfeitamente.
Erick Robertson
Eu sei que isso é antigo, mas isso também acontece no OSX
ferdil
Estou tendo esse problema, mas nenhuma das respostas abaixo parece resolvê-lo. Tenho certeza de que é causado por janelas que não me permitem 'roubar' o foco para minha primeira janela no aplicativo.
Craig Warren

Respostas:

69

Uma possível solução é:

java.awt.EventQueue.invokeLater(new Runnable() {
    @Override
    public void run() {
        myFrame.toFront();
        myFrame.repaint();
    }
});
sth
fonte
8
Talvez devamos iniciar TODO o código da IU dentro de invokeLater em primeiro lugar? ;)
java.is.for.desktop.indeed
2
Não funcionou para mim no Java 7 no KDE 4.9.5, a janela ainda se escondia abaixo de outros programas. O que me ajudou foi mudar a ordem de trazer as janelas para a frente. Em vez de ocultar uma janela e mostrar a segunda janela, mostre a segunda janela e depois oculte a primeira janela (JFrame).
Lekensteyn
1
Funciona com Windows 10 executando Java 1.8 em um miniaplicativo
Elliott
Qual seria o método inverso?
Cardeal - Reintegrar Monica em
33

Eu tive o mesmo problema ao trazer um JFramepara a frente no Ubuntu (Java 1.6.0_10). E a única maneira de resolver isso é fornecendo um WindowListener. Especificamente, tive que definir meu JFramepara ficar sempre no topo sempre que toFront()for invocado e fornecer windowDeactivatedum manipulador de eventos para setAlwaysOnTop(false).


Portanto, aqui está o código que pode ser colocado em uma base JFrame, que é usado para derivar todos os quadros de aplicativos.

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

Sempre que seu porta-retratos deve ser exibido ou colocado em primeiro plano frame.setVisible(true).

Desde que mudei para o Ubuntu 9.04, parece não haver necessidade de ter um WindowListenerpara invocar super.setAlwaysOnTop(false)- como pode ser observado; este código foi movido para os métodos toFront()e setVisible().

Observe que o método setVisible()sempre deve ser chamado em EDT.

01es
fonte
Obrigado! Esta pergunta também está relacionada: stackoverflow.com/questions/2315560/…
rogerdpack
Ele não compila por mim por causa do método setDisposed (). Não foi encontrado.
ka3ak
1
@ ka3ak Este é um setter protegido que poderia ser introduzido na classe base JFrame sugerida para rastrear a situação com o quadro sendo descartado. O método dispose () precisaria ser substituído com uma chamada para setDisposed (true). Isso não é estritamente necessário para todos.
01 de
1
A .setAlwaysOnTop(true);era o único que funcionou para mim quando se utiliza um JWindow.
DGolberg
setAlwaysOnTop(true)é a única maneira de executá-lo no Windows 10 - obrigado!
Hartmut P.
22

O Windows tem a facilidade de evitar que as janelas roubem o foco; em vez disso, ele pisca o ícone da barra de tarefas. No XP, ele está ativado por padrão (o único lugar que vi para alterá-lo é usando o TweakUI, mas há uma configuração de registro em algum lugar). No Vista, eles podem ter alterado o padrão e / ou exposto como uma configuração acessível ao usuário com a interface do usuário pronta para usar.

Evitar que as janelas se forcem para a frente e obtenham o foco é um recurso desde o Windows 2K (e eu, por exemplo, sou grato por isso).

Dito isso, tenho um pequeno aplicativo Java que uso para me lembrar de registrar minhas atividades enquanto trabalho e torna-se a janela ativa a cada 30 minutos (configurável, claro). Ele sempre funciona de forma consistente no Windows XP e nunca pisca a janela da barra de título. Ele usa o seguinte código, chamado no thread de IU como resultado de um evento de timer disparado:

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

(a primeira linha é restaurada se minimizada ... na verdade ela restauraria se maximizada também, mas eu nunca fiz isso).

Embora eu geralmente tenha este aplicativo minimizado, muitas vezes ele está simplesmente atrás do meu editor de texto. E, como eu disse, sempre funciona.

Tenho uma ideia de qual poderia ser o seu problema - talvez você tenha uma condição de corrida com a chamada setVisible (). toFront () pode não ser válido a menos que a janela seja realmente exibida quando for chamada; Eu tive esse problema com requestFocus () antes. Você pode precisar colocar a chamada toFront () em um ouvinte da IU em um evento ativado por janela.

07-09-2014: Em algum ponto no tempo, o código acima parou de funcionar, talvez em Java 6 ou 7. Depois de alguma investigação e experimentação, tive que atualizar o código para substituir o toFrontmétodo da janela, fazer isso (em conjunto com o código modificado de que está acima):

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 do Java 8_20, esse código parece estar funcionando bem.

Lawrence Dol
fonte
1
+1 para não permitir que as janelas roubem o foco. Odeio quando isso acontece quando estou digitando um documento.
Ken Paul
1
Concordo totalmente com você contra roubar o foco, mas, neste caso preciso, o usuário espera que o aplicativo venha para a frente. Mas não seria legal alterar as configurações do registro e alterar o comportamento completo do Windows.
boutta
Estou supondo super.setAlwaysOnTop(false);que a janela nem sempre fica para cima, o que é necessário para nos livrarmos do trueque configuramos anteriormente para trazer a janela para a frente, correto? Estou perguntando porque com o seu código a janela ainda está sempre no topo no meu caso, o que eu obviamente não quero. Executando jre1.8.0_66 no Windows 10.
Bram Vanroy
@Bram: Sim, está correto. Estou rodando o código na mesma versão do Java e do Windows e nem sempre acaba em cima das outras janelas. Pode não ser necessário definir sempre no topo, mas acho que caso contrário, o Windows apenas pisca a barra de título, pelo menos sob algumas condições.
Lawrence Dol
Hm, estranho. Você poderia dar uma olhada em uma pergunta semelhante na qual vinculo a esta resposta? Talvez esse código mostre mais claramente o problema: stackoverflow.com/questions/34637597/…
Bram Vanroy
11

Aqui está um método que REALMENTE funciona (testado no Windows Vista): D

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

A variável fullscreen indica se você deseja que o aplicativo seja executado em tela inteira ou em janela.

Isso não pisca a barra de tarefas, mas traz a janela para a frente de forma confiável.

Stefan Reich
fonte
Obrigado pela dica setExtendedState. Usei-o junto com as soluções toFront () e repaint () para trazer a janela para o primeiro plano, mesmo se ela foi minimizada.
roubo
1
Confirmado: esta solução funciona em WindowsXP, usando toFront resulta em mensagem intermitente na barra de tarefas. Obrigado!
Eric Lindauer
5

Hj, todos os seus métodos não estão funcionando para mim, no Fedora KDE 14. Eu tenho uma maneira suja de trazer uma janela para a frente, enquanto esperamos que o Oracle conserte esse 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 isso funciona perfeitamente no meu Fedora KDE 14 :-)


fonte
Um pouco hacky, funciona para nós, mas apenas para a primeira chamada :-). (Kubuntu 12.04) - outra solução falhou
usuário85155
Esta foi a única solução que funcionou para mim (Windows Server 2012 R2) para um problema em que um JFrame (login) é aberto, mas não tem foco até que o usuário clique nele.
Glenneroo
4

Este método simples funcionou perfeitamente para mim no Windows 7:

    private void BringToFront() {
        java.awt.EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                if(jFrame != null) {
                    jFrame.toFront();
                    jFrame.repaint();
                }
            }
        });
    }
Sr. Ed
fonte
2
O repaint()não é necessário, o invokeLater()fez. Obrigado.
Matthieu
4

Testei suas respostas e apenas a de Stefan Reich funcionou para mim. Embora eu não tenha conseguido restaurar a janela ao seu estado anterior (maximizada / normal). Eu achei esta mutação melhor:

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

Isso é em setStatevez de setExtendedState.

Jarekczek
fonte
3

A maneira mais simples que descobri que não tem inconsistência entre as plataformas:

setVisible (false); setVisible (true);

Rico
fonte
1
causa alguns piscadelas, não é? agradável e simples :)
rogerdpack
não funcionou para o meu processo em segundo plano. Além disso, a janela fica branca para a primeira atualização, se chamada do processo de primeiro plano. Não pode ser usado para capturas de tela.
DragonLord
piscar pode ser evitado verificando se a janela está
iconizada
2

As regras que regem o que acontece quando você .toFront () um JFrame são as mesmas no Windows e no Linux:

-> se uma janela do aplicativo existente for atualmente a janela em foco, então o foco muda para a janela solicitada -> se não, a janela simplesmente pisca na barra de tarefas

MAS :

-> novas janelas obtêm foco automaticamente

Então, vamos explorar isso! Você quer trazer uma janela para a frente, como fazer? Bem :

  1. Crie uma janela vazia sem finalidade
  2. Mostre
  3. Espere até que apareça na tela (setVisible faz isso)
  4. Quando mostrado, solicite o foco para a janela para a qual você realmente deseja trazer o foco
  5. esconda a janela vazia, destrua-a

Ou, em 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óvão
fonte
Não funcionou no Win7, ambas as janelas piscam (se eu não esconder a 2ª).
NateS de
Criativo. Não funcionou para o meu processo em segundo plano no Win7, quando coberto. O novo quadro não surge por cima. JDK 6u21 mais antigo.
DragonLord
0

Existem várias advertências no javadoc para o método toFront () que podem estar causando o problema.

Mas vou adivinhar de qualquer maneira, quando "apenas a guia na barra de tarefas piscar", o aplicativo foi minimizado? Nesse caso, a seguinte linha do javadoc pode ser aplicada:

"Se esta janela estiver visível, traz esta janela para a frente e pode torná-la a janela em foco."

Brendan Cashman
fonte
0

Para evitar que a janela perca o foco ao voltar a ficar visível após ser escondida, tudo o que é necessário é:

setExtendedState(JFrame.NORMAL);

Igual a:

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