Android 8.0 WebView 拍照、简易预览、二维码扫描 各种问题解决
项目用到了WebView包装HTML5做成app使用,其中有页面用到了二维码和拍照上传功能。本人从未做过android,短时间内完成,只能靠“热心网友”帮忙了,网上也铺天盖地各种demo和文章。
但是对于高版本,特别是android 8.0以上,网上的各种现成的Demo都不好用,各种问题。现在我成功了解决了这些问题,并汇总供初学者参考。
1. 权限问题
这个问题也是困扰最多的一个问题,各种Android版本对于权限的限制和提示不一样,特别是在我的Mate9(Android 8.0)上一度不提示,后台也不报错,但是浏览器就是在拍照后无法访问图片,搞了很久才知道是没有在代码里加动态权限检查。AndroidManifest.xml文件只是注册了需要用哪些权限并不是app获取到的权限,高版本android在安装app时忽略提示权限,所以一般app首次安装打开后,都会提示你需要什么权限。
由此,建议在首页的Activity的onCreate方法里,增加权限检查。
checkSelfPermission(Manifest.permission.CAMERA);
checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE);
等等,如果只用到了拍照,那么这2个就够了,可存比可读,不用再赋读取权限。
对于权限检查,有很多问题需要考虑,比如禁止后,怎么办?如果用户勾选“禁止后不再询问”,怎么办?
我这边的处理是如下:
Activity的回调方法onRequestPermissionsResult
-
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
-
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
-
HandleMainActivityResult.onRequestPermissionsResult( this,requestCode,permissions,grantResults);
-
}
-
//权限回调方法(默认知道某个requestCode赋值几个权限,数组大小为几)
-
public static void onRequestPermissionsResult(final MainActivity activity, int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
-
if (requestCode == ActivityResultConst.CODE_FOR_WRITE_PERMISSION) {
-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
-
boolean isShow = true;
-
Log.e(TAG, "grantResults.length:"+grantResults.length);
-
String tipTitle = "权限不可用";
-
if(grantResults.length == 2){
-
if (grantResults[ 0] != PackageManager.PERMISSION_GRANTED && grantResults[ 1] != PackageManager.PERMISSION_GRANTED) {
-
tipTitle = "相机和存储权限不可用";
-
} else if(grantResults[ 0] != PackageManager.PERMISSION_GRANTED){
-
tipTitle = "相机权限不可用";
-
} else if(grantResults[ 1] != PackageManager.PERMISSION_GRANTED){
-
tipTitle = "存储权限不可用";
-
} else {
-
//权限已经全部赋值成功
-
isShow = false;
-
}
-
// 判断用户是否 点击了不再提醒。(检测该权限是否还可以申请)
-
boolean completeForbidden1 = activity.shouldShowRequestPermissionRationale(permissions[ 0]);
-
boolean completeForbidden2 = activity.shouldShowRequestPermissionRationale(permissions[ 1]);
-
Log.e(TAG, permissions[ 0] + " isCompleteForbidden: " + completeForbidden1 + ";" + permissions[ 1] + " isCompleteForbidden: " + completeForbidden2);
-
if(!completeForbidden1 || !completeForbidden2){
-
//用户点击了禁止后不再询问,建议直接提示并退出
-
Toast.makeText(activity.getApplicationContext(), "以后可在-应用设置-权限管理-中,手动开启权限", Toast.LENGTH_SHORT).show();
-
} else {
-
if (isShow) {
-
new AlertDialog.Builder(activity)
-
.setTitle(tipTitle)
-
.setMessage( "由于手机助手需要拍照上传和扫描二维码功能,请开启权限;\n否则,您将无法正常使用")
-
.setPositiveButton( "立即开启", new DialogInterface.OnClickListener() {
-
-
public void onClick(DialogInterface dialog, int which) {
-
PermissionHandler.checkPermissionForCameraAndWriteStorage(activity);
-
}
-
})
-
.setNegativeButton( "取消", new DialogInterface.OnClickListener() {
-
-
public void onClick(DialogInterface dialog, int which) {
-
//Toast.makeText(activity.getApplicationContext(), "以后可在-应用设置-权限-中,手动开启权限", Toast.LENGTH_SHORT).show();
-
}
-
}).setCancelable( false).show();
-
}
-
}
-
} else {
-
PermissionHandler.checkPermissionForCameraAndWriteStorage(activity);
-
}
-
}
-
}
-
}
如果用户禁止了某个权限,而你程序又运行到此处怎么办?后台会直接报错,我的处理办法是try catch 然后进行权限提示(注意不是权限检查,因为这里不应该进行检查也不适合进行检查)。看我的webChromeClient子类代码:
-
-
public boolean onShowFileChooser(WebView webView,
-
ValueCallback<Uri[]> filePathCallback,
-
FileChooserParams fileChooserParams) {
-
mainActivity.setmUploadCallbackAboveL(filePathCallback);
-
try {
-
PhotoUtil.take(mainActivity);
-
} catch (java.lang.SecurityException e){
-
Log.e(TAG,e.getMessage(),e);
-
if (e.getMessage() != null && e.getMessage().indexOf( "Permission Denial") != - 1) {
-
String tipTitle = "缺少权限";
-
if (e.getMessage().indexOf( "android.permission.WRITE_EXTERNAL_STORAGE") != - 1) {
-
tipTitle = "缺少存储权限";
-
} else if(e.getMessage().indexOf( "android.permission.CAMERA") != - 1){
-
tipTitle = "缺少相机权限";
-
}
-
new AlertDialog.Builder(mainActivity)
-
.setTitle( "温馨提示")
-
.setMessage(tipTitle + ",可在-应用设置-权限管理-中,手动开启")
-
.setPositiveButton( "知道了", new DialogInterface.OnClickListener() {
-
-
public void onClick(DialogInterface dialog, int which) {
-
//Do Nothing
-
}
-
}).setCancelable( false).show();
-
}
-
}
-
return true;
-
}
也许有更好的办法,也许有第三方框架可以解决这个问题,但是作为初学者的我,目前先这么处理吧。
这里有个问题需要讨论:能不能在使用时才增加权限检查,而不是(首次)打开app首页时检查?
告诉你,这个是不行的!在使用时,动态检查权限,虽然app会跳出权限提示的dialog提示,但是Activity的onRequestPermissionsResult方法回调会出问题,导致你页面调整异常。这也就是为什么大部分app都是安装后,首次打开,会进行一连串权限提醒的原因。别问我为什么知道,我被坑了很久。
2.调用相机问题
如果是android 低版本,随便调(单独调用相机,不浏览相册),几行代码即可:
-
Intent intentCamera = new Intent();
-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
-
intentCamera.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); //添加这一句表示对目标应用临时授权该Uri所代表的文件
-
}
-
intentCamera.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
-
//将拍照结果保存至photo_file的Uri中,不保留在相册中
-
intentCamera.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
-
startActivityForResult(intentCamera, FILECHOOSER_RESULTCODE);
-
//兼容android 7.0+版本的照相机调用代码
-
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
-
if (intent.resolveActivity(mainActivity.getPackageManager()) != null) {
-
/*获取当前系统的android版本号*/
-
int currentapiVersion = android.os.Build.VERSION.SDK_INT;
-
Log.e(TAG, "currentapiVersion====>"+currentapiVersion);
-
if (currentapiVersion< 24){
-
intent.putExtra(MediaStore.EXTRA_OUTPUT, mainActivity.getImageUri());
-
mainActivity.startActivityForResult(intent, ActivityResultConst.CAMERA_RESULTCODE);
-
} else {
-
ContentValues contentValues = new ContentValues( 1);
-
contentValues.put(MediaStore.Images.Media.DATA, file.getAbsolutePath());
-
Uri uri = mainActivity.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,contentValues);
-
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
-
mainActivity.startActivityForResult(intent, ActivityResultConst.CAMERA_RESULTCODE);
-
}
-
} else {
-
Toast.makeText(mainActivity, "照相机不存在", Toast.LENGTH_SHORT).show();
-
}
android 7.0以上无法调用相机,网上有网友这么建议,在onCreate方法里加入如下代码:
-
//android 7.0系统解决拍照的问题
-
StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
-
StrictMode.setVmPolicy(builder.build());
-
builder.detectFileUriExposure();
如果需要调用相机或者打开相册,那么可以这么搞:
-
//调用照相机和浏览图片库代码
-
Intent captureIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
-
captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, mainActivity.getImageUri());
-
-
Intent Photo = new Intent(Intent.ACTION_PICK,
-
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
-
-
Intent chooserIntent = Intent.createChooser(Photo, "Image Chooser");
-
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Parcelable[]{captureIntent});
-
-
mainActivity.startActivityForResult(chooserIntent, ActivityResultConst.CAMERA_RESULTCODE);
-
private static void updatePhotos(MainActivity activity) {
-
// 该广播即使多发(即选取照片成功时也发送)也没有关系,只是唤醒系统刷新媒体文件
-
Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
-
intent.setData(activity.getImageUri());
-
activity.sendBroadcast(intent);
-
}
I/Choreographer: Skipped 179 frames! The application may be doing too much work on its main thread.
这种情况建议压缩图片,比如压缩成800*480,最多几百K,基本不会出问题。压缩代码见我上传的demo:
-
"null")(
-
-
private static void onActivityResultAboveL(MainActivity activity, int requestCode, int resultCode, Intent data) {
-
if (requestCode != ActivityResultConst.CAMERA_RESULTCODE
-
|| activity.getmUploadCallbackAboveL() == null) {
-
return;
-
}
-
Uri[] results = null;
-
if (resultCode == Activity.RESULT_OK) {
-
if (data == null) {
-
results = new Uri[]{activity.getImageUri()};
-
} 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) {
-
//压缩
-
results = PhotoUtil.doCompressImageForActivityResult(activity,results);
-
activity.getmUploadCallbackAboveL().onReceiveValue(results);
-
activity.setmUploadCallbackAboveL( null);
-
} else {
-
//压缩
-
results = new Uri[]{activity.getImageUri()};
-
results = PhotoUtil.doCompressImageForActivityResult(activity,results);
-
activity.getmUploadCallbackAboveL().onReceiveValue(results);
-
activity.setmUploadCallbackAboveL( null);
-
}
-
return;
-
}
results = PhotoUtil.doCompressImageForActivityResult(activity,results);
最后还有几个小问题,需要注意一下:
a. 二维码扫描之后,如果要返回,需要考虑是不是goback到父的父页面,不然扫描完一点击手机的返回键,直接就打开了照相机。
b. 我在AndroidManifest.xml文件给webview加了一个背景图片。
代码如 android:roundIcon="@mipmap/ic_launcher_round"
c. 我对MainActivity进行了一些简答的封装,不至于让全部的代码,都写在MainActivity.java中。
封装得很没有水平,用j2ee的思维在弄android,实在没办法。
-
ContentValues contentValues = new ContentValues( 1);
-
contentValues.put(MediaStore.Images.Media.DATA, file.getAbsolutePath());
-
Uri uri = mainActivity.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,contentValues);
-
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
-
mainActivity.startActivityForResult(intent, ActivityResultConst.CAMERA_RESULTCODE);
ActivityResultConst.CAMERA_RESULTCODE是我定义的常量类属性,不同的数字代表不同的请求码。
但是问题来了,扫描二维码,我用页面JavaScript调用BarcodeCallBack类实现的,好像没有进行Activity的Intent操作,无法设置或取得请求码,怎么办呢,我用了个非常挫的办法,在MainActivity里定义了一个Map,然后扫描二维码初始化时手动往这里面放一个常量,作为请求码,在onActivityResult方法里处理判断:
-
-
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
-
super.onActivityResult(requestCode, resultCode, data);
-
//自定义Activity返回结果码
-
Integer extraRequestCode = (Integer) extra.get(ActivityResultConst.REQUEST_CODE_KEY);
-
if(extraRequestCode == null || extraRequestCode == 0){
-
//如果没有自定义返回结果码,则使用onActivityResult的参数
-
extraRequestCode = requestCode;
-
}
-
//根据不同的返回码,执行不同的结果处理(一般指回调操作)
-
if (extraRequestCode == ActivityResultConst.BARCODE_RESULTCODE) {
-
HandleMainActivityResult.onBarcodeResult( this, requestCode, resultCode, data);
-
}
-
if (extraRequestCode == ActivityResultConst.CAMERA_RESULTCODE) {
-
HandleMainActivityResult.onCameraResult( this,requestCode,resultCode,data);
-
}
-
extra.clear();
-
}
e. 发现了一个比较好玩的事情:如果在AndroidManifest.xml里未注册相机权限,则似乎不需要动态检查相机权限,在安装时,权限列表里也未发现有相机权限,但是事实上,相机却可以正常调用。
如果在AndroidManifest.xml里注册相机权限,那么对不起,需要你在onCreate方法里动态检查相机权限,否则后台报错,相机无法调起。
说明相机这个权限很弱,默认可以让app使用,估计用一下,也不会怎么样,把系统搞崩溃吧。
f.如果你已经打开了照相机,这时,你按手机的回退键取消操作,再重新进来时,你会发现相机已经无法被调起。
原因是:因为对于页面表单<input type=file >来说,调用照相机相当于选择图片。而选择图片这个事件必须要有一个返回值,不然程序会一直处于等待状态,当你没有选定的时候你要传回一个null,否则程序就一直阻塞,就不能进行其它操作。
处理代码在Activity的方法onActivityResult(int requestCode, int resultCode, Intent data),对于我demo代码,也就是方法onCameraResult(MainActivity activity, int requestCode, int resultCode, Intent data)里加上取消操作的判断
-
public static void onCameraResult(MainActivity activity, int requestCode, int resultCode, Intent data) {
-
if(resultCode == Activity.RESULT_CANCELED) {
-
//打开相机,若取消,必须要设置一个null返回值,不然页面会一直处于等待状态,阻塞住无法响应
-
if(activity.getmUploadCallbackAboveL() != null){
-
activity.getmUploadCallbackAboveL().onReceiveValue( null);
-
}
-
if(activity.getmUploadMessage() != null){
-
activity.getmUploadMessage().onReceiveValue( null);
-
}
-
return;
-
}
-
updatePhotos(activity);
-
if ( null == activity.getmUploadMessage() && null == activity.getmUploadCallbackAboveL())
-
return;
-
Uri result = data == null || resultCode != Activity.RESULT_OK ? null : data.getData();
-
if (activity.getmUploadCallbackAboveL() != null) {
-
onActivityResultAboveL(activity, requestCode, resultCode, data);
-
} else if (activity.getmUploadMessage() != null) {
-
Log.e( "result", result + "");
-
if (result == null) {
-
//低版本,暂时不做压缩处理
-
activity.getmUploadMessage().onReceiveValue(activity.getImageUri());
-
activity.setmUploadMessage( null);
-
Log.e( "imageUri", activity.getImageUri() + "");
-
} else {
-
//压缩
-
result = PhotoUtil.doCompressImageForActivityResult(activity, data, null);
-
activity.getmUploadMessage().onReceiveValue(result);
-
activity.setmUploadMessage( null);
-
}
-
}
-
}
上面就是我完成的demo遇到的各种问题的汇总介绍。
如果有人觉得繁琐,那么我再上传一个最简答的demo版本,拍照预览功能的。(上传自己用jquery弄下,图片都被你file标签获取到了)。 点击下载 最简易版本的demo。
项目用到了WebView包装HTML5做成app使用,其中有页面用到了二维码和拍照上传功能。本人从未做过android,短时间内完成,只能靠“热心网友”帮忙了,网上也铺天盖地各种demo和文章。
但是对于高版本,特别是android 8.0以上,网上的各种现成的Demo都不好用,各种问题。现在我成功了解决了这些问题,并汇总供初学者参考。
1. 权限问题
这个问题也是困扰最多的一个问题,各种Android版本对于权限的限制和提示不一样,特别是在我的Mate9(Android 8.0)上一度不提示,后台也不报错,但是浏览器就是在拍照后无法访问图片,搞了很久才知道是没有在代码里加动态权限检查。AndroidManifest.xml文件只是注册了需要用哪些权限并不是app获取到的权限,高版本android在安装app时忽略提示权限,所以一般app首次安装打开后,都会提示你需要什么权限。
由此,建议在首页的Activity的onCreate方法里,增加权限检查。
checkSelfPermission(Manifest.permission.CAMERA);
checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE);
等等,如果只用到了拍照,那么这2个就够了,可存比可读,不用再赋读取权限。
对于权限检查,有很多问题需要考虑,比如禁止后,怎么办?如果用户勾选“禁止后不再询问”,怎么办?
我这边的处理是如下:
Activity的回调方法onRequestPermissionsResult
-
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
-
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
-
HandleMainActivityResult.onRequestPermissionsResult( this,requestCode,permissions,grantResults);
-
}
-
//权限回调方法(默认知道某个requestCode赋值几个权限,数组大小为几)
-
public static void onRequestPermissionsResult(final MainActivity activity, int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
-
if (requestCode == ActivityResultConst.CODE_FOR_WRITE_PERMISSION) {
-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
-
boolean isShow = true;
-
Log.e(TAG, "grantResults.length:"+grantResults.length);
-
String tipTitle = "权限不可用";
-
if(grantResults.length == 2){
-
if (grantResults[ 0] != PackageManager.PERMISSION_GRANTED && grantResults[ 1] != PackageManager.PERMISSION_GRANTED) {
-
tipTitle = "相机和存储权限不可用";
-
} else if(grantResults[ 0] != PackageManager.PERMISSION_GRANTED){
-
tipTitle = "相机权限不可用";
-
} else if(grantResults[ 1] != PackageManager.PERMISSION_GRANTED){
-
tipTitle = "存储权限不可用";
-
} else {
-
//权限已经全部赋值成功
-
isShow = false;
-
}
-
// 判断用户是否 点击了不再提醒。(检测该权限是否还可以申请)
-
boolean completeForbidden1 = activity.shouldShowRequestPermissionRationale(permissions[ 0]);
-
boolean completeForbidden2 = activity.shouldShowRequestPermissionRationale(permissions[ 1]);
-
Log.e(TAG, permissions[ 0] + " isCompleteForbidden: " + completeForbidden1 + ";" + permissions[ 1] + " isCompleteForbidden: " + completeForbidden2);
-
if(!completeForbidden1 || !completeForbidden2){
-
//用户点击了禁止后不再询问,建议直接提示并退出
-
Toast.makeText(activity.getApplicationContext(), "以后可在-应用设置-权限管理-中,手动开启权限", Toast.LENGTH_SHORT).show();
-
} else {
-
if (isShow) {
-
new AlertDialog.Builder(activity)
-
.setTitle(tipTitle)
-
.setMessage( "由于手机助手需要拍照上传和扫描二维码功能,请开启权限;\n否则,您将无法正常使用")
-
.setPositiveButton( "立即开启", new DialogInterface.OnClickListener() {
-
-
public void onClick(DialogInterface dialog, int which) {
-
PermissionHandler.checkPermissionForCameraAndWriteStorage(activity);
-
}
-
})
-
.setNegativeButton( "取消", new DialogInterface.OnClickListener() {
-
-
public void onClick(DialogInterface dialog, int which) {
-
//Toast.makeText(activity.getApplicationContext(), "以后可在-应用设置-权限-中,手动开启权限", Toast.LENGTH_SHORT).show();
-
}
-
}).setCancelable( false).show();
-
}
-
}
-
} else {
-
PermissionHandler.checkPermissionForCameraAndWriteStorage(activity);
-
}
-
}
-
}
-
}
如果用户禁止了某个权限,而你程序又运行到此处怎么办?后台会直接报错,我的处理办法是try catch 然后进行权限提示(注意不是权限检查,因为这里不应该进行检查也不适合进行检查)。看我的webChromeClient子类代码:
-
-
public boolean onShowFileChooser(WebView webView,
-
ValueCallback<Uri[]> filePathCallback,
-
FileChooserParams fileChooserParams) {
-
mainActivity.setmUploadCallbackAboveL(filePathCallback);
-
try {
-
PhotoUtil.take(mainActivity);
-
} catch (java.lang.SecurityException e){
-
Log.e(TAG,e.getMessage(),e);
-
if (e.getMessage() != null && e.getMessage().indexOf( "Permission Denial") != - 1) {
-
String tipTitle = "缺少权限";
-
if (e.getMessage().indexOf( "android.permission.WRITE_EXTERNAL_STORAGE") != - 1) {
-
tipTitle = "缺少存储权限";
-
} else if(e.getMessage().indexOf( "android.permission.CAMERA") != - 1){
-
tipTitle = "缺少相机权限";
-
}
-
new AlertDialog.Builder(mainActivity)
-
.setTitle( "温馨提示")
-
.setMessage(tipTitle + ",可在-应用设置-权限管理-中,手动开启")
-
.setPositiveButton( "知道了", new DialogInterface.OnClickListener() {
-
-
public void onClick(DialogInterface dialog, int which) {
-
//Do Nothing
-
}
-
}).setCancelable( false).show();
-
}
-
}
-
return true;
-
}
也许有更好的办法,也许有第三方框架可以解决这个问题,但是作为初学者的我,目前先这么处理吧。
这里有个问题需要讨论:能不能在使用时才增加权限检查,而不是(首次)打开app首页时检查?
告诉你,这个是不行的!在使用时,动态检查权限,虽然app会跳出权限提示的dialog提示,但是Activity的onRequestPermissionsResult方法回调会出问题,导致你页面调整异常。这也就是为什么大部分app都是安装后,首次打开,会进行一连串权限提醒的原因。别问我为什么知道,我被坑了很久。
2.调用相机问题
如果是android 低版本,随便调(单独调用相机,不浏览相册),几行代码即可:
-
Intent intentCamera = new Intent();
-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
-
intentCamera.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); //添加这一句表示对目标应用临时授权该Uri所代表的文件
-
}
-
intentCamera.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
-
//将拍照结果保存至photo_file的Uri中,不保留在相册中
-
intentCamera.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
-
startActivityForResult(intentCamera, FILECHOOSER_RESULTCODE);
-
//兼容android 7.0+版本的照相机调用代码
-
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
-
if (intent.resolveActivity(mainActivity.getPackageManager()) != null) {
-
/*获取当前系统的android版本号*/
-
int currentapiVersion = android.os.Build.VERSION.SDK_INT;
-
Log.e(TAG, "currentapiVersion====>"+currentapiVersion);
-
if (currentapiVersion< 24){
-
intent.putExtra(MediaStore.EXTRA_OUTPUT, mainActivity.getImageUri());
-
mainActivity.startActivityForResult(intent, ActivityResultConst.CAMERA_RESULTCODE);
-
} else {
-
ContentValues contentValues = new ContentValues( 1);
-
contentValues.put(MediaStore.Images.Media.DATA, file.getAbsolutePath());
-
Uri uri = mainActivity.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,contentValues);
-
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
-
mainActivity.startActivityForResult(intent, ActivityResultConst.CAMERA_RESULTCODE);
-
}
-
} else {
-
Toast.makeText(mainActivity, "照相机不存在", Toast.LENGTH_SHORT).show();
-
}
android 7.0以上无法调用相机,网上有网友这么建议,在onCreate方法里加入如下代码:
-
//android 7.0系统解决拍照的问题
-
StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
-
StrictMode.setVmPolicy(builder.build());
-
builder.detectFileUriExposure();
如果需要调用相机或者打开相册,那么可以这么搞:
-
//调用照相机和浏览图片库代码
-
Intent captureIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
-
captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, mainActivity.getImageUri());
-
-
Intent Photo = new Intent(Intent.ACTION_PICK,
-
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
-
-
Intent chooserIntent = Intent.createChooser(Photo, "Image Chooser");
-
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Parcelable[]{captureIntent});
-
-
mainActivity.startActivityForResult(chooserIntent, ActivityResultConst.CAMERA_RESULTCODE);
-
private static void updatePhotos(MainActivity activity) {
-
// 该广播即使多发(即选取照片成功时也发送)也没有关系,只是唤醒系统刷新媒体文件
-
Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
-
intent.setData(activity.getImageUri());
-
activity.sendBroadcast(intent);
-
}
I/Choreographer: Skipped 179 frames! The application may be doing too much work on its main thread.
这种情况建议压缩图片,比如压缩成800*480,最多几百K,基本不会出问题。压缩代码见我上传的demo:
-
"null")(
-
-
private static void onActivityResultAboveL(MainActivity activity, int requestCode, int resultCode, Intent data) {
-
if (requestCode != ActivityResultConst.CAMERA_RESULTCODE
-
|| activity.getmUploadCallbackAboveL() == null) {
-
return;
-
}
-
Uri[] results = null;
-
if (resultCode == Activity.RESULT_OK) {
-
if (data == null) {
-
results = new Uri[]{activity.getImageUri()};
-
} 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) {
-
//压缩
-
results = PhotoUtil.doCompressImageForActivityResult(activity,results);
-
activity.getmUploadCallbackAboveL().onReceiveValue(results);
-
activity.setmUploadCallbackAboveL( null);
-
} else {
-
//压缩
-
results = new Uri[]{activity.getImageUri()};
-
results = PhotoUtil.doCompressImageForActivityResult(activity,results);
-
activity.getmUploadCallbackAboveL().onReceiveValue(results);
-
activity.setmUploadCallbackAboveL( null);
-
}
-
return;
-
}
results = PhotoUtil.doCompressImageForActivityResult(activity,results);
最后还有几个小问题,需要注意一下:
a. 二维码扫描之后,如果要返回,需要考虑是不是goback到父的父页面,不然扫描完一点击手机的返回键,直接就打开了照相机。
b. 我在AndroidManifest.xml文件给webview加了一个背景图片。
代码如 android:roundIcon="@mipmap/ic_launcher_round"
c. 我对MainActivity进行了一些简答的封装,不至于让全部的代码,都写在MainActivity.java中。
封装得很没有水平,用j2ee的思维在弄android,实在没办法。
-
ContentValues contentValues = new ContentValues( 1);
-
contentValues.put(MediaStore.Images.Media.DATA, file.getAbsolutePath());
-
Uri uri = mainActivity.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,contentValues);
-
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
-
mainActivity.startActivityForResult(intent, ActivityResultConst.CAMERA_RESULTCODE);
ActivityResultConst.CAMERA_RESULTCODE是我定义的常量类属性,不同的数字代表不同的请求码。
但是问题来了,扫描二维码,我用页面JavaScript调用BarcodeCallBack类实现的,好像没有进行Activity的Intent操作,无法设置或取得请求码,怎么办呢,我用了个非常挫的办法,在MainActivity里定义了一个Map,然后扫描二维码初始化时手动往这里面放一个常量,作为请求码,在onActivityResult方法里处理判断:
-
-
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
-
super.onActivityResult(requestCode, resultCode, data);
-
//自定义Activity返回结果码
-
Integer extraRequestCode = (Integer) extra.get(ActivityResultConst.REQUEST_CODE_KEY);
-
if(extraRequestCode == null || extraRequestCode == 0){
-
//如果没有自定义返回结果码,则使用onActivityResult的参数
-
extraRequestCode = requestCode;
-
}
-
//根据不同的返回码,执行不同的结果处理(一般指回调操作)
-
if (extraRequestCode == ActivityResultConst.BARCODE_RESULTCODE) {
-
HandleMainActivityResult.onBarcodeResult( this, requestCode, resultCode, data);
-
}
-
if (extraRequestCode == ActivityResultConst.CAMERA_RESULTCODE) {
-
HandleMainActivityResult.onCameraResult( this,requestCode,resultCode,data);
-
}
-
extra.clear();
-
}
e. 发现了一个比较好玩的事情:如果在AndroidManifest.xml里未注册相机权限,则似乎不需要动态检查相机权限,在安装时,权限列表里也未发现有相机权限,但是事实上,相机却可以正常调用。
如果在AndroidManifest.xml里注册相机权限,那么对不起,需要你在onCreate方法里动态检查相机权限,否则后台报错,相机无法调起。
说明相机这个权限很弱,默认可以让app使用,估计用一下,也不会怎么样,把系统搞崩溃吧。
f.如果你已经打开了照相机,这时,你按手机的回退键取消操作,再重新进来时,你会发现相机已经无法被调起。
原因是:因为对于页面表单<input type=file >来说,调用照相机相当于选择图片。而选择图片这个事件必须要有一个返回值,不然程序会一直处于等待状态,当你没有选定的时候你要传回一个null,否则程序就一直阻塞,就不能进行其它操作。
处理代码在Activity的方法onActivityResult(int requestCode, int resultCode, Intent data),对于我demo代码,也就是方法onCameraResult(MainActivity activity, int requestCode, int resultCode, Intent data)里加上取消操作的判断
-
public static void onCameraResult(MainActivity activity, int requestCode, int resultCode, Intent data) {
-
if(resultCode == Activity.RESULT_CANCELED) {
-
//打开相机,若取消,必须要设置一个null返回值,不然页面会一直处于等待状态,阻塞住无法响应
-
if(activity.getmUploadCallbackAboveL() != null){
-
activity.getmUploadCallbackAboveL().onReceiveValue( null);
-
}
-
if(activity.getmUploadMessage() != null){
-
activity.getmUploadMessage().onReceiveValue( null);
-
}
-
return;
-
}
-
updatePhotos(activity);
-
if ( null == activity.getmUploadMessage() && null == activity.getmUploadCallbackAboveL())
-
return;
-
Uri result = data == null || resultCode != Activity.RESULT_OK ? null : data.getData();
-
if (activity.getmUploadCallbackAboveL() != null) {
-
onActivityResultAboveL(activity, requestCode, resultCode, data);
-
} else if (activity.getmUploadMessage() != null) {
-
Log.e( "result", result + "");
-
if (result == null) {
-
//低版本,暂时不做压缩处理
-
activity.getmUploadMessage().onReceiveValue(activity.getImageUri());
-
activity.setmUploadMessage( null);
-
Log.e( "imageUri", activity.getImageUri() + "");
-
} else {
-
//压缩
-
result = PhotoUtil.doCompressImageForActivityResult(activity, data, null);
-
activity.getmUploadMessage().onReceiveValue(result);
-
activity.setmUploadMessage( null);
-
}
-
}
-
}
上面就是我完成的demo遇到的各种问题的汇总介绍。
如果有人觉得繁琐,那么我再上传一个最简答的demo版本,拍照预览功能的。(上传自己用jquery弄下,图片都被你file标签获取到了)。 点击下载 最简易版本的demo。