但我偶然发现了这个问题 – 当我点击选择文件按钮时,文件对话框无法打开.
几天我用Google搜索了这个问题,但我没有找到任何解决方案.看起来有workaround on Java platform,但它不适用于C#.
有人有任何想法如何让它工作?
解决方法
首先,我们需要一个“中间人”.由于WebChromeClient未声明openFileChooser()方法,因此我们需要声明一个名为OpenFileWebChromeClient的版本.它声明了一个虚拟的OpenFileChooser方法,并为它提供了一个绑定,以便可以覆盖它:
using System; using Android.App; using Android.Content; using Android.Runtime; using Android.OS; using Android.Webkit; namespace Scratch.FileUpload { [Register ("android/webkit/WebChromeClient",DoNotGenerateAcw=true)] class OpenFileWebChromeClient : WebChromeClient { static IntPtr id_openFileChooser; [Register ("openFileChooser","(Landroid/webkit/ValueCallback;)V","GetopenFileChooserHandler")] public virtual void OpenFileChooser (IValueCallback uploadMsg) { if (id_openFileChooser == IntPtr.Zero) id_openFileChooser = jnienv.getmethodID (ThresholdClass,"openFileChooser","(Landroid/webkit/ValueCallback;)V"); if (GetType () == ThresholdType) jnienv.CallVoidMethod (Handle,id_openFileChooser,new JValue (jnienv.ToJniHandle (uploadMsg))); else jnienv.CallNonvirtualVoidMethod (Handle,ThresholdClass,new JValue (jnienv.ToJniHandle (uploadMsg))); } #pragma warning disable 0169 static Delegate cb_openFileChooser; static Delegate GetopenFileChooserHandler () { if (cb_openFileChooser == null) cb_openFileChooser = JNINativeWrapper.CreateDelegate ((Action<IntPtr,IntPtr,IntPtr>) n_OpenFileChooser); return cb_openFileChooser; } static void n_OpenFileChooser (IntPtr jnienv,IntPtr native__this,IntPtr native_uploadMsg) { OpenFileWebChromeClient __this = java.lang.Object.Getobject<OpenFileWebChromeClient> (native__this,JniHandleOwnership.DoNottransfer); var uploadMsg = java.lang.Object.Getobject<IValueCallback> (native_uploadMsg,JniHandleOwnership.DoNottransfer); __this.OpenFileChooser (uploadMsg); } #pragma warning restore 0169 } }
接下来,由于C#缺少匿名内部类,我们需要一个名为MyOpenFileWebChromeClient的显式类:
namespace Scratch.FileUpload { class MyOpenFileWebChromeClient : OpenFileWebChromeClient { Action<IValueCallback> cb; public MyOpenFileWebChromeClient(Action<IValueCallback> cb) { this.cb = cb; } public override void OpenFileChooser (IValueCallback uploadMsg) { cb (uploadMsg); } }
Activity端口与您引用的博客文章相同,只是它使用MyOpenFileWebChromeClient而不是匿名内部类.我还更新了一些逻辑来显示OnActivityResult()收到的URI:
namespace Scratch.FileUpload { [Activity (Label = "Scratch.FileUpload",MainLauncher = true)] public class Activity1 : Activity { private WebView wv; private IValueCallback mUploadMessage; const int FilechooserResultcode = 1; protected override void OnCreate (Bundle bundle) { base.OnCreate (bundle); wv = new WebView (this); wv.SetWebViewClient(new WebViewClient()); wv.SetWebChromeClient(new MyOpenFileWebChromeClient(uploadMsg => { mUploadMessage = uploadMsg; var intent = new Intent (Intent.ActionGetContent); intent.AddCategory(Intent.CategoryOpenable); intent.SetType("image/*"); StartActivityForResult(Intent.CreateChooser(intent,"File Chooser"),FilechooserResultcode); })); SetHtml(null); SetContentView(wv); } void SetHtml(string filename) { string html = @"<html> <body> <h1>Hello,world!</h1> <p>Input Box:</p> <input type=""file"" /> <p>URI: " + filename + @" </body> </html>"; wv.LoadData(html,"text/html","utf-8"); } protected override void OnActivityResult (int requestCode,Result resultCode,Intent data) { base.OnActivityResult (requestCode,resultCode,data); if (requestCode == FilechooserResultcode) { if (mUploadMessage == null) return; var result = data == null || resultCode != Result.Ok ? null : data.Data; SetHtml(result.ToString()); mUploadMessage.OnReceiveValue(result); mUploadMessage = null; } } } }
可悲的是,现在是时候采取纯粹的彻头彻尾的邪恶行为了. MyOpenFileWebChromeClient的上述声明的问题在于它不起作用,原因与M0S’博客无法在匿名内部类声明中使用@Override相同:你构建应用程序的android.jar不会声明openFileChooser()方法.
构建过程将生成Android Callable Wrappers,其中必须包含有效的Java代码.问题是生成的代码使用@Override来覆盖方法和接口方法,从而产生MyOpenFileWebChromeClient的Android Callable Wrapper:
package scratch.fileupload; public class MyOpenFileWebChromeClient extends android.webkit.WebChromeClient { static final String __md_methods; static { __md_methods = "n_openFileChooser:(Landroid/webkit/ValueCallback;)V:GetopenFileChooserHandler\n" + ""; mono.android.Runtime.register ("Scratch.FileUpload.MyOpenFileWebChromeClient,Scratch.FileUpload,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null",MyOpenFileWebChromeClient.class,__md_methods); } @Override public void openFileChooser (android.webkit.ValueCallback p0) { n_openFileChooser (p0); } private native void n_openFileChooser (android.webkit.ValueCallback p0); java.util.ArrayList refList; public void monodroidAddReference (java.lang.Object obj) { if (refList == null) refList = new java.util.ArrayList (); refList.add (obj); } public void monodroidClearReferences () { if (refList != null) refList.clear (); } }
显然,MyOpenFileWebChromeClient.openFileChooser()上的@Override会产生编译错误,那么我们如何才能使这个工作呢?通过提供我们自己的@Override注释!
package scratch.fileupload; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.soURCE) public @interface Override { }
将上述内容放入名为Override.java的文件中,将其添加到项目中,并将其Build操作设置为AndroidJavaSource.
生成的项目有效,因为我们在与MyOpenFileWebChromeClient类型相同的包中提供自定义@Override注释. (这因此要求您知道生成的包名称是什么,并且为每个包提供单独的@Override注释.)同一包中的类型优先于导入的名称,甚至来自java的名称.lang,所以我们的自定义@Override注释不仅编译,它还被MyOpenFileWebChromeClient android可调用包装器使用,优先于java.lang.Override注释.
我确实说这是纯粹的不朽邪恶,不是吗?
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。