当前位置: 首页 > 知识库问答 >
问题:

JavaFX最小化未装饰阶段

冀胤运
2023-03-14

我有一个未装饰的JavaFX舞台,我自己的最小化,最大化

有没有办法通过单击任务栏图标来最小化纯Java代码未修饰的阶段?如果不是的话,我该怎么做,比如说,JNA?

编辑:好的,我一直在尝试用JNA解决这个问题,但由于几乎没有C/C/JNA,我在设置这个方面有点麻烦。如果有人帮我把这些东西拼凑起来,我将不胜感激。。

到目前为止,这是我的代码:

public final class Utils {

   static {
    if (PlatformUtil.isWin7OrLater()) {
            Native.register("shell32");
            Native.register("user32");
        }
    }

    // Apparently, this is the event I am after
    public static final int WM_ACTIVATEAPP = 0x1C;


    public static void registerMinimizeHandler(Stage stage) {
       // Hacky way to get a pointer to JavaFX Window
       Pointer pointer = getWindowPointer(stage);

       WinDef.HWND hwnd = new WinDef.HWND(pointer);

       // Here's my minimize/activate handler
       WinUser.WindowProc windowProc = new MinimizeHandler(stage);

       Pointer magicPointer = ... set this to point to windowProc?

       // This.. apparently, re-sets the WndProc? But how do I get the "magicPointer" that is "attached" to the windowProc?
       User32.INSTANCE.SetWindowLong(hwnd, User32.GWL_WNDPROC, magicPointer);
    }
}

 private static class MinimizeHandler implements WinUser.WindowProc {

    private Stage stage;

    private MinimizeHandler(Stage stage) {
        this.stage = stage;
    }

    @Override
    public WinDef.LRESULT callback(WinDef.HWND hWnd, int uMsg, WinDef.WPARAM wParam, WinDef.LPARAM lParam) {
        if (uMsg == WM_ACTIVATEAPP) {
            System.out.println("ACTIVATE");
        }
        return User32.INSTANCE.DefWindowProc(hWnd, uMsg, wParam, lParam);
    }
}

private static Pointer getWindowPointer(Stage stage) {
    try {
        TKStage tkStage = stage.impl_getPeer();
        Method getPlatformWindow = tkStage.getClass().getDeclaredMethod("getPlatformWindow" );
        getPlatformWindow.setAccessible(true);
        Object platformWindow = getPlatformWindow.invoke(tkStage);
        Method getNativeHandle = platformWindow.getClass().getMethod( "getNativeHandle" );
        getNativeHandle.setAccessible(true);
        Object nativeHandle = getNativeHandle.invoke(platformWindow);
        return new Pointer((Long) nativeHandle);
    } catch (Throwable e) {
        System.err.println("Error getting Window Pointer");
        return null;
    }
}

编辑2:我最终在这一点上做得更进一步,但当我重新设置WNDPROC时,我未装饰的窗口没有响应任何事件。。我为一个具有有效解决方案的自包含示例提供了100%的声誉奖励。只有Windows(7)是可以的,我甚至不知道它在其他平台上的行为。

编辑3:嗯,我有点放弃了这个。。我正确地设置了所有内容,并收到了事件,但在确定要收听的正确事件时遇到了问题。。

由于对这个问题有一些兴趣,如果有人想尝试继续这样做,这是我的最终代码(希望它应该开箱即用地“工作”):

public final class Utils {

    static interface ExtUser32 extends StdCallLibrary, User32 {
        ExtUser32 INSTANCE = (ExtUser32) Native.loadLibrary(
                        "user32",
                        ExtUser32.class,
                        W32APIOptions.DEFAULT_OPTIONS);

        WinDef.LRESULT CallWindowProcW(
                        Pointer lpWndProc,
                        Pointer hWnd,
                        int msg,
                        WinDef.WPARAM wParam,
                        WinDef.LPARAM lParam);

        int SetWindowLong(HWND hWnd, int nIndex, com.sun.jna.Callback wndProc) throws LastErrorException;
    }

    // Some possible event types
    public static final int WM_ACTIVATE = 0x0006;
    public static final int WM_ACTIVATEAPP = 0x1C;
    public static final int WM_NCACTIVATE = 0x0086;

    public static void registerMinimizeHandler(Stage stage) {
        Pointer pointer = getWindowPointer(stage);
        WinDef.HWND hwnd = new WinDef.HWND(pointer);
        long old = ExtUser32.INSTANCE.GetWindowLong(hwnd, User32.GWL_WNDPROC);
        MinimizeHandler handler = new MinimizeHandler(stage, old);
        ExtUser32.INSTANCE.SetWindowLong(hwnd, User32.GWL_WNDPROC, handler);
    }

    private static Pointer getWindowPointer(Stage stage) {
    try {
        TKStage tkStage = stage.impl_getPeer();
        Method getPlatformWindow = tkStage.getClass().getDeclaredMethod("getPlatformWindow" );
        getPlatformWindow.setAccessible(true);
        Object platformWindow = getPlatformWindow.invoke(tkStage);
        Method getNativeHandle = platformWindow.getClass().getMethod( "getNativeHandle" );
        getNativeHandle.setAccessible(true);
        Object nativeHandle = getNativeHandle.invoke(platformWindow);
        return new Pointer((Long) nativeHandle);
    } catch (Throwable e) {
        System.err.println("Error getting Window Pointer");
        return null;
    }
}

    private static class MinimizeHandler implements WinUser.WindowProc, StdCallLibrary.StdCallCallback {

        private Pointer mPrevWndProc32;

        private Stage stage;

        private MinimizeHandler(Stage stage, long oldPtr) {
            this.stage = stage;

            mPrevWndProc32 = new Pointer(oldPtr);

            // Set up an event pump to deliver messages for JavaFX to handle
            Thread thread = new Thread() {
                @Override
                public void run() {
                    int result;
                    WinUser.MSG msg = new WinUser.MSG();
                    while ((result = User32.INSTANCE.GetMessage(msg, null, 0, 0)) != 0) {
                        if (result == -1) {
                            System.err.println("error in get message");
                            break;
                        }
                        else {
                            System.out.println("got message: " + result);
                            User32.INSTANCE.TranslateMessage(msg);
                            User32.INSTANCE.DispatchMessage(msg);
                        }
                    }
                }
            };
            thread.start();
        }

        @Override
        public WinDef.LRESULT callback(WinDef.HWND hWnd, int uMsg, WinDef.WPARAM wParam, WinDef.LPARAM lParam) {

            if (uMsg == WM_ACTIVATEAPP) {
                // Window deactivated (wParam == 0)... Here's where I got stuck and gave up,
                // this is probably not the best event to listen to.. the app
                // does indeed get iconified now by pressing the task-bar button, but it
                // instantly restores afterwards..
                if (wParam.intValue() == 0) {
                    stage.setIconified(true);
                }
                return new WinDef.LRESULT(0);
            }

            // Let JavaFX handle other events
            return ExtUser32.INSTANCE.CallWindowProcW(
                            mPrevWndProc32,
                            hWnd.getPointer(),
                            uMsg,
                            wParam,
                            lParam);
        }
    }
}

共有3个答案

井旺
2023-03-14

没有正确回答你的问题...但这是解决方案

@FXML private void minimize()
{
Stage stage = (Stage) minimize.getScene().getWindow();
stage.setIconified(true);
}
欧阳英彦
2023-03-14

这里要注意两件事。

首先,这些库看起来不像是最新版本的JNA,截至目前为5.50,从Maven存储库添加。我不得不添加4.2.1库。

其次,您可能会遇到此异常,就像我在Windows 10和Java11上所做的那样:在学习JavaNative Access时出现包com.sun.glass.ui错误

解决方案是转到IDE中的VM选项(运行-

--add-exports
javafx.graphics/com.sun.glass.ui=ALL-UNNAMED

在那之后它应该会起作用。

我希望看到有人实现本机Windows动画来最小化和取消最小化未装饰的窗口,但我还没有彻底搜索,看看这是否已经讨论过。如果我遇到解决方案,我会更新这个。

编辑:

在对Windows动画进行进一步研究后,看起来有一个解决方案可以一起被黑客攻击,但我放弃了尝试实现下面的C#hack。这似乎更像是一个操作系统问题,而不仅仅是JavaFX。

通过在start()中修改,我可以使初始窗口保持未装饰状态,同时最小化和使用动画:

int newStyle = oldStyle | 0x00020000 | 0x00C00000;

但是,在最小化并重新打开后,Windows边框看起来很奇怪。

然后,我尝试在图标化时使用ChangeListener来交换Windows样式。

stage.iconifiedProperty().addListener(new ChangeListener<Boolean>() {

        @Override
        public void changed(ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) {
            if (t1.booleanValue() == true) {
                int newStyle = oldStyle | 0x00020000 | 0x00C00000;
                user32.SetWindowLong(hwnd, GWL_STYLE, newStyle);
            } else if (t1.booleanValue() == false) {
                int newStyle = oldStyle | 0x00020000;
                user32.SetWindowLong(hwnd, GWL_STYLE, newStyle);
            }
        }
    });

这成功地使窗口的un-最小化动画始终正常工作,同时使(可见)阶段无边框。

一旦我找到了重新应用的最佳方法,我似乎可以使最小化动画生效:

int newStyle = oldStyle | 0x00020000 | 0x00C00000;
user32.SetWindowLong(hwnd, GWL_STYLE, newStyle);

就在舞台图标化之前,用户看不到边框。一旦实现,这可能与下面第一个链接中的C解决方案类似。基本上,上面的ChangeListener做的事情是相反的。

解决无边界/未装饰动画的链接:

使用无边框形式的windows动画

https://exceptionshub.com/borderless-window-using-areo-snap-shadow-minimize-animation-and-shake.html

http://pinvoke.net/default.aspx/Constants/Window样式。html

JavaFX最小化

朱鹏
2023-03-14

您可以只设置适当的窗口样式。它在XP中工作,但在windows 7 32位中应该可以。我认为(但无法测试)如果您使用64位,那么请更改为Ptr windows函数,即GetWindowLongPtr。

import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.platform.win32.WinUser;
import static com.sun.jna.platform.win32.WinUser.GWL_STYLE;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class JNATest extends Application {
    public static void main(String[] args) { launch(args); }

    @Override
    public void start(Stage stage) {
        TextArea ta = new TextArea("output\n");
        VBox root = new VBox(5,ta);
        Scene scene = new Scene(root,800,200);
        stage.setTitle("Find this window");
        stage.setScene(scene);
        stage.show();
        //gets this window (stage)
        long lhwnd = com.sun.glass.ui.Window.getWindows().get(0).getNativeWindow();
        Pointer lpVoid = new Pointer(lhwnd);
        //gets the foreground (focused) window
        final User32 user32 = User32.INSTANCE;
        char[] windowText = new char[512];
        HWND hwnd = user32.GetForegroundWindow();
        //see what the title is
        user32.GetWindowText(hwnd, windowText, 512);
        //user32.GetWindowText(new HWND(lpVoid), windowText, 512);//to use the hwnd from stage
        String text=(Native.toString(windowText));
        //see if it's the same pointer
        ta.appendText("HWND java:" + lpVoid + " HWND user32:"+hwnd+" text:"+text+"\n");
        //change the window style if it's the right title
        if (text.equals(stage.getTitle())){
            //the style to change 
            int WS_DLGFRAME = 0x00400000;//s/b long I think
            //not the same constant here??
            ta.appendText("windows api:"+WS_DLGFRAME+" JNA: "+WinUser.SM_CXDLGFRAME);
            int oldStyle = user32.GetWindowLong(hwnd, GWL_STYLE);
            int newStyle = oldStyle & ~0x00400000; //bitwise not WS_DLGFRAME means remove the style
            newStyle = newStyle & ~0x00040000;//WS_THICKFRAME   
            user32.SetWindowLong(hwnd, GWL_STYLE, newStyle);
        }
    }

}

我猜你用

            long oldStyleLong = user32.GetWindowLongPtr(hwnd, GWL_STYLE).longValue();
            long newStyleLong = oldStyleLong & ~ 0x00400000l;
            user32.SetWindowLongPtr(hwnd, GWL_STYLE, new BaseTSD.LONG_PTR(newStyleLong));

64位。我想我的User32.dll中没有这些功能,所以我不能测试它。那里有很多无关的代码,主要用于测试或教学。一旦你弄清楚你想做什么,就删除未使用的行。

注意:不要添加newStyle=newStyle

以下是最简单的代码量,只需使用阶段的HWND更改未装饰的阶段。

    public void start(Stage stage) {
        Scene scene = new Scene(new Pane(new Label("Hello World")));
        stage.initStyle(StageStyle.UNDECORATED);
        stage.setTitle("Find this window");
        stage.setScene(scene);
        stage.show();
        long lhwnd = com.sun.glass.ui.Window.getWindows().get(0).getNativeWindow();
        Pointer lpVoid = new Pointer(lhwnd);
        HWND hwnd = new HWND(lpVoid);
        final User32 user32 = User32.INSTANCE;
        int oldStyle = user32.GetWindowLong(hwnd, GWL_STYLE);
        System.out.println(Integer.toBinaryString(oldStyle));
        int newStyle = oldStyle | 0x00020000;//WS_MINIMIZEBOX
        System.out.println(Integer.toBinaryString(newStyle));
        user32.SetWindowLong(hwnd, GWL_STYLE, newStyle);
    }

它打印出前后的样式标志,以便您查看设置了哪些样式。

 类似资料:
  • 问题内容: 我有一个未经修饰的JavaFX Stage,还有我自己的最小化,最大化和关闭按钮。但不幸的是,与经过修饰的行为相比,在Windows 7中单击任务栏图标不会自动最小化该阶段。 通过单击任务栏图标,是否可以通过纯Java代码最小化未修饰的阶段?如果不是,我该如何使用JNA? 编辑: 好的,我一直在尝试使用JNA解决此问题,但是几乎没有C / C ++ / JNA完成,因此设置起来有些麻烦

  • 我是JavaFX技术的新手,目前我正在开发JavaFX应用程序,其中有一个未装饰的阶段,我可以使用下面的代码在屏幕上移动它,但我无法从右下角调整此窗口的大小,有人能给我建议解决方案吗。

  • 我创建了一个小库,其中包含一个自定义窗口/阶段,用于使我的所有应用程序具有恒定和统一的外观。基本上,这只是一个没有装饰的舞台,有一个锚具,包含定制的最小化/最大化/关闭和调整大小控件。 唯一的问题是按下最小化按钮时执行的方法。在我的主系统(ArchLinux)上运行应用程序时不会发生任何事情,看起来应用程序被冻结了,这里有一个可视化示例:在Imgur上上传的示例图像,还没有足够的代表来发布图像 最

  • 我在JavaFX应用程序中有一个未修饰的阶段。为了最小化它,我需要一个最小化按钮。我在中创建了我的布局,其中有一个,但是当我尝试使用最小化位于控制器内部的这个按钮时,使用,它找不到阶段。 如何在控制器类中找到对该阶段的引用?

  • 我有一扇没有装饰的窗户: 我想知道我怎样才能把它做成一扇可拖动的未装饰窗户?我想在用户用鼠标右键选择窗口时更改其位置,然后在按住鼠标键的同时移动鼠标。 附言:我测试了这个解决方案,但它不起作用:

  • 我在这个问题上使用了公认的答案:JavaFX最小化未装饰阶段,以适当地最小化我的应用程序。 然而,不幸的是,默认窗口最小化了 我知道可以在未装饰的窗口中显示动画,因为我有一个应用程序具有这种行为(PotPlayer)。 如何使用JNA制作动画? 编辑:这是一个可以正常最小化JavaFX窗口的Kotlin代码段,还添加了bounty。