WebView完美解决上传图库图片
版权声明:本文为博主原创文章,码字不易,转载请注明原文地址,谢谢。
我们在使用WebView这个控件的时候,经常需要去写一些方法供H5页面调用,我在开发的过程中遇到这样一个需求,通过点击H5页面上的按钮调用Android手机的图库,并且进行上传图片。
通过网上查找资料,找到了WebChromeClient的一个隐藏方法openFileChooser();
- Android3.0以下的SDK,openFileChooser仅有一个参数:openFileChooser(ValueCallback< Uri > uploadFile)。
- Android3.0以上的SDK,openFileChooser有两个参数:openFileChooser(ValueCallback< Uri > uploadFile, String acceptType)。
- Android4.1以上的SDK,openFileChooser增加到三个参数:openFileChooser(ValueCallback< Uri > uploadFile, String acceptType, String capture)。
- Android5.0以上的SDK,openFileChooser被onShowFileChooser给替代了,onShowFileChooser(WebView webview,ValueCallback< Uri > filePathCallback,FileChooserParams fileChooserParams)。
Android6.0的SDK,WebChromeClient.class里的openFileChooser和onShowFileChooser的源码:
方法openFileChooser:
/**
* Tell the client to open a file chooser.
* @param uploadFile A ValueCallback to set the URI of the file to upload.
* onReceiveValue must be called to wake up the thread.a
* @param acceptType The value of the 'accept' attribute of the input tag
* associated with this file picker.
* @param capture The value of the 'capture' attribute of the input tag
* associated with this file picker.
*
* @deprecated Use {@link #showFileChooser} instead.
* @hide This method was not published in any SDK version.
*/
@SystemApi
@Deprecated
public void openFileChooser(ValueCallback<Uri> uploadFile, String acceptType, String capture) {
uploadFile.onReceiveValue(null);
}
方法onShowFileChooser:
/**
* 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;
}
来看下代码:
这是一个内部类,重写WebChromeClient的openFileChooser和onShowFileChooser。
/**
* 内部类
*/
class MyWebChromeClient extends WebChromeClient {
// For Android 3.0+
public void openFileChooser(ValueCallback<Uri> uploadMsg,
String acceptType) {
if (mUploadMessage != null)
return;
mUploadMessage = uploadMsg;
selectImage(RESULT_CODE_PICK_FROM_ALBUM_BELLOW_LOLLILOP);
}
// For Android < 3.0
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
openFileChooser(uploadMsg, "");
}
// For Android > 4.1.1
public void openFileChooser(ValueCallback<Uri> uploadMsg,
String acceptType, String capture) {
openFileChooser(uploadMsg, acceptType);
}
// For Android 5.0+
public boolean onShowFileChooser(WebView webView,
ValueCallback<Uri[]> filePathCallback,
FileChooserParams fileChooserParams) {
mUploadCallbackAboveL = filePathCallback;
selectImage(RESULT_CODE_PICK_FROM_ALBUM_ABOVE_LOLLILOP);
return true;
}
@Override
public void onProgressChanged(WebView view, int newProgress) {
super.onProgressChanged(view, newProgress);
}
}
openFileChooser和onShowFileChooser方法,当Web页面上点击了<input type="file"/>
的标签时,不同版本的SDK调用openFileChooser或者onShowFileChooser。
选择完图片后,进入onActivityResult,根据不同的requestCode值区分开AndroidSDK 5.0以上和5.0以下,5.0以下传回Uri对象,5.0以上传回Uri数组。其中处理下onActivityResult传回来的Intent data,将Intent data转换成Uri。
/**选择后,回传值*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (mUploadMessage == null && mUploadCallbackAboveL == null) {
return;
}
Uri uri = null;
switch (requestCode) {
case RESULT_CODE_PICK_FROM_ALBUM_BELLOW_LOLLILOP:
uri = afterChosePic(data);
if (mUploadMessage != null) {
mUploadMessage.onReceiveValue(uri);
mUploadMessage = null;
}
break;
case RESULT_CODE_PICK_FROM_ALBUM_ABOVE_LOLLILOP:
try {
uri = afterChosePic(data);
if (uri == null) {
mUploadCallbackAboveL.onReceiveValue(new Uri[] { });
mUploadCallbackAboveL = null;
break;
}
if (mUploadCallbackAboveL != null && uri != null) {
mUploadCallbackAboveL.onReceiveValue(new Uri[] { uri });
mUploadCallbackAboveL = null;
}
} catch (Exception e) {
mUploadCallbackAboveL = null;
e.printStackTrace();
}
break;
}
}
**
下面把完整的代码贴上来:
**
WebViewActivity.java
package com.lwd.webviewupimg;
import java.io.File;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore.Images.ImageColumns;
import android.view.KeyEvent;
import android.view.View;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebSettings.LayoutAlgorithm;
import android.webkit.WebView;
import android.webkit.WebViewClient;
/**
* @author lwd
* @create 2016年8月17日
*/
@SuppressLint("SetJavaScriptEnabled")
public class WebViewActivity extends Activity{
private WebView webview;
private ValueCallback<Uri> mUploadMessage;
private ValueCallback<Uri[]> mUploadCallbackAboveL;
private final int RESULT_CODE_PICK_FROM_ALBUM_BELLOW_LOLLILOP = 1;
private final int RESULT_CODE_PICK_FROM_ALBUM_ABOVE_LOLLILOP = 2;
private String url = "";//这里添加含有图片上传功能的H5页面访问地址即可。
String compressPath = "";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_welcome);
initData();
}
public void initData() {
webview = (WebView) findViewById(R.id.webview);
initWebView();
webview.loadUrl(url);
}
@SuppressWarnings("deprecation")
private void initWebView(){
webview.setScrollBarStyle(View.GONE);
webview.getSettings().setJavaScriptEnabled(true);
webview.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
webview.getSettings().setDomStorageEnabled(false);
webview.getSettings().setCacheMode(WebSettings.LOAD_DEFAULT);
webview.getSettings().setPluginState(WebSettings.PluginState.ON);
webview.getSettings().setAllowFileAccess(true);
webview.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
webview.getSettings().setLayoutAlgorithm(LayoutAlgorithm.NORMAL);
webview.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
webview.getSettings().setUseWideViewPort(true);
webview.getSettings().setLoadWithOverviewMode(true);
webview.getSettings().setAllowContentAccess(true);
webview.requestFocus();
webview.setWebViewClient(new WebClient());
webview.setWebChromeClient(new MyWebChromeClient());
}
/**选择后,回传值*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (mUploadMessage == null && mUploadCallbackAboveL == null) {
return;
}
Uri uri = null;
switch (requestCode) {
case RESULT_CODE_PICK_FROM_ALBUM_BELLOW_LOLLILOP:
uri = afterChosePic(data);
if (mUploadMessage != null) {
mUploadMessage.onReceiveValue(uri);
mUploadMessage = null;
}
break;
case RESULT_CODE_PICK_FROM_ALBUM_ABOVE_LOLLILOP:
try {
uri = afterChosePic(data);
if (uri == null) {
mUploadCallbackAboveL.onReceiveValue(new Uri[] { });
mUploadCallbackAboveL = null;
break;
}
if (mUploadCallbackAboveL != null && uri != null) {
mUploadCallbackAboveL.onReceiveValue(new Uri[] { uri });
mUploadCallbackAboveL = null;
}
} catch (Exception e) {
mUploadCallbackAboveL = null;
e.printStackTrace();
}
break;
}
}
/**
* 选择照片后结束
* @param data
*/
private Uri afterChosePic(Intent data) {
if (data == null) {
return null;
}
String path = getRealFilePath(data.getData());
if (path == null) {//取消选择图片的时候,返回回来的Intent不为空,但getData为空,这里加个判空
return null;
}
String[] names = path.split("\\.");
String endName = null;
if (names != null) {
endName = names[names.length - 1];
}
if (endName != null) {
compressPath = compressPath.split("\\.")[0] + "." + endName;
}
File newFile;
try {
newFile = FileUtils.compressFile(path, compressPath);
} catch (Exception e) {
newFile = null;
}
return Uri.fromFile(newFile);
}
/**
* 根据Uri获取图片文件的绝对路径
*/
public String getRealFilePath(final Uri uri) {
if (null == uri) {
return null;
}
final String scheme = uri.getScheme();
String data = null;
if (scheme == null) {
data = uri.getPath();
} else if (ContentResolver.SCHEME_FILE.equals(scheme)) {
data = uri.getPath();
} else if (ContentResolver.SCHEME_CONTENT.equals(scheme)) {
Cursor cursor = getContentResolver().query(uri,
new String[] { ImageColumns.DATA }, null, null, null);
if (null != cursor) {
if (cursor.moveToFirst()) {
int index = cursor.getColumnIndexOrThrow(ImageColumns.DATA);
if (index > -1) {
data = cursor.getString(index);
}
}
cursor.close();
}
}
return data;
}
@Override
public void onBackPressed() {
super.onBackPressed();
if(webview.canGoBack()){
webview.goBack();
}else{
finish();
}
}
private class WebClient extends WebViewClient {
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK) && webview.canGoBack()) {
webview.goBack();
return true;
}
return super.onKeyDown(keyCode, event);
}
/**打开图库,同时处理图片(项目业务需要统一命名)*/
private void selectImage(int resultCode) {
compressPath = Environment.getExternalStorageDirectory().getPath() + "/QWB/temp";
File file = new File(compressPath);
if (!file.exists()) {
file.mkdirs();
}
compressPath = compressPath + File.separator + "compress.png";
File image = new File(compressPath);
if (image.exists()) {
image.delete();
}
Intent intent = new Intent(
Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(intent, resultCode);
}
/**
* 内部类
*/
class MyWebChromeClient extends WebChromeClient {
//openFileChooser(隐藏方法)仅适用android5.0以下的环境,android5.0及以上使用onShowFileChooser
// For Android 3.0+
public void openFileChooser(ValueCallback<Uri> uploadMsg,
String acceptType) {
if (mUploadMessage != null)
return;
mUploadMessage = uploadMsg;
selectImage(RESULT_CODE_PICK_FROM_ALBUM_BELLOW_LOLLILOP);
}
// For Android < 3.0
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
openFileChooser(uploadMsg, "");
}
// For Android > 4.1.1
public void openFileChooser(ValueCallback<Uri> uploadMsg,
String acceptType, String capture) {
openFileChooser(uploadMsg, acceptType);
}
// For Android 5.0+
public boolean onShowFileChooser(WebView webView,
ValueCallback<Uri[]> filePathCallback,
FileChooserParams fileChooserParams) {
mUploadCallbackAboveL = filePathCallback;
selectImage(RESULT_CODE_PICK_FROM_ALBUM_ABOVE_LOLLILOP);
return true;
}
@Override
public void onProgressChanged(WebView view, int newProgress) {
super.onProgressChanged(view, newProgress);
}
}
}
FileUtils.java
package com.lwd.webviewupimg;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.media.ExifInterface;
import android.text.TextUtils;
import android.util.Log;
public class FileUtils {
/**
* 把图片压缩到200K
*
* @param oldpath
* 压缩前的图片路径
* @param newPath
* 压缩后的图片路径
* @return
*/
public static File compressFile(String oldpath, String newPath) {
Bitmap compressBitmap = FileUtils.decodeFile(oldpath);
Bitmap newBitmap = ratingImage(oldpath, compressBitmap);
ByteArrayOutputStream os = new ByteArrayOutputStream();
newBitmap.compress(CompressFormat.PNG, 100, os);
byte[] bytes = os.toByteArray();
File file = null ;
try {
file = FileUtils.getFileFromBytes(bytes, newPath);
} catch (Exception e) {
e.printStackTrace();
}finally{
if(newBitmap != null ){
if(!newBitmap.isRecycled()){
newBitmap.recycle();
}
newBitmap = null;
}
if(compressBitmap != null ){
if(!compressBitmap.isRecycled()){
compressBitmap.recycle();
}
compressBitmap = null;
}
}
return file;
}
private static Bitmap ratingImage(String filePath,Bitmap bitmap){
int degree = readPictureDegree(filePath);
return rotaingImageView(degree, bitmap);
}
/**
* 旋转图片
* @param angle
* @param bitmap
* @return Bitmap
*/
public static Bitmap rotaingImageView(int angle , Bitmap bitmap) {
//旋转图片 动作
Matrix matrix = new Matrix();;
matrix.postRotate(angle);
System.out.println("angle2=" + angle);
// 创建新的图片
Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0,
bitmap.getWidth(), bitmap.getHeight(), matrix, true);
return resizedBitmap;
}
/**
* 读取图片属性:旋转的角度
* @param path 图片绝对路径
* @return degree旋转的角度
*/
public static int readPictureDegree(String path) {
int degree = 0;
try {
ExifInterface exifInterface = new ExifInterface(path);
int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
degree = 90;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
degree = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
degree = 270;
break;
}
} catch (IOException e) {
e.printStackTrace();
}
return degree;
}
/**
* 把字节数组保存为一个文件
*
* @param b
* @param outputFile
* @return
*/
public static File getFileFromBytes(byte[] b, String outputFile) {
File ret = null;
BufferedOutputStream stream = null;
try {
ret = new File(outputFile);
FileOutputStream fstream = new FileOutputStream(ret);
stream = new BufferedOutputStream(fstream);
stream.write(b);
} catch (Exception e) {
// log.error("helper:get file from byte process error!");
e.printStackTrace();
} finally {
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
// log.error("helper:get file from byte process error!");
e.printStackTrace();
}
}
}
return ret;
}
/**
* 图片压缩
*
* @param fPath
* @return
*/
public static Bitmap decodeFile(String fPath) {
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inJustDecodeBounds = true;
opts.inDither = false; // Disable Dithering mode
opts.inPurgeable = true; // Tell to gc that whether it needs free
opts.inInputShareable = true; // Which kind of reference will be used to
BitmapFactory.decodeFile(fPath, opts);
final int REQUIRED_SIZE = 200;
int scale = 1;
if (opts.outHeight > REQUIRED_SIZE || opts.outWidth > REQUIRED_SIZE) {
final int heightRatio = Math.round((float) opts.outHeight
/ (float) REQUIRED_SIZE);
final int widthRatio = Math.round((float) opts.outWidth
/ (float) REQUIRED_SIZE);
scale = heightRatio < widthRatio ? heightRatio : widthRatio;//
}
Log.i("scale", "scal ="+ scale);
opts.inJustDecodeBounds = false;
opts.inSampleSize = scale;
Bitmap bm = BitmapFactory.decodeFile(fPath, opts).copy(Config.ARGB_8888, false);
return bm;
}
/**
* 创建目录
* @param path
*/
public static void setMkdir(String path)
{
File file = new File(path);
if(!file.exists())
{
file.mkdirs();
Log.e("file", "目录不存在 创建目录 ");
}else{
Log.e("file", "目录存在");
}
}
/**
* 获取目录名称
* @param url
* @return FileName
*/
public static String getFileName(String url)
{
int lastIndexStart = url.lastIndexOf("/");
if(lastIndexStart!=-1)
{
return url.substring(lastIndexStart+1, url.length());
}else{
return null;
}
}
/**
* 删除该目录下的文件
*
* @param path
*/
public static void delFile(String path) {
if (!TextUtils.isEmpty(path)) {
File file = new File(path);
if (file.exists()) {
file.delete();
}
}
}
}