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

来源:互联网 发布:爆菊感受 知乎 编辑:程序博客网 时间:2024/05/22 11:34

把上一篇(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端<input type=text>的标签被选择之后,文件选择器的制作和生成     */    private ValueCallback<Uri> uploadFile;    private ValueCallback<Uri[]> 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);    }    /**     * 针对网页里的<input type="file"  accept="image/*" capture="camera" >, WebView默认是不会弹出选择文件对话框的     * 需要重写该方法,自己来弹出选择文件对话框。     * <p>     * 注意SDK不同的版本会有不同的方法,需要统一处理     */    // For Android 3.0+    public void openFileChooser(ValueCallback<Uri> uploadFile, String acceptType) {        openFileChooseProcess(uploadFile, acceptType);    }    // For Android < 3.0    public void openFileChooser(ValueCallback<Uri> uploadFile) {        openFileChooseProcess(uploadFile, null);    }    // For Android  > 4.1.1    @Override    public void openFileChooser(ValueCallback<Uri> uploadFile, String acceptType, String capture) {        openFileChooseProcess(uploadFile, acceptType);    }    // For Android  >= 5.0    @Override    public boolean onShowFileChooser(WebView webView,                                     ValueCallback<Uri[]> 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<Uri> 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<Uri> getUploadFile() {        return uploadFile;    }    public ValueCallback<Uri[]> getUploadFiles() {        return uploadFiles;    }    public void setUploadFile(ValueCallback<Uri> uploadFile) {        this.uploadFile = uploadFile;    }    public void setUploadFiles(ValueCallback<Uri[]> 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<Uri> uploadFile = webChromeClient.getUploadFile();            ValueCallback<Uri[]> 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);            }        }    }
原创粉丝点击