WebView填坑之旅3--H5中选择本地文件

把上一篇(WebView爬坑之旅2–a标签是target="_blank"时创建新窗口)的问题解决后,继续上线APP,然后又发现不能选择本地文件,这不是坑爹吗@##¥继续改不由自主的想起这张图:

谁让我是开发呢?

网页中选择手机中的文件,这个主要是WebChromeClient中实现,具体实现如下:
代码的有些片段是参考腾讯x5内核demo中实现的,由于这个功能在自己项目中用的不多,所以没做兼容测试,按道理大部分手机是可以的=. =

import android.app.AlertDialog;
import android.app.Dialog;
import android.content.ComponentName;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Environment;
import android.os.Message;
import android.provider.MediaStore;
import android.support.annotation.NonNull;
import android.text.TextUtils;

import com.orhanobut.logger.Logger;
import com.tencent.smtt.export.external.interfaces.ConsoleMessage;
import com.tencent.smtt.export.external.interfaces.JsResult;
import com.tencent.smtt.sdk.ValueCallback;
import com.tencent.smtt.sdk.WebChromeClient;
import com.tencent.smtt.sdk.WebView;
import com.tencent.smtt.sdk.WebViewClient;
import com.zhangzhong.jieqianban.common.frame.Constants;
import com.zhangzhong.jieqianban.common.utils.CommonUtils;

import java.io.File;

/**
 * Created by ly on 2017/9/1 16:53.
 */
public class CustomWebChromeClient extends WebChromeClient {
    private static final String TAG = "WebChromeClient";
    public static final int FILECHOOSER_TAKEPICTURE = 1;
    public static final int FILECHOOSER_CHOOSERFILE = 2;

    /**
     * 用于展示在web端的标签被选择之后,文件选择器的制作和生成
     */
    private ValueCallback uploadFile;
    private ValueCallback uploadFiles;
    private BaseWebActivity activity;

    private File takePicFile;//拍照保存的图片

    public CustomWebChromeClient(@NonNull BaseWebActivity activity) {
        this.activity = activity;
    }

    /**
     * 解析网页标题后的回调
     * Created by ly on 2017/9/26 9:54
     */
    @Override
    public void onReceivedTitle(WebView webView, String s) {
        Logger.d(">>>>onReceivedTitle:" + s);
        if (activity != null)
            activity.setTopTitle(s);
        super.onReceivedTitle(webView, s);
    }


    /**
     * 当网页里a标签target="_blank",打开新窗口时,这里会调用
     */
    @Override
    public boolean onCreateWindow(WebView webView, boolean isDialog, boolean isUserGesture, Message resultMsg) {
        X5WebView newWebView = new X5WebView(activity);
        X5WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj;
        newWebView.setWebChromeClient(new CustomWebChromeClient(activity));
        newWebView.setWebViewClient(new WebViewClient() {
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                if (activity != null) {
                    //拦截url,跳转新窗口=,=
                    Intent intent = new Intent(activity, CommWebActivity.class);
                    intent.putExtra(Constants.INTENT_KEY_URL, url);
                    activity.startActivity(intent);
                }
                //防止触发现有界面的WebChromeClient的相关回调
                return true;
            }
        });
        transport.setWebView(newWebView);
        resultMsg.sendToTarget();

        return true;
//        return super.onCreateWindow(webView, isDialog, isUserGesture, message);
    }

    /**
     * 控制台消息输出
     */
    @Override
    public boolean onConsoleMessage(ConsoleMessage cm) {
        if (cm.messageLevel() == ConsoleMessage.MessageLevel.DEBUG) {
            Logger.d(TAG, String.format("%s -- From line %s of %s", cm.message(), cm.lineNumber(), cm.sourceId()));
        } else if (cm.messageLevel() == ConsoleMessage.MessageLevel.LOG
                || cm.messageLevel() == ConsoleMessage.MessageLevel.TIP) {
            Logger.i(TAG, String.format("%s -- From line %s of %s", cm.message(), cm.lineNumber(), cm.sourceId()));
        } else if (cm.messageLevel() == ConsoleMessage.MessageLevel.WARNING) {
            Logger.w(TAG, String.format("%s -- From line %s of %s", cm.message(), cm.lineNumber(), cm.sourceId()));
        } else if (cm.messageLevel() == ConsoleMessage.MessageLevel.ERROR) {
            Logger.e(TAG, String.format("%s -- From line %s of %s", cm.message(), cm.lineNumber(), cm.sourceId()));
        }
        return true;
    }

    @Override
    public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
//        AlertDialog.Builder builder = new AlertDialog.Builder(BaseWebActivity.this);
//        builder.setMessage(message);
//        builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
//            @Override
//            public void onClick(DialogInterface dialog, int which) {
//                result.confirm();
//            }
//        });
//        builder.setCancelable(false).create().show();
        // 返回true表示自已处理,返回false表示由系统处理
        return false;
    }

    /**
     * 页面加载进度
     */
    @Override
    public void onProgressChanged(WebView view, int newProgress) {
//        if (pb_web != null) {
//            pb_web.setVisibility(View.VISIBLE);
//            pb_web.setMax(100);
//            pb_web.setProgress(newProgress);
//            if (pb_web.getProgress() == 100) {
//                pb_web.setVisibility(View.GONE);
//            }
//        }
        super.onProgressChanged(view, newProgress);
    }

    /**
     * 针对网页里的, WebView默认是不会弹出选择文件对话框的
     * 需要重写该方法,自己来弹出选择文件对话框。
     * 

* 注意SDK不同的版本会有不同的方法,需要统一处理 */ // For Android 3.0+ public void openFileChooser(ValueCallback uploadFile, String acceptType) { openFileChooseProcess(uploadFile, acceptType); } // For Android < 3.0 public void openFileChooser(ValueCallback uploadFile) { openFileChooseProcess(uploadFile, null); } // For Android > 4.1.1 @Override public void openFileChooser(ValueCallback uploadFile, String acceptType, String capture) { openFileChooseProcess(uploadFile, acceptType); } // For Android >= 5.0 @Override public boolean onShowFileChooser(WebView webView, ValueCallback filePathCallback, FileChooserParams fileChooserParams) { super.onShowFileChooser(webView, filePathCallback, fileChooserParams); this.uploadFiles = filePathCallback; if (fileChooserParams.isCaptureEnabled() && Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { takePicture(); } else { String[] acceptTypes = fileChooserParams.getAcceptTypes(); String acceptType = null; if (acceptTypes != null && acceptTypes.length > 0) acceptType = acceptTypes[0]; showMenu4InputFile(acceptType); } return true;//返回true,filePathCallback被调用 } private void openFileChooseProcess(ValueCallback uploadFile, String acceptType) { this.uploadFile = uploadFile; showMenu4InputFile(acceptType); } private void showMenu4InputFile(final String acceptType) { try { String[] menus = {"拍照", "相册"}; Dialog dialog = new AlertDialog.Builder(activity).setItems(menus, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { switch (which) { case 0: takePicture(); break; case 1: default: chooseFile(acceptType); break; } } }).create(); //这里很重要,如果弹出对话框,用户选择一个图片或者进行拍照,但是进行到一半的时候,用户cancel了,这个时候再去点击“选择文件”按钮时,网页会失去响应。 //原因是:点击“选择文件”按钮时,网页会缓存一个ValueCallback对象,必须触发了该对象的onReceiveValue()方法,WebView才会释放,进而才能再一次的选择文件。 //当弹层被取消时,返回未选择文件 dialog.setOnCancelListener(new DialogInterface.OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { if (uploadFile != null) { uploadFile.onReceiveValue(null); uploadFile = null; } } }); dialog.show(); } catch (Exception e) { e.printStackTrace(); } } private void chooseFile(String acceptType) { try { Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.addCategory(Intent.CATEGORY_OPENABLE); //能够返回一个Uri结果 if (TextUtils.isEmpty(acceptType)) {//接收类型 acceptType = "*/*"; //选择的文件类型,例如:image/*表示图片 } intent.setType(acceptType); activity.startActivityForResult(Intent.createChooser(intent, "File Chooser"), FILECHOOSER_CHOOSERFILE); } catch (Exception e) { e.printStackTrace(); } } private void takePicture() { try { //这种方式拍照后onActivityResult data=null String pictureName = "webPic-" + System.currentTimeMillis() + ".jpg"; File file = new File(Constants.TEMP_PATH); if (!file.exists()) { file.mkdirs(); } takePicFile = new File(file, pictureName); Uri imageUri = CommonUtils.getUriForFile(takePicFile); Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri); intent.putExtra("return-data", false); intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString()); intent.putExtra("noFaceDetection", true); ComponentName componentName = intent.resolveActivity(activity.getPackageManager()); if (componentName != null) { activity.startActivityForResult(intent, FILECHOOSER_TAKEPICTURE); } // 方式二 // Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); // activity.startActivityForResult(takePictureIntent, FILECHOOSER_TAKEPICTURE); } catch (Exception e) { e.printStackTrace(); } } public ValueCallback getUploadFile() { return uploadFile; } public ValueCallback getUploadFiles() { return uploadFiles; } public void setUploadFile(ValueCallback uploadFile) { this.uploadFile = uploadFile; } public void setUploadFiles(ValueCallback uploadFiles) { this.uploadFiles = uploadFiles; } public File getTakePicFile() { return takePicFile; } public void setTakePicFile(File takePicFile) { this.takePicFile = takePicFile; }

需要注意的是,onActivityResult回调部分代码(详情请见WebView爬坑之旅1–腾讯x5内核(tbs) 中BaseWebActivity 的实现):

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        //从web页选择文件/拍照的处理
        if (requestCode == CustomWebChromeClient.FILECHOOSER_CHOOSERFILE
                || requestCode == CustomWebChromeClient.FILECHOOSER_TAKEPICTURE) {
            Uri result;
            if (requestCode == CustomWebChromeClient.FILECHOOSER_CHOOSERFILE) {
                result = data == null ? null : data.getData();
            } else {
                result = CommonUtils.getUriForFile(webChromeClient.getTakePicFile());
            }

            ValueCallback uploadFile = webChromeClient.getUploadFile();
            ValueCallback uploadFiles = webChromeClient.getUploadFiles();
            if (null != uploadFile) {
                uploadFile.onReceiveValue(result);
                webChromeClient.setUploadFile(null);
            }
            if (null != uploadFiles) {
                uploadFiles.onReceiveValue(result == null ? null : new Uri[]{result});
                webChromeClient.setUploadFiles(null);
            }
        }
    }

你可能感兴趣的:(android)