WebView完美解决上传图库图片

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();
			}
		}
	}
}

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值