จะนำหน้าต่างไปด้านหน้าได้อย่างไร?


90

เรามีแอปพลิเคชัน Java ที่จำเป็นต้องนำไปยังเบื้องหน้าเมื่อกลไกการควบคุมทางไกลเปิดใช้งานบางสิ่งในแอปพลิเคชัน

เพื่อให้ได้สิ่งนี้เราได้ตระหนักในวิธีการที่เรียกว่าคลาสซึ่งแสดงถึงเฟรมของแอปพลิเคชันของเรา (ส่วนขยายของ a JFrame) การใช้งานต่อไปนี้:

setVisible(true);
toFront();

ภายใต้ Windows XP จะใช้งานได้ในครั้งแรกที่เรียกในครั้งที่สองเฉพาะแท็บในแถบงานกะพริบกรอบจะไม่อยู่ด้านหน้าอีกต่อไป เช่นเดียวกันกับ Win2k บน Vista ดูเหมือนว่าจะทำงานได้ดี

คุณมีความคิดหรือไม่?


คุณมีตัวอย่างสำหรับพฤติกรรมนี้หรือไม่?
OscarRyz

3
คำตอบที่ถูกต้องคือการเรียกtoFront()บน EDT invokeLaterใช้ มีคำตอบง่ายๆอยู่ด้านล่าง แต่ไม่ใช่คำตอบที่ยอมรับ มันทำงานแม้ว่า อย่างสมบูรณ์แบบ.
Erick Robertson

ฉันรู้ว่านี่เป็นเรื่องเก่า แต่สิ่งนี้ก็เกิดขึ้นใน OSX ด้วย
ferdil

ฉันกำลังประสบปัญหานี้ แต่ดูเหมือนว่าไม่มีคำตอบใดที่จะช่วยแก้ปัญหาได้ ฉันแน่ใจว่ามันเกิดจาก windows ไม่ยอมให้ฉัน 'Steal' Focus สำหรับหน้าต่างแรกของฉันในแอปพลิเคชัน
Craig Warren

คำตอบ:


69

ทางออกที่เป็นไปได้คือ:

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

8
บางทีเราควรเริ่มโค้ด UI ทั้งหมดภายใน invokeLater ตั้งแต่แรก? ;)
java.is.for.desktop.indeed

2
ไม่ได้ผลสำหรับฉันใน Java 7 บน KDE 4.9.5 หน้าต่างจะยังคงซ่อนอยู่ด้านล่างโปรแกรมอื่น ๆ สิ่งที่ช่วยฉันคือเปลี่ยนลำดับการนำหน้าต่างมาด้านหน้า แทนที่จะซ่อนหน้าต่างเดียวและแสดงหน้าต่างที่สองให้แสดงหน้าต่างที่สองจากนั้นซ่อนหน้าต่างแรก (JFrame)
Lekensteyn

1
ทำงานร่วมกับ Windows 10 ที่ใช้ Java 1.8 ในแอพเพล็ต
Elliott

วิธีการผกผันจะเป็นอย่างไร?
Cardinal - คืนสถานะ Monica

33

ฉันมีปัญหาเดียวกันกับการนำ a JFrameไปด้านหน้าภายใต้ Ubuntu (Java 1.6.0_10) และวิธีเดียวที่ฉันจะแก้ไขได้คือการระบุWindowListenerไฟล์. โดยเฉพาะผมก็มีการตั้งค่าของฉันJFrameเสมออยู่ด้านบนเมื่อใดก็ตามที่toFront()มีการเรียกใช้และให้จัดการเหตุการณ์ที่จะwindowDeactivatedsetAlwaysOnTop(false)


ดังนั้นนี่คือรหัสที่สามารถวางลงในฐานJFrameซึ่งใช้เพื่อรับเฟรมแอปพลิเคชันทั้งหมด

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

frame.setVisible(true)เมื่อใดก็ตามที่กรอบของคุณควรจะแสดงหรือนำไปโทรด้านหน้า

เนื่องจากฉันย้ายไปที่ Ubuntu 9.04 ดูเหมือนว่าจะไม่จำเป็นต้องมีWindowListenerสำหรับการเรียกใช้super.setAlwaysOnTop(false)- อย่างที่สังเกตได้ รหัสนี้ถูกย้ายไปที่วิธีการtoFront()และsetVisible().

โปรดทราบว่าsetVisible()ควรเรียกใช้วิธีการใน EDT เสมอ


ขอบคุณ! ที่เกี่ยวข้องก็คือคำถามนี้stackoverflow.com/questions/2315560/…
rogerdpack

ฉันไม่ได้รวบรวมเนื่องจากวิธี setDisposed () ไม่พบ.
ka3ak

1
@ ka3ak นี่คือ setter ที่ได้รับการป้องกันซึ่งสามารถนำมาใช้ในคลาส JFrame-base ที่แนะนำเพื่อติดตามสถานการณ์ที่มีการกำจัดเฟรม วิธีการกำจัด () จะต้องถูกแทนที่ด้วยการเรียก setDisposed (จริง) นี่ไม่ใช่สิ่งที่จำเป็นสำหรับทุกคนอย่างเคร่งครัด
01

1
.setAlwaysOnTop(true);เป็นเพียงคนเดียวที่ทำงานสำหรับฉันเมื่อใช้ JWindow
DGolberg

setAlwaysOnTop(true)เป็นวิธีเดียวที่ฉันทำให้มันทำงานภายใต้ windows 10 - ขอบคุณ!
Hartmut P.

23

Windows มีสิ่งอำนวยความสะดวกในการป้องกันไม่ให้หน้าต่างขโมยโฟกัส แต่จะกะพริบไอคอนแถบงาน ใน XP จะเปิดโดยค่าเริ่มต้น (ที่เดียวที่ฉันเคยเห็นการเปลี่ยนแปลงคือใช้ TweakUI แต่มีการตั้งค่ารีจิสทรีอยู่ที่ใดที่หนึ่ง) ใน Vista พวกเขาอาจเปลี่ยนค่าเริ่มต้นและ / หรือเปิดเผยว่าเป็นการตั้งค่าที่ผู้ใช้สามารถเข้าถึงได้ด้วย UI ที่พร้อมใช้งาน

การป้องกันไม่ให้หน้าต่างบังคับตัวเองไปด้านหน้าและการโฟกัสเป็นคุณสมบัติตั้งแต่ Windows 2K (และฉันขอขอบคุณสำหรับสิ่งนี้)

ที่กล่าวว่าฉันมีแอป Java เล็กน้อยที่ฉันใช้เพื่อเตือนให้ฉันบันทึกกิจกรรมของฉันในขณะที่ทำงานและทำให้ตัวเองเป็นหน้าต่างที่ใช้งานอยู่ทุกๆ 30 นาที (แน่นอนว่ากำหนดค่าได้) ทำงานอย่างสม่ำเสมอภายใต้ Windows XP และไม่กะพริบหน้าต่างแถบหัวเรื่อง ใช้รหัสต่อไปนี้เรียกว่าในเธรด UI อันเป็นผลมาจากการเริ่มทำงานของเหตุการณ์จับเวลา:

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

(บรรทัดแรกจะคืนค่าหากย่อให้เล็กสุด ... อันที่จริงมันจะคืนค่าหากขยายใหญ่สุด แต่ฉันไม่เคยมี)

ในขณะที่ฉันมักจะย่อแอปนี้ให้เล็กที่สุด แต่บ่อยครั้งมันอยู่หลังโปรแกรมแก้ไขข้อความของฉัน และอย่างที่ฉันพูดมันได้ผลเสมอ

ฉันมีความคิดว่าปัญหาของคุณคืออะไร - บางทีคุณอาจมีเงื่อนไขการแข่งขันกับการโทร setVisible () toFront () อาจไม่ถูกต้องเว้นแต่หน้าต่างจะปรากฏขึ้นจริงเมื่อถูกเรียก ฉันเคยมีปัญหากับ requestFocus () มาก่อน คุณอาจต้องวางการโทร toFront () ในตัวฟัง UI ในเหตุการณ์ที่เปิดใช้งานหน้าต่าง

2014-09-07:ในบางช่วงเวลาโค้ดด้านบนหยุดทำงานอาจเป็นที่ Java 6 หรือ 7 หลังจากการตรวจสอบและทดลองบางอย่างฉันต้องอัปเดตโค้ดเพื่อแทนที่toFrontวิธีการของหน้าต่างให้ทำสิ่งนี้ (ร่วมกับโค้ดที่แก้ไขจากสิ่งที่ อยู่เหนือ):

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

ใน Java 8_20 ดูเหมือนว่าโค้ดนี้จะใช้งานได้ดี


1
+1 สำหรับรองรับไม่ให้ windows ขโมยโฟกัส ฉันเกลียดเมื่อสิ่งนั้นเกิดขึ้นเมื่อฉันพิมพ์เอกสาร
Ken Paul

1
ฉันเห็นด้วยอย่างยิ่งกับคุณในเรื่องการขโมยโฟกัส แต่ในกรณีนี้ผู้ใช้คาดว่าแอปพลิเคชันจะมาที่ด้านหน้า แต่มันจะคลายความเย็นในการเปลี่ยนการตั้งค่ารีจิสทรีและเปลี่ยนลักษณะการทำงานของ Windows ทั้งหมด
boutta

ฉันเดาsuper.setAlwaysOnTop(false);ว่าหน้าต่างไม่ได้อยู่ด้านบนเสมอไปซึ่งจำเป็นต้องกำจัดสิ่งที่trueเราตั้งไว้ก่อนหน้านี้เพื่อนำหน้าต่างไปด้านหน้าถูกต้องหรือไม่? ฉันถามเพราะด้วยรหัสของคุณหน้าต่างยังคงอยู่ด้านบนเสมอในกรณีของฉันซึ่งเห็นได้ชัดว่าฉันไม่ต้องการ รัน jre1.8.0_66 บน Windows 10
Bram Vanroy

@ แบรม: ใช่ถูกต้อง ฉันใช้รหัสบน Java และ Windows เวอร์ชันเดียวกันและไม่ได้จบลงที่ด้านบนของหน้าต่างอื่นเสมอไป อาจไม่จำเป็นต้องตั้งไว้ด้านบนเสมอไป แต่ฉันคิดว่าเป็นอย่างอื่น Windows ก็กะพริบแถบหัวเรื่องอย่างน้อยก็ภายใต้เงื่อนไขบางประการ
Lawrence Dol

หืมแปลกดี คุณช่วยดูคำถามที่คล้ายกันซึ่งฉันเชื่อมโยงไปยังคำตอบนี้ได้ไหม บางทีรหัสนั้นอาจแสดงปัญหาได้ชัดเจนขึ้น: stackoverflow.com/questions/34637597/…
Bram Vanroy

11

นี่เป็นวิธีการที่ใช้งานได้จริง (ทดสอบบน Windows Vista): D

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

ตัวแปรเต็มหน้าจอระบุว่าคุณต้องการให้แอปทำงานแบบเต็มหน้าจอหรือแบบหน้าต่าง

สิ่งนี้ไม่ได้กะพริบแถบงาน แต่นำหน้าต่างไปด้านหน้าอย่างน่าเชื่อถือ


ขอบคุณสำหรับเคล็ดลับ setExtendedState ฉันใช้มันร่วมกับวิธีแก้ปัญหา toFront () และ repaint () เพื่อนำหน้าต่างไปที่พื้นหน้าแม้ว่าจะย่อเล็กสุด
ปล้น

1
ได้รับการยืนยัน: โซลูชันนี้ใช้ได้ใน WindowsXP โดยใช้ toFront ผลลัพธ์ในข้อความกะพริบในแถบงาน ขอบคุณ!
Eric Lindauer

5

Hj วิธีการทั้งหมดของคุณไม่ได้ผลสำหรับฉันใน Fedora KDE 14 ฉันมีวิธีที่สกปรกในการนำหน้าต่างมาไว้ข้างหน้าในขณะที่เรากำลังรอให้ Oracle แก้ไขปัญหานี้

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

  //...

}

และใช้งานได้ดีใน Fedora KDE 14 ของฉัน :-)


แฮ็คเล็กน้อยใช้ได้กับเรา แต่สำหรับการโทรครั้งแรกเท่านั้น :-) (Kubuntu 12.04) - โซลูชันอื่น ๆ ล้มเหลว
user85155

นี่เป็นทางออกเดียวที่ใช้ได้ผลสำหรับฉัน (Windows Server 2012 R2) สำหรับปัญหาที่เปิด JFrame (ล็อกอิน) แต่ไม่มีโฟกัสจนกว่าผู้ใช้จะคลิก
glenneroo

4

วิธีง่ายๆนี้ใช้ได้ผลกับฉันอย่างสมบูรณ์ใน Windows 7:

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

2
repaint()ไม่จำเป็นต้องมีการinvokeLater()ทำมัน ขอขอบคุณ.
Matthieu

4

ฉันทดสอบคำตอบของคุณและมีเพียงStefan Reichเท่านั้นที่ใช้ได้กับฉัน แม้ว่าฉันจะไม่สามารถกู้คืนหน้าต่างกลับสู่สถานะก่อนหน้าได้ (ขยายใหญ่สุด / ปกติ) ฉันพบว่าการกลายพันธุ์นี้ดีขึ้น:

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

นั่นคือแทนsetStatesetExtendedState


3

วิธีที่ง่ายที่สุดที่ฉันพบว่าไม่มีความไม่สอดคล้องกันระหว่างแพลตฟอร์ม:

setVisible (เท็จ); setVisible (จริง);


1
ทำให้บางคนกระพริบ แต่ไม่ได้? ดีและเรียบง่าย :)
rogerdpack

ใช้ไม่ได้กับกระบวนการพื้นหลังของฉัน หน้าต่างยังปรากฏเป็นสีขาวสำหรับการรีเฟรชครั้งแรกหากเรียกจากกระบวนการเบื้องหน้า ไม่สามารถใช้เพื่อจับหน้าจอ
DragonLord

สามารถหลีกเลี่ยงการกะพริบได้โดยตรวจสอบว่าหน้าต่างนั้นมี
สัญลักษณ์

2

กฎที่ควบคุมสิ่งที่เกิดขึ้นเมื่อคุณ. toFront () a JFrame จะเหมือนกันใน windows และใน linux:

-> หากหน้าต่างของแอปพลิเคชันที่มีอยู่ในปัจจุบันเป็นหน้าต่างที่โฟกัสอยู่ให้โฟกัสสลับไปที่หน้าต่างที่ร้องขอ -> หากไม่เป็นเช่นนั้นหน้าต่างจะกะพริบในแถบงาน

แต่:

-> หน้าต่างใหม่รับโฟกัสโดยอัตโนมัติ

มาใช้ประโยชน์จากสิ่งนี้กันเถอะ! ต้องการนำหน้าต่างมาไว้ด้านหน้าต้องทำอย่างไร? ดี:

  1. สร้างหน้าต่างว่างเปล่าที่ไม่มีวัตถุประสงค์
  2. แสดง
  3. รอให้ปรากฏบนหน้าจอ (setVisible ทำเช่นนั้น)
  4. เมื่อแสดงขึ้นให้ขอโฟกัสสำหรับหน้าต่างที่คุณต้องการนำโฟกัสไป
  5. ซ่อนหน้าต่างที่ว่างเปล่าทำลายมัน

หรือในรหัสจาวา:

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

ไม่ทำงานบน Win7 ทั้งสองหน้าต่างแฟลช (ถ้าฉันไม่ซ่อนตัวที่ 2)
NateS

สร้างสรรค์. ใช้ไม่ได้กับกระบวนการพื้นหลังของฉันบน Win7 เมื่อครอบคลุม เฟรมใหม่ไม่ขึ้นมาด้านบน เก่ากว่า JDK 6u21
DragonLord

0

มีคำเตือนมากมายใน javadoc สำหรับวิธีการ toFront () ซึ่งอาจทำให้เกิดปัญหาของคุณ

แต่ฉันจะเดาต่อไปเมื่อ "เฉพาะแท็บในแถบงานกะพริบ" แอปพลิเคชันถูกย่อให้เล็กที่สุดหรือไม่ ในกรณีนี้อาจใช้บรรทัดต่อไปนี้จาก javadoc:

"หากมองเห็นหน้าต่างนี้ให้นำหน้าต่างนี้ไปด้านหน้าและอาจทำให้เป็นหน้าต่างที่โฟกัส"


0

เพื่อหลีกเลี่ยงไม่ให้หน้าต่างสูญเสียโฟกัสเมื่อหน้าต่างกลับมามองเห็นได้หลังจากซ่อนสิ่งที่จำเป็นคือ

setExtendedState(JFrame.NORMAL);

ชอบมาก:

defaultItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                showWindow();
                setExtendedState(JFrame.NORMAL);
            }
});
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.