input标签在Android中的使用小记

input 标签在Android中的使用小记

最近遇到个需求,html页面有个<input type="file" /> 标签需要调用客户端的拍照跟选择照片功能,在iOS上面可以正常调起拍照,选择照片。但是在Android上却没有反应,当然这也是我意料之中的事情。

遇到问题当然就想办法去解决了,我先打开了biying搜索,全都是爬的别人写的博客,千篇一律没有任何价值,于是上了stackoverflow,找到了一个这个回答 。接下来我打开了webChormeClient(sdk25)的源码,看到了这个方法,当页面的input标签点击之后,我们可以拦截,然后自己处理并将数据返回,返回什么样的数据格式呢?当时我也在想这个问题,后来看到了这个得到了答案,base64

  /**
     * Tell the client to show a file chooser.
     *
     * This is called to handle HTML forms with 'file' input type, in response to the
     * user pressing the "Select File" button.
     * To cancel the request, call <code>filePathCallback.onReceiveValue(null)</code> and
     * return true.
     *
     * @param webView The WebView instance that is initiating the request.
     * @param filePathCallback Invoke this callback to supply the list of paths to files to upload,
     *                         or NULL to cancel. Must only be called if the
     *                         <code>showFileChooser</code> implementations returns true.
     * @param fileChooserParams Describes the mode of file chooser to be opened, and options to be
     *                          used with it.
     * @return true if filePathCallback will be invoked, false to use default handling.
     *
     * @see FileChooserParams
     */
    public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback,
            FileChooserParams fileChooserParams) {
        return false;
    }

思路:

  • 当监控到html页面的input标签点击的时候由我们自己弹出对话框让用户选择拍照,选择照片等
  • 用户拍照选择照片处理完成之后转成base64后将数据返回前端
  • 前端拿到客户端返回的base64数据显示在对应的控件上

Html的处理

Html代码

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1, user-scalable=no">
        <meta name="apple-mobile-web-app-capable" content="yes">
        <title></title>
        <style>
            .header {
                height: 44px;
                line-height: 44px;
                text-align: center;
            }

            #capture_img {
                width: 200px;
                height: 200px;
            }

            #video {
                width: 100%;
                height: 200px;
            }
        </style>
    </head>

    <body>
        <header class="header">
            <input type="file" accept="image/*" capture="camera" />
        </header>
        <img id="capture_img" />
        <video id="video" controls="controls" />
    </body>
    <script>
        //***Android端拍照、选择照片之后会调用这个方法***
        function onCaptureFinished(base64String) {
            var img = document.getElementById('capture_img');
            if(base64String) {
                img.src = 'data:image/png;base64,' + base64String;
            }
        }

        //***Android端录像之后调用这个方法***
        function onRecordFinished(base64String) {
            var video = document.getElementById('video');
            video.src = "data:video/mp4;base64," + base64String;
            setTimeout(function() {
                video.play();
            }, 1000);
        }
    </script>
</html>

Android的处理

  • 初始化WebView
 private void initWebView() {
        mWebView = (WebView) findViewById(R.id.wv_content);
        WebSettings mWebSettings = mWebView.getSettings();
        mWebSettings.setAppCacheEnabled(false);//禁止APP缓存
        mWebSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);//禁止网页缓存
        mWebSettings.setJavaScriptEnabled(true);
        mWebSettings.setSupportZoom(false);//不允许缩放
        mWebSettings.setAllowFileAccess(true);
        mWebView.setWebViewClient(new WebViewClient());
        mWebView.setWebChromeClient(new FileWebChromeClient());
        jsObject = new JavaScriptObject(this);
        //这里是我们要执行js方法的对象
        mWebView.addJavascriptInterface(jsObject, "qfxl");
        mWebView.loadUrl("file:///android_asset/index.html");
    }
  • 在使用webView加载这个网页之后重写WebChromeClient中的onShowFileChooser方法
class FileWebChromeClient extends WebChromeClient {
        // 当页面有input type=file 标签的时候会调用这个方法
        public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback,
                                         WebChromeClient.FileChooserParams fileChooserParams) {

            //这里弹框让用户选择拍照录像选择照片等
            showFileChooser();
            return false;
        }
    }
  • 监听到Html的input事件,弹窗
   private void showFileChooser() {
        String[] selectPicTypeStr = {"拍照", "录像", "从相册中选"};
        AlertDialog mAlertDialog = new AlertDialog.Builder(MainActivity.this)
                .setItems(selectPicTypeStr,
                        new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog,
                                                int which) {
                                switch (which) {
                                    // 拍照
                                    case 0:
                                        Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                                        File file = new File(IMG_PATH);
                                        if (!file.exists()) {
                                            try {
                                                file.createNewFile();
                                            } catch (IOException e) {
                                                e.printStackTrace();
                                            }
                                        }
                                        captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file));
                                        startActivityForResult(captureIntent, CAPTURE_TAG);
                                        break;
                                    //录像
                                    case 1:
                                        File videoFile = new File(VIDEO_PATH);
                                        if (!videoFile.exists()) {
                                            try {
                                                videoFile.createNewFile();
                                            } catch (IOException e) {
                                                e.printStackTrace();
                                            }
                                        }
                                        Intent videoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
                                        videoIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(videoFile));
                                        startActivityForResult(videoIntent, CAMERA_TAG);
                                        break;
                                    // 手机相册
                                    case 2:
                                        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
                                        i.addCategory(Intent.CATEGORY_OPENABLE);
                                        i.setType("image/*");
                                        startActivityForResult(Intent.createChooser(i, "选择您要添加的照片"), CHOOSE_TAG);
                                        break;
                                    default:
                                        break;
                                }

                            }
                        }).setOnCancelListener(new DialogInterface.OnCancelListener() {
                    @Override
                    public void onCancel(DialogInterface dialogInterface) {
                        //DO NOTHING
                    }
                }).show();
    }
  • 处理拍照录像选择照片的回调onActivityResult
 @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
            case CAPTURE_TAG://拍照
                if (data != null) {//指定了uri则data 为空
                } else {
                    Bitmap bitmap = BitmapFactory.decodeFile(IMG_PATH);
                    if (bitmap != null) {
                    //调用js方法,将Bitmap转成base64String
                        jsObject.onCaptureFinished(mWebView, castBitmapToBase64(bitmap));
                    } else {
                        Toast.makeText(MainActivity.this, "bitmap is null", Toast.LENGTH_SHORT).show();
                    }
                }
                break;
            case CAMERA_TAG://录像
                if (data != null) {
                    File file = new File(VIDEO_PATH);
                    if (file.exists()) {
                        try {
                            FileInputStream fin = new FileInputStream(file);
                            byte[] b = new byte[(int) file.length()];
                            fin.read(b);
                            fin.close();
                            String base64String = Base64.encodeToString(b, Base64.DEFAULT);
                            //调用js方法
                            jsObject.onRecordFinished(mWebView, base64String);
                        } catch (FileNotFoundException e) {
                            e.printStackTrace();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
                break;
            case CHOOSE_TAG://从相册中选
                if (data != null) {
                    Uri uri = data.getData();
                    try {
                        Bitmap bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), uri);
                        //调用js方法
                        jsObject.onCaptureFinished(mWebView, castBitmapToBase64(bitmap));
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                break;
        }
    }
  • JavaScriptObject
public class JavaScriptObject {
    private Context context;

    public JavaScriptObject(Context context) {
        this.context = context;
    }
    /**
     *
     * 拍照或者选择照片结束
     * @param webview
     * @param base64String
     */
    public void onCaptureFinished(WebView webview, String base64String) {
        webview.loadUrl("javascript:onCaptureFinished('" + base64String + "')");
    }

    /**
     * 录屏结束
     * @param webView
     */
    public void onRecordFinished(WebView webView,String base64String){
        webView.loadUrl("javascript:onRecordFinished('" + base64String + "')");
    }
}

完整代码

public class MainActivity extends AppCompatActivity {
    private WebView mWebView;
    private JavaScriptObject jsObject;
    private final int CAPTURE_TAG = 1;
    private final int CAMERA_TAG = 2;
    private final int CHOOSE_TAG = 3;
    private final String IMG_PATH = Environment.getExternalStorageDirectory() + File.separator + "upload.jpg";
    private final String VIDEO_PATH = Environment.getExternalStorageDirectory() + File.separator + "upload.mp4";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initWebView();
    }

    private void initWebView() {
        mWebView = (WebView) findViewById(R.id.wv_content);
        WebSettings mWebSettings = mWebView.getSettings();
        mWebSettings.setAppCacheEnabled(false);//禁止APP缓存
        mWebSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);//禁止网页缓存
        mWebSettings.setJavaScriptEnabled(true);
        mWebSettings.setSupportZoom(false);//不允许缩放
        mWebSettings.setAllowFileAccess(true);
        mWebView.setWebViewClient(new WebViewClient());
        mWebView.setWebChromeClient(new FileWebChromeClient());
        jsObject = new JavaScriptObject(this);
        mWebView.addJavascriptInterface(jsObject, "qfxl");
        mWebView.loadUrl("file:///android_asset/index.html");
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
            case CAPTURE_TAG://拍照
                if (data != null) {//指定了uri则data 为空
                } else {
                    Bitmap bitmap = BitmapFactory.decodeFile(IMG_PATH);
                    if (bitmap != null) {
                        jsObject.onCaptureFinished(mWebView, castBitmapToBase64(bitmap));
                    } else {
                        Toast.makeText(MainActivity.this, "bitmap is empty", Toast.LENGTH_SHORT).show();
                    }
                }
                break;
            case CAMERA_TAG://录像
                if (data != null) {
                    File file = new File(VIDEO_PATH);
                    if (file.exists()) {
                        try {
                            FileInputStream fin = new FileInputStream(file);
                            byte[] b = new byte[(int) file.length()];
                            fin.read(b);
                            fin.close();
                            String base64String = Base64.encodeToString(b, Base64.DEFAULT);
                            jsObject.onRecordFinished(mWebView, base64String);
                            Toast.makeText(MainActivity.this, "finished", Toast.LENGTH_SHORT).show();
                        } catch (FileNotFoundException e) {
                            e.printStackTrace();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
                break;
            case CHOOSE_TAG://从相册中选
                if (data != null) {
                    Uri uri = data.getData();
                    try {
                        Bitmap bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), uri);
                        jsObject.onCaptureFinished(mWebView, castBitmapToBase64(bitmap));
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                break;
        }
    }

    /**
     * 将bitmap转成base64
     *
     * @param bitmap
     * @return
     */
    private String castBitmapToBase64(Bitmap bitmap) {
        String base64String = "";
        try {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            bitmap.compress(Bitmap.CompressFormat.JPEG, 30, bos);
            byte[] bitmapBytes = bos.toByteArray();
            bos.flush();
            bos.close();
            base64String = Base64.encodeToString(bitmapBytes, Base64.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return base64String;
    }


    class FileWebChromeClient extends WebChromeClient {
        // 当页面有input type=file 标签的时候会调用这个方法
        public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback,
                                         WebChromeClient.FileChooserParams fileChooserParams) {

            showFileChooser();
            return false;
        }

    }

    private void showFileChooser() {
        String[] selectPicTypeStr = {"拍照", "录像", "从相册中选"};
        AlertDialog mAlertDialog = new AlertDialog.Builder(MainActivity.this)
                .setItems(selectPicTypeStr,
                        new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog,
                                                int which) {
                                switch (which) {
                                    // 拍照
                                    case 0:
                                        Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                                        File file = new File(IMG_PATH);
                                        if (!file.exists()) {
                                            try {
                                                file.createNewFile();
                                            } catch (IOException e) {
                                                e.printStackTrace();
                                            }
                                        }
                                        captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file));
                                        startActivityForResult(captureIntent, CAPTURE_TAG);
                                        break;
                                    //录像
                                    case 1:
                                        File videoFile = new File(VIDEO_PATH);
                                        if (!videoFile.exists()) {
                                            try {
                                                videoFile.createNewFile();
                                            } catch (IOException e) {
                                                e.printStackTrace();
                                            }
                                        }
                                        Intent videoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
                                        videoIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(videoFile));
                                        startActivityForResult(videoIntent, CAMERA_TAG);
                                        break;
                                    // 手机相册
                                    case 2:
                                        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
                                        i.addCategory(Intent.CATEGORY_OPENABLE);
                                        i.setType("image/*");
                                        startActivityForResult(Intent.createChooser(i, "选择您要添加的照片"), CHOOSE_TAG);
                                        break;
                                    default:
                                        break;
                                }

                            }
                        }).setOnCancelListener(new DialogInterface.OnCancelListener() {
                    @Override
                    public void onCancel(DialogInterface dialogInterface) {
                        //DO NOTHING
                    }
                }).show();
    }

}

流程梳理一下:

  • 监听到html的file事件
  • 客户端弹出对话框让用户选择具体操作
  • 处理用户操作的回调
  • 将回调的数据返回给html

总结

博客不能停止更新,好记性不如烂笔头。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值