Webview调用相册相机失败等问题(h5交互问题)

1.FileProvider用于解决高版本的android系统出现的调用相机和相册不成功的问题

①.  权限申请

建议在你的BaseActivity中去增加两个方法。

public void onPermissionRequests(String permission, OnBooleanListener onBooleanListener) {
    onPermissionListener = onBooleanListener;
    Log.d("MainActivity", "0");
    if (ContextCompat.checkSelfPermission(this,
            permission)
            != PackageManager.PERMISSION_GRANTED) {
        // Should we show an explanation?
        Log.d("MainActivity", "1");
        if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                Manifest.permission.READ_CONTACTS)) {
            //权限已有
            onPermissionListener.onClick(true);
        } else {
            //没有权限,申请一下
            ActivityCompat.requestPermissions(this,
                    new String[]{permission},
                    1);
        }
    }else{
        onPermissionListener.onClick(true);
        Log.d("MainActivity", "2"+ContextCompat.checkSelfPermission(this,
                permission));
    }
}

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    if (requestCode == 1) {
        if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            //权限通过
            if (onPermissionListener != null) {
                onPermissionListener.onClick(true);
            }
        } else {
            //权限拒绝
            if (onPermissionListener != null) {
                onPermissionListener.onClick(false);
            }
        }
        return;
    }
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}

然后在你需要申请权限的地方调用这个方法就可以了

onPermissionRequests

②.  7.0相机问题

Android7.0中尝试传递 file:// URI 会触发 FileUriExposedException,因为在Android7.0之后Google认为直接使用本地

的根目录即file:// URI是不安全的操作,直接访问会抛出FileUriExposedExCeption异常,这就意味着在Android7.0以前

我们访问相机拍照存储时,如果使用URI的方式直接存储剪裁图片就会造成这个异常,那么如何解决这个问题呢?

 Google为我们提供了FileProvider类,进行一种特殊的内容提供,FileProvider时ContentProvide的子类,它使用了和

内容提供器类似的机制来对数据进行保护,可以选择性地将封装过的Uri共享给外部,从而提高了应用的安全性。下面

就让我们看一下如何使用这个内容提供者进行数据访问的:

      使用FileProvider获取Uri就会将以前的file:// URI准换成content:// URI,实现一种安全的应用间数据访问,内容提 

供者作为Android的四大组件之一,使用同样需要在清单文件AndroidManifest.xml中进行注册的,注册方法如下:首先

是准备工作,先在res文件夹下添加xml文件夹,然后创建一个文件file_paths,内容如下:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">

    <!--填写你要所要申请访问的目录地址,name最好是你的目录名,path是你要申请的目录-->
    <external-path name="camera_photos" path="."  />
</paths>

然后在AndroidManifest.xml文件中添加

<!-- 这里直接复制就可以,需要注意的是authorities,以你的包名加上fileprovider,因为他需要唯一 -->
<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="包名.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths"/>
</provider>

这样准备工作就做好了,不要纠结着FileProvider,到时候直接用就可以,下面是调用代码:

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File photoFile = createImagePathFile(MainActivity.this);
intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);

/*
* 这里就是高版本需要注意的,需用使用FileProvider来获取Uri,同时需要注意getUriForFile
* 方法第二个参数要与AndroidManifest.xml中provider的里面的属性authorities的值一致
* */
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
imageUriFromCamera = FileProvider.getUriForFile(MainActivity.this,
        "包名.fileprovider", photoFile);
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUriFromCamera);
startActivityForResult(intent, GET_IMAGE_BY_CAMERA_U);

然后就没有然后了,结尾附几个Demo,很简单:

https://github.com/xuezj/FileProviderDemo  

https://github.com/zhengzhong1/Android6.0PermissionsDemo

https://github.com/HelloWmz/PhotoDemo

2.直接在webview中点击事件调用摄像头和相册就得重写webcromeClient方法

WebViewClient webViewClient = new WebViewClient() {
    public OkHttpClient client = new OkHttpClient();

    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        view.loadUrl(url);
        return true;
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    @Override
    public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {

        // ------5.0以上手机执行------
        Uri uri = request.getUrl();
        String url = uri.toString();
        return shouldInterceptRequest(view, url);
    }


    @Override
    public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
        String newUrl = StringUtil.getNewUrl(url);
        Utils.CommonUtilLog(TAG, "old url=" + url);
        Utils.CommonUtilLog(TAG, "new url=" + newUrl);


        if (newUrl.equals(url)) {
            return super.shouldInterceptRequest(view, url);
        }


        Request request1 = new Request.Builder()
                .url(newUrl)
                .build();
        Response response = null;
        try {
            response = client.newCall(request1).execute();
        } catch (IOException e) {
            e.printStackTrace();
        }
        String type = Utils.getMimeType(newUrl);
        Utils.CommonUtilLog(TAG, "type=" + type);
        return new WebResourceResponse(type, response.header("content-encoding", "utf-8"), response.body().byteStream());
    }

};

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_BACK && mWebView.canGoBack()) {
        mWebView.goBack();
        return true;
    }

    return super.onKeyDown(keyCode, event);
}

@Override
protected void onPause() {
    super.onPause();

}

@Override
protected void onDestroy() {
    super.onDestroy();
    if (mWebView != null) {
        mWebView.clearHistory();
        ((ViewGroup) mWebView.getParent()).removeView(mWebView);
        mWebView.loadUrl("about:blank");
        mWebView.stopLoading();
        mWebView.setWebChromeClient(null);
        mWebView.setWebViewClient(null);
        mWebView.destroy();
        mWebView = null;
    }
}

/*
*=====HTML5调相机相册======
* 重写WebChromeClient方法
*/

WebChromeClient webChromeClient = new WebChromeClient() {
    @Override
    public void onProgressChanged(WebView view, int newProgress) {
        super.onProgressChanged(view, newProgress);

    }
    @Override
    public boolean onShowFileChooser(WebView webView,
                                     ValueCallback<Uri[]> filePathCallback,
                                     FileChooserParams fileChooserParams) {
        mUploadCallbackAboveL = filePathCallback;
        take();
        return true;
    }


    public void openFileChooser(ValueCallback<Uri> uploadMsg) {
        mUploadMessage = uploadMsg;
        take();
    }

    public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
        mUploadMessage = uploadMsg;
        take();
    }

    public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
        mUploadMessage = uploadMsg;
        take();
    }
};
private void take() {
    File imageStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "MyApp");        // Create the storage directory if it does not exist
    if (!imageStorageDir.exists()) {
        imageStorageDir.mkdirs();
    }
    File file = new File(imageStorageDir + File.separator + "IMG_" + String.valueOf(System.currentTimeMillis()) + ".jpg");
    imageUri = Uri.fromFile(file);
    final List<Intent> cameraIntents = new ArrayList();
    final Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    final PackageManager packageManager = getPackageManager();
    final List<ResolveInfo> listCam = packageManager.queryIntentActivities(captureIntent, 0);
    for (ResolveInfo res : listCam) {
        final String packageName = res.activityInfo.packageName;
        final Intent i = new Intent(captureIntent);
        i.setComponent(new ComponentName(res.activityInfo.packageName, res.activityInfo.name));
        i.setPackage(packageName);
        i.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
        cameraIntents.add(i);

    }
    Intent i = new Intent(Intent.ACTION_GET_CONTENT);
    i.addCategory(Intent.CATEGORY_OPENABLE);
    i.setType("image/*");
    Intent chooserIntent = Intent.createChooser(i, "Image Chooser");
    chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, cameraIntents.toArray(new Parcelable[]{}));
    RemoteWebActivity.this.startActivityForResult(chooserIntent, FILE_CHOOSER_RESULT_CODE);
}

@SuppressWarnings("null")
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private void onActivityResultAboveL(int requestCode, int resultCode, Intent data) {
    if (requestCode != FILE_CHOOSER_RESULT_CODE
            || mUploadCallbackAboveL == null) {
        return;
    }
    Uri[] results = null;
    if (resultCode == Activity.RESULT_OK) {
        if (data == null) {

            results = new Uri[]{imageUri};
        } else {
            String dataString = data.getDataString();
            ClipData clipData = data.getClipData();
            if (clipData != null) {
                results = new Uri[clipData.getItemCount()];
                for (int i = 0; i < clipData.getItemCount(); i++) {
                    ClipData.Item item = clipData.getItemAt(i);
                    results[i] = item.getUri();
                }
            }
            if (dataString != null)
                results = new Uri[]{Uri.parse(dataString)};
        }
    }
    if (results != null) {
        mUploadCallbackAboveL.onReceiveValue(results);
        mUploadCallbackAboveL = null;
    } else {
        results = new Uri[]{imageUri};
        mUploadCallbackAboveL.onReceiveValue(results);
        mUploadCallbackAboveL = null;
    }
    return;
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == FILE_CHOOSER_RESULT_CODE) {
        if (null == mUploadMessage && null == mUploadCallbackAboveL) return;
        Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();
        if (mUploadCallbackAboveL != null) {
            onActivityResultAboveL(requestCode, resultCode, data);
        } else if (mUploadMessage != null) {
            if (result != null) {
                String path = getPath(getApplicationContext(),
                        result);
                Uri uri = Uri.fromFile(new File(path));
                mUploadMessage
                        .onReceiveValue(uri);
            } else {
                mUploadMessage.onReceiveValue(imageUri);
            }
            mUploadMessage = null;

        }
    }
}

@SuppressLint("NewApi")
@TargetApi(Build.VERSION_CODES.KITKAT)
public static String getPath(final Context context, final Uri uri) {
    final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
    // DocumentProvider
    if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
        // ExternalStorageProvider
        if (isExternalStorageDocument(uri)) {
            final String docId = DocumentsContract.getDocumentId(uri);
            final String[] split = docId.split(":");
            final String type = split[0];
            if ("primary".equalsIgnoreCase(type)) {
                return Environment.getExternalStorageDirectory() + "/" + split[1];
            }                // TODO handle non-primary volumes
        } else if (isDownloadsDocument(uri)) {
            final String id = DocumentsContract.getDocumentId(uri);
            final Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
            return getDataColumn(context, contentUri, null, null);
        }
    }
    // MediaStore (and general)
    else if ("content".equalsIgnoreCase(uri.getScheme())) {
        return getDataColumn(context, uri, null, null);
    }
    // File
    else if ("file".equalsIgnoreCase(uri.getScheme())) {
        return uri.getPath();
    }
    return null;
}
public static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {
    Cursor cursor = null;
    final String column = "_data";
    final String[] projection = {column};
    try {
        cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
        if (cursor != null && cursor.moveToFirst()) {
            final int column_index = cursor.getColumnIndexOrThrow(column);
            return cursor.getString(column_index);
        }
    } finally {
        if (cursor != null) cursor.close();
    }
    return null;
}
public static boolean isExternalStorageDocument(Uri uri) {
    return "com.android.externalstorage.documents".equals(uri.getAuthority());
}
/**
 * @param uri The Uri to check.
 * @return Whether the Uri authority is DownloadsProvider.
 */
public static boolean isDownloadsDocument(Uri uri) {
    return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}

在h5界面必须有那么一行代码     

<div class="jdmodel-first-bottom">
	<img class="jdmodel-first-bottom-button" src="/statics/jdmodel/css/image/bottom.png"/>
	<input type="file" id="cameraInput" style="display: none" accept="image/*"/>
	<img class="jdmodel-first-bottom-photograph" src="/statics/jdmodel/css/image/photograph.png" id="photoBtnId"/>
</div>

 

返回后的图片会放到id为cameraInput的界面位置!!!

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Webview是一种嵌入Android应用程序内的网页视图控件,可以通过WebView来显示和加载网页内容,同时也支持一些交互功能,比如调用相机、定位等。H5是一种基于HTML和CSS的网页开发技术,可以在Webview中使用。以下是Webview H5调用相机的Demo: 1. HTML代码: ``` <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>调用相机Demo</title> <style> img { width: 300px; height: auto; } </style> </head> <body> <h1>Webview H5调用相机Demo</h1> <p>请点击下面的按钮拍摄照片:</p> <button onclick="takePhoto()">拍照</button> <img id="photo" src="" alt="" /> <script> function takePhoto() { // 调用Android相机接口 android.takePhoto(); } function setPhoto(dataUrl) { // 获取拍照后的base64编码数据,并显示在img标签中 var photo = document.getElementById('photo'); photo.src = dataUrl; photo.style.display = 'block'; } </script> </body> </html> ``` 2. Java代码: ```java public class MainActivity extends AppCompatActivity { private WebView webView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); webView = findViewById(R.id.webview); webView.getSettings().setJavaScriptEnabled(true); webView.setWebChromeClient(new MyWebChromeClient(this)); webView.addJavascriptInterface(new JsInterface(this), "android"); webView.loadUrl("file:///android_asset/index.html"); } static class MyWebChromeClient extends WebChromeClient { private Activity mActivity; MyWebChromeClient(Activity mActivity) { this.mActivity = mActivity; } @Override public void onPermissionRequest(final PermissionRequest request) { mActivity.runOnUiThread(new Runnable() { @Override public void run() { request.grant(request.getResources()); } }); } } static class JsInterface { private Activity mActivity; JsInterface(Activity mActivity) { this.mActivity = mActivity; } // 声明被JavaScript调用的方法 @JavascriptInterface public void takePhoto() { Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); if (takePictureIntent.resolveActivity(mActivity.getPackageManager()) != null) { mActivity.startActivityForResult(takePictureIntent, 1); } } // 声明被Android调用的方法 @SuppressWarnings("unused") @JavascriptInterface public void setPhoto(String dataUrl) { final String finalDataUrl = dataUrl; mActivity.runOnUiThread(new Runnable() { @Override public void run() { webView.loadUrl("javascript:setPhoto('" + finalDataUrl + "');"); } }); } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == 1 && resultCode == RESULT_OK) { Bundle extras = data.getExtras(); Bitmap photo = (Bitmap) extras.get("data"); String dataUrl = "data:image/jpeg;base64," + bitmapToBase64(photo); webView.loadUrl("javascript:setPhoto('" + dataUrl + "');"); } } private String bitmapToBase64(Bitmap bitmap) { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.JPEG, 100, byteArrayOutputStream); byte[] byteArray = byteArrayOutputStream.toByteArray(); return Base64.encodeToString(byteArray, Base64.DEFAULT); } } ``` 这个Demo的实现过程比较简单,大致思路如下: 1. 在HTML代码中添加一个按钮和一个img标签,用于拍照和显示照片。 2. 在Angular中,将这个函数作为一个模块,供调用。比如这样一个调用摄像头的模块: ```javascript angular.module('demo', []) .controller('DemoController', function($scope) { $scope.takePhoto = function() { // 调用相机 navigator.camera.getPicture($scope.onPhotoSuccess, $scope.onPhotoFail, { quality: 50, destinationType: Camera.DestinationType.DATA_URL }); }; $scope.onPhotoSuccess = function(imageData) { // 显示照片 $scope.photo = "data:image/jpeg;base64," + imageData; }; $scope.onPhotoFail = function() { alert('拍照失败!'); }; }); ``` 3. 在Java代码中,创建一个WebChromeClient类,用于处理权限请求。在做Android 6.0 runtime权限处理时,无法直接使用`setWebChromeClient`函数,需要重载该函数以保证打开相机时不需要再次请求权限。 ```java static class MyWebChromeClient extends WebChromeClient { private Activity mActivity; MyWebChromeClient(Activity mActivity) { this.mActivity = mActivity; } @Override public void onPermissionRequest(final PermissionRequest request) { mActivity.runOnUiThread(new Runnable() { @Override public void run() { request.grant(request.getResources()); } }); } } ``` 4. 在Java的`JsInterface`类中,声明一个被JavaScript调用的方法`takePhoto()`,该方法通过意图调用系统相机应用。照片拍摄结束后,通过调用另一个被Android调用的方法`setPhoto(String dataUrl)`将照片通过WebView的`loadUrl()`函数传递回JavaScript中。 ```java static class JsInterface { private Activity mActivity; JsInterface(Activity mActivity) { this.mActivity = mActivity; } @JavascriptInterface public void takePhoto() { Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); if (takePictureIntent.resolveActivity(mActivity.getPackageManager()) != null) { mActivity.startActivityForResult(takePictureIntent, 1); } } @SuppressWarnings("unused") @JavascriptInterface public void setPhoto(String dataUrl) { final String finalDataUrl = dataUrl; mActivity.runOnUiThread(new Runnable() { @Override public void run() { webView.loadUrl("javascript:setPhoto('" + finalDataUrl + "');"); } }); } } ``` 5. 在Java代码的`onActivityResult()`方法中获取拍摄后的照片,并通过`loadUrl()`函数传递到JavaScript中。 ```java @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == 1 && resultCode == RESULT_OK) { Bundle extras = data.getExtras(); Bitmap photo = (Bitmap) extras.get("data"); String dataUrl = "data:image/jpeg;base64," + bitmapToBase64(photo); webView.loadUrl("javascript:setPhoto('" + dataUrl + "');"); } } ``` 以上就是一个简单的Webview H5调用相机Demo的实现过程。注意,该Demo中的实现方式仅供参考,实际应用中应该严格控制照片的安全性和隐私保护。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值