2012-02-21 59 views
4

我有用於上傳文件的網頁。用戶選擇文件與<input type="file" />並按提交按鈕,一切正常。現在我需要創建android應用程序(在C#中,包含單純的android),它包含簡單的webview,並且必須像web版本一樣工作。Mono for Android,WebView輸入域filechoos不起作用

但我偶然發現了這個問題 - 當我點擊Choose file按鈕時,文件對話框無法打開。

我GOOGLE了這個問題幾天,但我沒有找到任何解決方案。看起來像是workaround on Java platform,但它在C#上不起作用。

有人有任何想法如何使其工作?

回答

2

我有一個想法如何使它工作。其中一部分是沼澤標準的「如何綁定一種虛擬方法」,其中的一部分是純粹的邪惡。

首先,我們需要一個「中介」。由於WebChromeClient未聲明openFileChooser()方法,因此我們需要聲明一個名爲OpenFileWebChromeClient的版本。它聲明virtualOpenFileChooser方法,併爲它的綁定,以便它可以被覆蓋:

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

活動端口與您提到的博客文章完全相同,只是它使用MyOpenFileWebChromeClient而不是匿名內部類。我也更新了一些邏輯來顯示的URI OnActivityResult()接收:

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替代的方法和接口的方法,導致Android的可調用包裝爲MyOpenFileWebChromeClient

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

顯然@OverrideMyOpenFileWebChromeClient.openFileChooser()將產生一個編譯器錯誤,那麼,我們如何使這項工作? 通過提供我們自己的@Override註釋!上述到一個名爲Override.java文件,將其添加到項目中,其生成操作設置爲AndroidJavaSource

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 { 
} 

廣場。

生成的項目的工作原理是因爲我們在與MyOpenFileWebChromeClient類型相同的包中提供了自定義的@Override註釋。 (因此,這要求您知道生成的包名稱是什麼,並且您爲每個包提供單獨的@Override註釋。)同一包中的類型優先於導入的名稱,甚至來自java.lang的名稱,因此我們的自定義@Override註釋不僅可以編譯,而且還可以被MyOpenFileWebChromeClient機器人可調用包裝器使用,優先於java.lang.Override註解。

我確實說過這是純粹的邪惡,不是嗎?

+1

上帝之母!這是一個解決方法,讓這個工作。幸運的是它已經在Mono for Android 4.2中得到修復! http://docs.xamarin.com/android/Releases/Mono_For_Android_4/Mono_For_Android_4.2#Bug_Fixes – Cheesebaron 2012-05-16 14:15:13

+0

這是固定的。文件上傳彈出不在我身邊的webview工作 – 2018-02-02 16:45:58

0

step1 = 文件上傳會起作用,我們需要給android清單中的讀/寫權限。 主活動cs

step2= 
private Action<int, Result, Intent> resultCallbackvalue; 

public void StartActivity(Intent intent, int requestCode, Action<int, Result, Intent> resultCallback) 
{ 
this.resultCallbackvalue = resultCallback; 
StartActivityForResult(intent, requestCode); 
} 
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data) 
{ 
base.OnActivityResult(requestCode, resultCode, data); 
if (this.resultCallbackvalue != null) 
{ 
this.resultCallbackvalue(requestCode, resultCode, data); 
this.resultCallbackvalue = null; 
} 
} 
step3= add ExtendedChromeClient,cs Inheriting from : WebChromeClient 

private static int filechooser = 1; 
private IValueCallback message; 
private MainActivity activity = null; 

    public ExtendedChromeClient(MainActivity context) 
    { 
     this.activity = context; 
    } 

    public override bool OnShowFileChooser(WebView webView, IValueCallback filePathCallback, FileChooserParams fileChooserParams) 
    { 
     this.message = filePathCallback; 
     Intent chooserIntent = fileChooserParams.CreateIntent(); 
     chooserIntent.AddCategory(Intent.CategoryOpenable); 
     this.activity.StartActivity(Intent.CreateChooser(chooserIntent, "File Chooser"), filechooser, this.OnActivityResult); 
     return true; 
    } 

    private void OnActivityResult(int requestCode, Result resultCode, Intent data) 
    { 
     if (data != null) 
     { 
      if (requestCode == filechooser) 
      { 
       if (null == this.message) 
       { 
        return; 
       } 

       this.message.OnReceiveValue(WebChromeClient.FileChooserParams.ParseResult((int)resultCode, data)); 
       this.message = null; 
      } 
     } 
    }