Android WebView嵌入H5之file頭像上傳檔案,適配所有版本
阿新 • • 發佈:2018-12-29
最近公司需要開發新的專案,為了節省時間成本,要求整體嵌入H5介面。對,沒看錯是整體嵌入,心中一萬隻草泥馬奔騰,沒辦法大佬就是大佬,還是的照做。
今天來說說h5上傳頭像的問題吧。網上有很多的解決方案,開始我覺得好像很簡單,都有成功案例,等把程式碼copy下來,發現並不能執行成功,真是一臉懵逼...N臉懵逼。
先說說為什麼別人的程式碼都說測試通過了而在我這裡都不行了呢?
去google了一把,因為在4.4之後的核心不再是基於webkit核心,禁用了file域來讀取手機本地檔案,臥槽不是說向下相容的麼,相容尼瑪啊。上張圖,
這就是為什麼拍照之後並不能正確讀取到資料。問題找到之後,那麼解決就很好解決了。拍完照片之後,根據路徑把圖片插入到系統圖庫裡面,完美解決。
下面在解決一下系統版本相容的問題,需要重寫WebChromeClient的以下方法,因為方法中有hide,你只需要重寫就是。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 的完整程式碼,你只需要拷過去,簡單呼叫,在新增以下許可權就可以了。// 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; }
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 [email protected]
* @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
);
}
}
到這裡,基本就適配了所有版本,親測可用。