由于我不熟悉 Objective-C 和 JNF,我只能让 JNI 类部分执行。到目前为止我尝试过的方法如下:
这里是 Open.java,它是一个更重要的 Java Swing 应用程序的 MCV,它应该有一个更好看、功能更好的打开文件对话框,而不是不合理的
java.awt.FileDialog
或丑陋且功能较少的 javax.swing.JFileChooser
。
import java.awt.event.*;
import javax.swing.*;
public class Open extends JFrame {
public Open () {
getContentPane().add(new JButton(new AbstractAction() {
public void actionPerformed (ActionEvent e) {
try {
NativeOpenFileDialog.run();
} catch (Throwable ex) {
System.err.println(ex.getMessage());
}
}
}));
pack();
setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() { new Open(); }
});
}
}
这里是NativeOpenFileDialog.java
import javax.swing.*;
public class NativeOpenFileDialog {
static {
System.loadLibrary("natopndlg");
};
public static native void run ();
public static void main (String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() { NativeOpenFileDialog.run(); }
});
}
}
这是 NativeOpenFileDialog.m
#import <Cocoa/Cocoa.h>
#include <jni.h>
#include "NativeOpenFileDialog.h"
JNIEXPORT void JNICALL Java_NativeOpenFileDialog_run (JNIEnv *thisEnv, jclass jcls) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSOpenPanel *panel = [NSOpenPanel openPanel];
[panel setCanChooseFiles:YES];
[panel setCanChooseDirectories:YES];
[panel setAllowsMultipleSelection:YES];
if ([panel runModal] != NSModalResponseOK)
NSLog(@"User did not press OK");
[pool release];
}
这是 makefile(我使用的是 Xcode 15.2):
SDK = /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk
Open.class: Open.java NativeOpenFileDialog.java NativeOpenFileDialog.m
javac -h . NativeOpenFileDialog.java
javac Open.java
gcc -dynamiclib -o libnatopndlg.dylib -framework Cocoa -isysroot $(SDK) -I $(JAVA_HOME)/include -I $(JAVA_HOME)/include/darwin -fobjc-exceptions -std=c99 NativeOpenFileDialog.m
这是我执行后如何执行它的
make
。
java Open
单击按钮会引发异常:
NSInternalInconsistencyException', reason: 'NSWindow should only be instantiated on the main thread!
这是可以理解的,因为面板应该在 UI 线程上打开。我相信我所要做的就是确保 NSOpenPanel 创建的窗口是 JFrame 的子窗口并在 UI 线程上打开。我相信这至少意味着我必须执行一个 Java 函数才能获取 JFrame 的 NSWindow。
您可以使用dispatch_async() 和dispatch_sync() 在UI 线程上执行操作。 NSObject 还包含一个方法
performSelectorOnMainThread:withObject:waitUntilDone:
,它将在主线程上执行选择器。
这是使用 Java Objective-C 桥的要点,它可以执行与此类似的操作:
https://gist.github.com/anonymous/3966989
对于您的具体问题,您可以将方法主体包装在dispatch_async中
JNIEXPORT void JNICALL Java_NativeOpenFileDialog_run (JNIEnv *thisEnv, jclass jcls) {
dispatch_async(dispatch_get_main_queue(), ^{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSOpenPanel *panel = [NSOpenPanel openPanel];
[panel setCanChooseFiles:YES];
[panel setCanChooseDirectories:YES];
[panel setAllowsMultipleSelection:YES];
if ([panel runModal] != NSModalResponseOK)
NSLog(@"User did not press OK");
[pool release];
});
}
当然,这只是打开对话框,不会对用户选择的文件执行任何操作,因此您需要在此基础上进行构建。