加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

c# – Mono for Android,WebView输入字段filechooser不起作用

发布时间:2020-12-15 17:23:54 所属栏目:百科 来源:网络整理
导读:我有用于上传文件的网页.用户使用 input type =“file”/选择文件并按下提交按钮,一切正常.现在我需要创建 Android应用程序(在C#with mono for android),它包含简单的webview,并且必须像web版本一样工作. 但我偶然发现了这个问题 – 当我点击选择文件按钮时,
我有用于上传文件的网页.用户使用< input type =“file”/>选择文件并按下提交按钮,一切正常.现在我需要创建 Android应用程序(在C#with mono for android),它包含简单的webview,并且必须像web版本一样工作.

但我偶然发现了这个问题 – 当我点击选择文件按钮时,文件对话框无法打开.

几天我用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:GetOpenFileChooserHandlern" +
            "";
        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注释.

我确实说这是纯粹的不朽邪恶,不是吗?

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读