最近公司需要开发新的项目,为了节省时间成本,要求整体嵌入H5界面。对,没看错是整体嵌入,心中一万只草泥马奔腾,没办法大佬就是大佬,还是的照做。
今天来说说h5上传头像的问题吧。网上有很多的解决方案,开始我觉得好像很简单,都有成功案例,等把代码copy下来,发现并不能运行成功,真是一脸懵逼...N脸懵逼。
先说说为什么别人的代码都说测试通过了而在我这里都不行了呢?
去google了一把,因为在4.4之后的内核不再是基于webkit内核,禁用了file域来读取手机本地文件,卧槽不是说向下兼容的么,兼容尼玛啊。上张图,
这就是为什么拍照之后并不能正确读取到数据。问题找到之后,那么解决就很好解决了。拍完照片之后,根据路径把图片插入到系统图库里面,完美解决。
File cameraFile = new File(mCameraFilePath);
Bitmap bitmap1 = getimage(cameraFile.getPath());
result = Uri.parse(MediaStore.Images.Media.insertImage(
mContext.getContentResolver(), bitmap1, null, null));
Log.e(TAG,"5.0-result="+result);
uploadMessage.onReceiveValue(result);
下面在解决一下系统版本兼容的问题,需要重写WebChromeClient的以下方法,因为方法中有hide,你只需要重写就是。
// For Android < 3.0
public void openFileChooser(ValueCallback<Uri> valueCallback) {
uploadMessage = valueCallback;
openImageChooserActivity();
}
// For Android >= 3.0
public void openFileChooser(ValueCallback valueCallback, String acceptType) {
uploadMessage = valueCallback;
openImageChooserActivity();
}
//For Android >= 4.1
public void openFileChooser(ValueCallback<Uri> valueCallback, String acceptType, String capture) {
uploadMessage = valueCallback;
openImageChooserActivity();
}
// For Android >= 5.0
@Override
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) {
uploadMessageAboveL = filePathCallback;
openImageChooserActivity();
return true;
}
看见有的同学,说重写不起作用,我暂时是没遇到的,如果遇到了再解决吧。下面贴一个webchromeclient 的完整代码,你只需要拷过去,简单调用,在添加以下权限就可以了。
package com.choe.webviewdemo2;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ClipData;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebStorage;
import android.webkit.WebView;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
/**
* @author cyk
* @date 2016/12/14 14:02
* @email choe0227@163.com
* @desc
* @modifier
* @modify_time
* @modify_remark
*/
public class MyWebChromeClient extends WebChromeClient {
private Activity mContext;
private String mCameraFilePath;
private ValueCallback<Uri> uploadMessage;
private ValueCallback<Uri[]> uploadMessageAboveL;
private final static String TAG="MyWebChromeClient";
private final static int FILE_CHOOSER_RESULT_CODE = 10000;
public MyWebChromeClient(Activity context) {
mContext = context;
}
@Override
public void onProgressChanged(WebView view, int newProgress) {
super.onProgressChanged(view, newProgress);
}
// For Android < 3.0
public void openFileChooser(ValueCallback<Uri> valueCallback) {
uploadMessage = valueCallback;
openImageChooserActivity();
}
// For Android >= 3.0
public void openFileChooser(ValueCallback valueCallback, String acceptType) {
uploadMessage = valueCallback;
openImageChooserActivity();
}
//For Android >= 4.1
public void openFileChooser(ValueCallback<Uri> valueCallback, String acceptType, String capture) {
uploadMessage = valueCallback;
openImageChooserActivity();
}
// For Android >= 5.0
@Override
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) {
uploadMessageAboveL = filePathCallback;
openImageChooserActivity();
return true;
}
@Override
//扩容
public void onReachedMaxAppCacheSize(long requiredStorage, long quota, WebStorage.QuotaUpdater quotaUpdater) {
quotaUpdater.updateQuota(requiredStorage*2);
}
@Override
public void onConsoleMessage(String message, int lineNumber, String sourceID) {
Log.e("h5端的log", String.format("%s -- From line %s of %s", message, lineNumber, sourceID));
}
private void openImageChooserActivity() {
initDialog();
}
/**
* 上传头像时的弹出框
*/
private void initDialog(){
new AlertDialog.Builder(mContext)
.setTitle("更改头像")
.setItems(new String[]{"拍照", "图库选取"},
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,
int which) {
switch (which) {
case 0:
Intent i1=createCameraIntent();
mContext.startActivityForResult(Intent.createChooser(i1, "Image Chooser"), FILE_CHOOSER_RESULT_CODE);
break;
case 1:
Intent i=createFileItent();
mContext.startActivityForResult(Intent.createChooser(i, "Image Chooser"), FILE_CHOOSER_RESULT_CODE);
break;
}
}
}).setNegativeButton("取消", null).show();
}
/**
* 创建选择图库的intent
* @return
*/
private Intent createFileItent(){
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
Intent intent = new Intent(
Intent.ACTION_PICK,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
intent.setDataAndType(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
"image/*");
return intent;
}
/**
* 创建调用照相机的intent
* @return
*/
private Intent createCameraIntent() {
VersionUtils.checkAndRequestPermissionAbove23(mContext);
Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File externalDataDir = Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
System.out.println("externalDataDir:" + externalDataDir);
File cameraDataDir = new File(externalDataDir.getAbsolutePath()
+ File.separator + "browser-photo");
cameraDataDir.mkdirs();
mCameraFilePath = cameraDataDir.getAbsolutePath() + File.separator
+ System.currentTimeMillis() + ".jpg";
System.out.println("mcamerafilepath:" + mCameraFilePath);
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT,
Uri.fromFile(new File(mCameraFilePath)));
return cameraIntent;
}
/**
* 处理拍照返回函数
* @param requestCode
* @param resultCode
* @param data
*/
public void onActivityResult(int requestCode, int resultCode, Intent data){
if (requestCode == FILE_CHOOSER_RESULT_CODE) {
if (null == uploadMessage&& null == uploadMessageAboveL)
return;
Uri result = data == null || resultCode != Activity.RESULT_OK ? null
: data.getData();
if (uploadMessageAboveL != null) {//5.0以上
onActivityResultAboveL(requestCode, resultCode, data);
}else if(uploadMessage != null) {
if (result == null && data == null
&& resultCode == Activity.RESULT_OK) {
File cameraFile = new File(mCameraFilePath);
Bitmap bitmap1 = getimage(cameraFile.getPath());
result = Uri.parse(MediaStore.Images.Media.insertImage(
mContext.getContentResolver(), bitmap1, null, null));
}
Log.e(TAG,"5.0-result="+result);
uploadMessage.onReceiveValue(result);
uploadMessage = null;
}
}
}
/**
* 处理拍照返回函数 5。0以上
* @param requestCode
* @param resultCode
* @param intent
*/
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void onActivityResultAboveL(int requestCode, int resultCode, Intent intent) {
Log.e(TAG,"5.0+ 返回了");
if (requestCode != FILE_CHOOSER_RESULT_CODE || uploadMessageAboveL == null)
return;
Uri[] results = null;
if (resultCode == Activity.RESULT_OK) {
if (intent != null) {
String dataString = intent.getDataString();
ClipData clipData = intent.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)};
}else {
File cameraFile = new File(mCameraFilePath);
Bitmap bitmap1 = getimage(cameraFile.getPath());
Uri result = Uri.parse(MediaStore.Images.Media.insertImage(
mContext.getContentResolver(), bitmap1, null, null));
results=new Uri[]{result};
}
}
uploadMessageAboveL.onReceiveValue(results);
uploadMessageAboveL = null;
}
/**
* 根据图片路径获取图p片
* @param srcPath
* @return
*/
private Bitmap getimage(String srcPath) {
BitmapFactory.Options newOpts = new BitmapFactory.Options();
// 开始读入图片,此时把options.inJustDecodeBounds 设回true了
newOpts.inJustDecodeBounds = true;
Bitmap bitmap = BitmapFactory.decodeFile(srcPath, newOpts);// 此时返回bm为空
newOpts.inJustDecodeBounds = false;
int w = newOpts.outWidth;
int h = newOpts.outHeight;
// 现在主流手机比较多是800*480分辨率,所以高和宽我们设置为
float hh = 800f;// 这里设置高度为800f
float ww = 480f;// 这里设置宽度为480f
// 缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可
int be = 1;// be=1表示不缩放
if (w > h && w > ww) {// 如果宽度大的话根据宽度固定大小缩放
be = (int) (newOpts.outWidth / ww);
} else if (w < h && h > hh) {// 如果高度高的话根据宽度固定大小缩放
be = (int) (newOpts.outHeight / hh);
}
if (be <= 0)
be = 1;
newOpts.inSampleSize = be;// 设置缩放比例
// 重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了
bitmap = BitmapFactory.decodeFile(srcPath, newOpts);
return compressImage(bitmap);// 压缩好比例大小后再进行质量压缩
}
/**
* 裁剪图片大小
* @param image
* @return
*/
private Bitmap compressImage(Bitmap image) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.JPEG, 100, baos);// 质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
int options = 100;
while (baos.toByteArray().length / 1024 > 100) { // 循环判断如果压缩后图片是否大于100kb,大于继续压缩
baos.reset();// 重置baos即清空baos
image.compress(Bitmap.CompressFormat.JPEG, options, baos);// 这里压缩options%,把压缩后的数据存放到baos中
options -= 10;// 每次都减少10
}
ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());// 把压缩后的数据baos存放到ByteArrayInputStream中
Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);// 把ByteArrayInputStream数据生成图片
return bitmap;
}
}
在webview的界面,需要重写onActivityResult方法,并调用处理选择图片返回的方法。
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Log.e(TAG,"收到返回消息了");
mMyWebChromeClient.onActivityResult(requestCode,resultCode,data);
}
添加上这些东西,上传头像基本搞定。
忘了说了,适配6.0的时候,还需要进行一下权限申请,拍照属于危险权限。
/**
* 6.0 手机系统以上 检查并请求权限
* @param context 必须为 Activity
*/
public static void checkAndRequestPermissionAbove23(Activity context){
int i = ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA);
if (i!= PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(context,
new String[]{Manifest.permission.CAMERA},Constant.TAKE_PHOTO_PERMISSION
);
}
}
到这里,基本就适配了所有版本,亲测可用。