Flutter-让webview支持图片上传(Android,

2020-10-27  本文已影响0人  Cosecant

详细源码地址:https://gitee.com/yugecse/WebViewUploadApplication/tree/master/

该功能主要是对谷歌的第三方库(webview_flutter)进行源码修改,如果升级版本后需要重新修改。

需要修改或新增的文件:

  1. 修改项目app/build.gradle文件,增加一个图片选择库;修改如下:
    dependencies {
        implementation 'com.zhihu.android:matisse:0.5.3-beta3'
        implementation 'com.github.bumptech.glide:glide:4.11.0'
        annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
    }
  1. 修改FlutterWebview.java

   // 文件选择监听声明
   interface OnShowFileChooserListener {

        boolean onShowFileChooser(ValueCallback<Uri[]> filePathCallback, String acceptType);

    }

    private static final String JS_CHANNEL_NAMES_FIELD = "javascriptChannelNames";
    private final InputAwareWebView webView;
    private final MethodChannel methodChannel;
    private final FlutterWebViewClient flutterWebViewClient;
    private final Handler platformThreadHandler;
    // 声明监听器对象
    private OnShowFileChooserListener onShowFileChooserListener;

    // Verifies that a url opened by `Window.open` has a secure url.
    private class FlutterWebChromeClient extends WebChromeClient {
        @Override
        public boolean onCreateWindow(
                final WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) {
            final WebViewClient webViewClient =
                    new WebViewClient() {
                        @TargetApi(Build.VERSION_CODES.LOLLIPOP)
                        @Override
                        public boolean shouldOverrideUrlLoading(
                                @NonNull WebView view, @NonNull WebResourceRequest request) {
                            final String url = request.getUrl().toString();
                            if (!flutterWebViewClient.shouldOverrideUrlLoading(
                                    FlutterWebView.this.webView, request)) {
                                webView.loadUrl(url);
                            }
                            return true;
                        }

                        @Override
                        public boolean shouldOverrideUrlLoading(WebView view, String url) {
                            if (!flutterWebViewClient.shouldOverrideUrlLoading(
                                    FlutterWebView.this.webView, url)) {
                                webView.loadUrl(url);
                            }
                            return true;
                        }
                    };

            final WebView newWebView = new WebView(view.getContext());
            newWebView.setWebViewClient(webViewClient);

            final WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj;
            transport.setWebView(newWebView);
            resultMsg.sendToTarget();

            return true;
        }

        // 重写文件选择函数
        @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
        @Override
        public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
            String[] acceptTypes = fileChooserParams.getAcceptTypes();
            if (acceptTypes != null && acceptTypes.length > 0 && onShowFileChooserListener != null)
                return onShowFileChooserListener.onShowFileChooser(filePathCallback, acceptTypes[0]);
            return super.onShowFileChooser(webView, filePathCallback, fileChooserParams);
        }
    }

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
    @SuppressWarnings("unchecked")
    FlutterWebView(
            final Context context,
            BinaryMessenger messenger,
            int id,
            Map<String, Object> params,
            View containerView,
           // 传入文件选择监听器对象
            OnShowFileChooserListener onShowFileChooserListener) {
        // 监听器对象赋值
        this.onShowFileChooserListener = onShowFileChooserListener;
        DisplayListenerProxy displayListenerProxy = new DisplayListenerProxy();
        DisplayManager displayManager =
                (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
        displayListenerProxy.onPreWebViewInitialization(displayManager);
        webView = new InputAwareWebView(context, containerView);
        displayListenerProxy.onPostWebViewInitialization(displayManager);

        platformThreadHandler = new Handler(context.getMainLooper());
        // Allow local storage.
        webView.getSettings().setDomStorageEnabled(true);
        webView.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);

        // Multi windows is set with FlutterWebChromeClient by default to handle internal bug: b/159892679.
        webView.getSettings().setSupportMultipleWindows(true);
        webView.setWebChromeClient(new FlutterWebChromeClient());

        methodChannel = new MethodChannel(messenger, "plugins.flutter.io/webview_" + id);
        methodChannel.setMethodCallHandler(this);

        flutterWebViewClient = new FlutterWebViewClient(methodChannel);
        Map<String, Object> settings = (Map<String, Object>) params.get("settings");
        if (settings != null) applySettings(settings);

        if (params.containsKey(JS_CHANNEL_NAMES_FIELD)) {
            List<String> names = (List<String>) params.get(JS_CHANNEL_NAMES_FIELD);
            if (names != null) registerJavaScriptChannelNames(names);
        }

        Integer autoMediaPlaybackPolicy = (Integer) params.get("autoMediaPlaybackPolicy");
        if (autoMediaPlaybackPolicy != null) updateAutoMediaPlaybackPolicy(autoMediaPlaybackPolicy);
        if (params.containsKey("userAgent")) {
            String userAgent = (String) params.get("userAgent");
            updateUserAgent(userAgent);
        }
        if (params.containsKey("initialUrl")) {
            String url = (String) params.get("initialUrl");
            webView.loadUrl(url);
        }
    }
  1. 修改FlutterWebViewFactory.java
public final class WebViewFactory extends PlatformViewFactory {
  private final BinaryMessenger messenger;
  private final View containerView;
  private final FlutterWebView.OnShowFileChooserListener onShowFileChooserListener;

  WebViewFactory(BinaryMessenger messenger, View containerView, FlutterWebView.OnShowFileChooserListener onShowFileChooserListener) {
    super(StandardMessageCodec.INSTANCE);
    this.messenger = messenger;
    this.containerView = containerView;
    this.onShowFileChooserListener = onShowFileChooserListener;
  }

  @SuppressWarnings("unchecked")
  @Override
  public PlatformView create(Context context, int id, Object args) {
    Map<String, Object> params = (Map<String, Object>) args;
    return new FlutterWebView(context, messenger, id, params, containerView, onShowFileChooserListener);
  }
}
  1. 新增加一个WebViewFileUploader.java的类
package io.flutter.plugins.webviewflutter;

import android.app.Activity;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.net.Uri;
import android.webkit.ValueCallback;
import android.widget.Toast;

import com.zhihu.matisse.Matisse;
import com.zhihu.matisse.MimeType;
import com.zhihu.matisse.engine.impl.GlideEngine;

import java.util.List;

public class WebViewFileUploader {

    private Activity activity;

    private ValueCallback<Uri[]> filePathCallback;

    private String acceptType;

    WebViewFileUploader(Activity activity ){
        this.activity = activity;
    }

    public void start(ValueCallback<Uri[]> filePathCallback, String acceptType){
        this.filePathCallback = filePathCallback;
        this.acceptType = acceptType;
        if(acceptType != null && acceptType.toLowerCase().contains("image/*")){
            pickImage();
        }else {
            this.filePathCallback.onReceiveValue(null);
            this.filePathCallback = null;
            Toast.makeText(activity, "您设置的AcceptType不是图片", Toast.LENGTH_SHORT).show();
        }
    }

    private void pickImage(){
        Matisse.from(activity)
                .choose(MimeType.ofAll())
                .countable(true)
                .maxSelectable(1)
//                .addFilter(new GifSizeFilter(320, 320, 5 * Filter.K * Filter.K))
//                .gridExpectedSize(getResources().getDimensionPixelSize(R.dimen.grid_expected_size))
                .restrictOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)
                .thumbnailScale(0.85f)
                .imageEngine(new GlideEngine())
                .showPreview(true)
                .forResult(0x001ABC);
    }

    public boolean onActivityResult(int requestCode, int resultCode, Intent data){
        if(requestCode == 0x001ABC){
            if(resultCode == Activity.RESULT_OK){
              List<Uri> uriPaths = Matisse.obtainResult(data);
              if(uriPaths != null && uriPaths.size() > 0){
                  filePathCallback.onReceiveValue(new Uri[]{ uriPaths.get(0) });
                  filePathCallback = null;
                  return true;
              }
            }
            filePathCallback.onReceiveValue(null);
            filePathCallback = null;
            return true;
        }
        return false;
    }

}

  1. 修改FlutterWebViewPlugin.java类

/**
 * Java platform implementation of the webview_flutter plugin.
 *
 * <p>Register this in an add to app scenario to gracefully handle activity and context changes.
 *
 * <p>Call {@link #registerWith(Registrar)} to use the stable {@code io.flutter.plugin.common}
 * package instead.
 */
public class WebViewFlutterPlugin implements FlutterPlugin, ActivityAware, PluginRegistry.ActivityResultListener, FlutterWebView.OnShowFileChooserListener {

    private Activity activity;
    private FlutterCookieManager flutterCookieManager;
    private WebViewFileUploader fileUploader;

    /**
     * Add an instance of this to {@link io.flutter.embedding.engine.plugins.PluginRegistry} to
     * register it.
     *
     * <p>THIS PLUGIN CODE PATH DEPENDS ON A NEWER VERSION OF FLUTTER THAN THE ONE DEFINED IN THE
     * PUBSPEC.YAML. Text input will fail on some Android devices unless this is used with at least
     * flutter/flutter@1d4d63ace1f801a022ea9ec737bf8c15395588b9. Use the V1 embedding with {@link
     * #registerWith(Registrar)} to use this plugin with older Flutter versions.
     *
     * <p>Registration should eventually be handled automatically by v2 of the
     * GeneratedPluginRegistrant. https://github.com/flutter/flutter/issues/42694
     */
    public WebViewFlutterPlugin() {
    }

//  /**
//   * Registers a plugin implementation that uses the stable {@code io.flutter.plugin.common}
//   * package.
//   *
//   * <p>Calling this automatically initializes the plugin. However plugins initialized this way
//   * won't react to changes in activity or context, unlike {@link CameraPlugin}.
//   */
//  @SuppressWarnings("deprecation")
//  public static void registerWith(io.flutter.plugin.common.PluginRegistry.Registrar registrar) {
//    registrar
//        .platformViewRegistry()
//        .registerViewFactory(
//            "plugins.flutter.io/webview",
//            new WebViewFactory(registrar.messenger(), registrar.view()));
//    new FlutterCookieManager(registrar.messenger());
//  }

    @Override
    public void onAttachedToEngine(FlutterPluginBinding binding) {
        BinaryMessenger messenger = binding.getBinaryMessenger();
        binding
                .getPlatformViewRegistry()
                .registerViewFactory(
                        "plugins.flutter.io/webview",
                        new WebViewFactory(messenger, /*containerView=*/ null, this));
        flutterCookieManager = new FlutterCookieManager(messenger);
    }

    @Override
    public void onDetachedFromEngine(FlutterPluginBinding binding) {
        if (flutterCookieManager == null) {
            return;
        }

        flutterCookieManager.dispose();
        flutterCookieManager = null;
    }

    @Override
    public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) {
        this.activity = binding.getActivity();
        binding.addActivityResultListener(this);
    }

    @Override
    public void onDetachedFromActivityForConfigChanges() {

    }

    @Override
    public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) {
        binding.removeActivityResultListener(this);
    }

    @Override
    public void onDetachedFromActivity() {

    }

    @Override
    public boolean onShowFileChooser(ValueCallback<Uri[]> filePathCallback, String acceptType) {
        if (fileUploader == null) fileUploader = new WebViewFileUploader(activity);
        fileUploader.start(filePathCallback, acceptType);
        return true;
    }

    @Override
    public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
        if (fileUploader != null)
            return fileUploader.onActivityResult(requestCode, resultCode, data);
        return false;
    }
}

至此,你需要实现的图片文件选择的功能已经实现!

注明:原创内容,请注明出处,谢谢!

上一篇下一篇

猜你喜欢

热点阅读