Làm thế nào để đưa một cửa sổ ra phía trước?


90

Chúng tôi có một ứng dụng Java cần được đưa lên nền tảng khi một cơ chế điều khiển từ xa kích hoạt một cái gì đó trong ứng dụng.

Để có được điều này, chúng tôi đã nhận ra trong phương thức được gọi của lớp đại diện cho khung của ứng dụng của chúng tôi (phần mở rộng của a JFrame) triển khai sau:

setVisible(true);
toFront();

Trong Windows XP, tính năng này hoạt động ngay lần đầu tiên được gọi, vào lần thứ hai, chỉ tab trên thanh tác vụ nhấp nháy, khung không xuất hiện ở phía trước nữa. Tương tự với Win2k. Trên Vista, nó hoạt động tốt.

Bạn có bất cứ ý tưởng?


bạn có một mẫu cho hành vi này?
OscarRyz

3
Câu trả lời thích hợp là gọi toFront()EDT bằng cách sử dụng invokeLater. Có một câu trả lời đơn giản được bao gồm bên dưới, nhưng nó không phải là câu trả lời được chấp nhận. Tuy nhiên, nó hoạt động. Hoàn hảo.
Erick Robertson

Tôi biết điều này là cũ, nhưng điều này cũng xảy ra trên OSX
ferdil

Tôi đang gặp sự cố này, nhưng không có câu trả lời nào dưới đây dường như giải quyết được nó. Tôi chắc chắn nguyên nhân là do cửa sổ không cho phép tôi 'Đánh cắp' Focus cho cửa sổ đầu tiên của tôi trong ứng dụng.
Craig Warren

Câu trả lời:


69

Một giải pháp khả thi là:

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

8
Có lẽ người ta nên bắt đầu TẤT CẢ mã giao diện người dùng bên trong invokeLater ngay từ đầu? ;)
java.is.for.desktop.indeed

2
Không hoạt động đối với tôi trong Java 7 trên KDE 4.9.5, cửa sổ sẽ vẫn ẩn bên dưới các chương trình khác. Điều gì đã giúp tôi thay đổi thứ tự đưa cửa sổ lên phía trước. Thay vì ẩn một cửa sổ và hiển thị cửa sổ thứ hai, hãy hiển thị cửa sổ thứ hai và sau đó ẩn cửa sổ đầu tiên (JFrame).
Lekensteyn

1
Hoạt động với Windows 10 chạy Java 1.8 trong một applet
Elliott

Phương pháp nghịch đảo sẽ là gì?
Đức Hồng y - Phục hồi Monica

33

Tôi đã gặp vấn đề tương tự khi đưa a JFramelên phía trước trong Ubuntu (Java 1.6.0_10). Và cách duy nhất tôi có thể giải quyết nó là cung cấp một WindowListener. Cụ thể, tôi đã phải đặt của mình JFrameđể luôn ở trên cùng bất cứ khi nào toFront()được gọi và cung cấp windowDeactivatedtrình xử lý sự kiện setAlwaysOnTop(false).


Vì vậy, đây là mã có thể được đặt vào một cơ sở JFrame, được sử dụng để lấy tất cả các khung ứng dụng.

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

Bất cứ khi nào khung của bạn sẽ được hiển thị hoặc chuyển sang cuộc gọi trước frame.setVisible(true).

Vì tôi đã chuyển sang Ubuntu 9.04 nên dường như không cần phải có lệnh WindowListenergọi super.setAlwaysOnTop(false)- như có thể thấy; mã này đã được chuyển đến các phương thức toFront()setVisible().

Xin lưu ý rằng phương thức setVisible()phải luôn được gọi trên EDT.


Cảm ơn! Cũng liên quan là câu hỏi này: stackoverflow.com/questions/2315560/...
rogerdpack

Nó không do tôi biên dịch vì phương thức setDisposed (). Không thể tìm thấy.
ka3ak

1
@ ka3ak Đây là bộ thiết lập được bảo vệ có thể được giới thiệu trong lớp cơ sở JFrame được đề xuất để theo dõi tình hình với khung được xử lý. Phương thức dispose () sẽ cần được ghi đè bằng lệnh gọi setDisposed (true). Điều này không hoàn toàn cần thiết cho tất cả mọi người.
01es

1
Cái .setAlwaysOnTop(true);duy nhất phù hợp với tôi khi sử dụng JWindow.
DGolberg

setAlwaysOnTop(true)là cách duy nhất để tôi chạy nó dưới windows 10 - cảm ơn!
Hartmut P.

22

Windows có khả năng ngăn chặn các cửa sổ lấy cắp tiêu điểm; thay vào đó nó sẽ nhấp nháy biểu tượng thanh tác vụ. Trong XP, nó được bật theo mặc định (nơi duy nhất tôi đã thấy để thay đổi nó là sử dụng TweakUI, nhưng có một cài đặt đăng ký ở đâu đó). Trong Vista, họ có thể đã thay đổi mặc định và / hoặc hiển thị nó như một cài đặt người dùng có thể truy cập được với giao diện người dùng out-of-box.

Ngăn không cho các cửa sổ tự ép mình ra phía trước và lấy nét là một tính năng kể từ Windows 2K (và tôi rất biết ơn vì điều đó).

Điều đó nói rằng, tôi có một ứng dụng Java nhỏ mà tôi sử dụng để nhắc nhở tôi ghi lại các hoạt động của tôi trong khi làm việc và nó tự đặt cửa sổ hoạt động sau mỗi 30 phút (tất nhiên là có thể định cấu hình). Nó luôn hoạt động nhất quán trong Windows XP và không bao giờ nhấp nháy cửa sổ thanh tiêu đề. Nó sử dụng mã sau, được gọi trong chuỗi giao diện người dùng do một sự kiện hẹn giờ kích hoạt:

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

(dòng đầu tiên khôi phục nếu được thu nhỏ ... thực ra nó cũng sẽ khôi phục nếu được tối đa hóa, nhưng tôi chưa bao giờ làm như vậy).

Mặc dù tôi thường thu nhỏ ứng dụng này, nhưng thường thì nó chỉ nằm sau trình soạn thảo văn bản của tôi. Và, như tôi đã nói, nó luôn hoạt động.

Tôi có một ý tưởng về vấn đề của bạn có thể là gì - có lẽ bạn có một điều kiện chạy đua với lệnh gọi setVible (). toFront () có thể không hợp lệ trừ khi cửa sổ thực sự được hiển thị khi nó được gọi; Tôi đã gặp sự cố này với requestFocus () trước đây. Bạn có thể cần đặt lệnh gọi toFront () trong một trình nghe UI trên một sự kiện được kích hoạt cửa sổ.

2014-09-07: Tại một số thời điểm mã ở trên ngừng hoạt động, có lẽ ở Java 6 hoặc 7. Sau một số điều tra và thử nghiệm, tôi đã phải cập nhật mã để ghi đè toFrontphương thức của cửa sổ thực hiện việc này (kết hợp với mã sửa đổi từ những gì ở trên):

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

Đối với Java 8_20, mã này dường như đang hoạt động tốt.


1
+1 vì hỗ trợ không cho phép cửa sổ lấy cắp tiêu điểm. Tôi ghét khi điều đó xảy ra khi tôi đang gõ tài liệu.
Ken Paul

1
Tôi hoàn toàn đồng ý với bạn chống lại việc lấy cắp tiêu điểm, nhưng trong trường hợp chính xác này, người dùng mong đợi ứng dụng xuất hiện trước. Nhưng việc thay đổi cài đặt sổ đăng ký và thay đổi hành vi hoàn chỉnh của cửa sổ sẽ rất khó.
boutta vào

Tôi đoán super.setAlwaysOnTop(false);là để cửa sổ không phải lúc nào cũng ở trên cùng, điều này là cần thiết để loại bỏ truechúng ta đã đặt trước đó để đưa cửa sổ lên phía trước, đúng không? Tôi đang hỏi vì với mã của bạn, cửa sổ vẫn luôn ở trên cùng trong trường hợp của tôi, điều mà tôi rõ ràng không muốn. Chạy jre1.8.0_66 trên Windows 10
Bram Vanroy

@Bram: Vâng, đúng vậy. Tôi đang chạy mã trên cùng một phiên bản Java và Windows và nó không phải lúc nào cũng nằm trên các cửa sổ khác. Có thể không cần thiết phải đặt luôn ở trên cùng, nhưng tôi nghĩ nếu không thì Windows chỉ nhấp nháy thanh tiêu đề, ít nhất là trong một số điều kiện.
Lawrence Dol,

Hừm, lạ lùng. Bạn có thể xem qua một câu hỏi tương tự mà tôi liên kết đến câu trả lời này không? Có thể mã đó cho thấy rõ ràng hơn vấn đề: stackoverflow.com/questions/34637597/…
Bram Vanroy

11

Đây là một phương pháp THỰC SỰ hoạt động (được thử nghiệm trên Windows Vista): D

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

Biến toàn màn hình cho biết bạn muốn ứng dụng chạy toàn màn hình hay ở chế độ cửa sổ.

Thao tác này không làm flash thanh tác vụ, nhưng đưa cửa sổ về phía trước một cách đáng tin cậy.


Cảm ơn vì mẹo setExtendedState. Tôi đã sử dụng nó cùng với giải pháp toFront () và repaint () để đưa cửa sổ lên nền trước ngay cả khi nó đã được thu nhỏ.
cướp

1
Đã xác nhận: giải pháp này hoạt động trong WindowsXP, sử dụng ToFront dẫn đến thông báo nhấp nháy trên thanh tác vụ. Cảm ơn!
Eric Lindauer

5

Hj, tất cả các phương pháp của bạn đều không hoạt động với tôi, trong Fedora KDE 14. Tôi có một cách bẩn thỉu để đưa một cửa sổ lên phía trước, trong khi chúng tôi đang chờ Oracle khắc phục sự cố này.

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

  //...

}

Và, điều này hoạt động hoàn hảo trong Fedora KDE 14 của tôi :-)


Một chút hacky, phù hợp với chúng tôi, nhưng chỉ cho cuộc gọi đầu tiên :-). (Kubuntu 12.04) - giải pháp khác đã thất bại
user85155

Đây là giải pháp duy nhất phù hợp với tôi (Windows Server 2012 R2) cho sự cố trong đó JFrame (đăng nhập) được mở nhưng không có tiêu điểm cho đến khi người dùng nhấp vào nó.
glenneroo

4

Phương pháp đơn giản này đã hoạt động hoàn hảo đối với tôi trong Windows 7:

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

2
Các repaint()là không cần thiết, invokeLater()đã làm nó. Cảm ơn bạn.
Matthieu

4

Tôi đã kiểm tra câu trả lời của bạn và chỉ câu trả lời của Stefan Reich phù hợp với tôi. Mặc dù tôi không thể khôi phục cửa sổ về trạng thái trước đó (tối đa / bình thường). Tôi thấy đột biến này tốt hơn:

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

Đó là setStatethay vì setExtendedState.


3

Cách đơn giản nhất mà tôi thấy rằng không có sự nhất quán giữa các nền tảng:

setVible (sai); setVible (đúng);


1
gây ra một số nhấp nháy mặc dù phải không? đẹp và đơn giản mặc dù :)
rogerdpack

không hoạt động cho quá trình lý lịch của tôi. Cửa sổ cũng có màu trắng cho lần làm mới đầu tiên nếu được gọi từ quy trình nền trước. Không thể sử dụng để lấy màn hình.
DragonLord

Có thể tránh nhấp nháy bằng cách kiểm tra xem cửa sổ có được biểu tượng hóa hay không
totaam

2

Các quy tắc điều chỉnh những gì sẽ xảy ra khi bạn .toFront () một JFrame giống nhau trong windows và linux:

-> nếu một cửa sổ của ứng dụng hiện tại là cửa sổ có tiêu điểm, thì tiêu điểm sẽ hoán đổi thành cửa sổ được yêu cầu -> nếu không, cửa sổ chỉ nhấp nháy trên thanh tác vụ

NHƯNG :

-> cửa sổ mới tự động lấy tiêu điểm

Vì vậy, hãy khai thác điều này! Bạn muốn đưa cửa sổ ra phía trước thì phải làm như thế nào? Tốt :

  1. Tạo một cửa sổ trống không có mục đích
  2. Cho nó xem
  3. Chờ nó hiển thị trên màn hình (setVnable làm được điều đó)
  4. Khi được hiển thị, hãy yêu cầu tiêu điểm cho cửa sổ bạn thực sự muốn lấy tiêu điểm đến
  5. ẩn cửa sổ trống, phá hủy nó

Hoặc, trong mã 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);
  }
});

Không hoạt động trên Win7, cả hai cửa sổ đều nhấp nháy (nếu tôi không ẩn thứ 2).
NateS

Sáng tạo. Không hoạt động cho quy trình nền của tôi trên Win7, khi được bảo vệ. Khung mới không xuất hiện trên đầu trang. JDK 6u21 cũ hơn.
DragonLord

0

Có rất nhiều cảnh báo trong javadoc đối với phương thức toFront () có thể gây ra sự cố của bạn.

Nhưng dù sao thì tôi cũng sẽ đoán xem, khi "chỉ có tab trong thanh tác vụ nhấp nháy", ứng dụng đã được thu nhỏ chưa? Nếu vậy, dòng sau từ javadoc có thể áp dụng:

"Nếu Cửa sổ này có thể nhìn thấy, hãy đưa Cửa sổ này lên phía trước và có thể biến nó thành Cửa sổ tập trung."


0

Để tránh cửa sổ mất tiêu điểm khi nó trở lại hiển thị sau khi bị ẩn, tất cả những gì cần thiết là:

setExtendedState(JFrame.NORMAL);

Như vậy:

defaultItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                showWindow();
                setExtendedState(JFrame.NORMAL);
            }
});
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.