最近钱旺宣布成立QBike,准备进军单车俱乐部,将挑战膜拜,OFO单车! 虽然新产业一直产生,可能走滴滴模式。开发行情还是不太乐观,只能敲敲敲!欢迎一叶飘舟加入本公众号阵营!正参加2016博客之星,点击原文支持一票!
阅读文章需要几分钟,不妨早上听听歌 开启新的一天!Go!
最近在项目开发中,需要使用WebView上传文件。默认情况下情况下,使用Android的WebView是不能够支持上传文件的。经过查找资料,得知需要重新WebChromeClient,根据选择到的文件Uri,传给页面去上传就可以了。
自定义WebChromeClient
先在WebViewActivity里面自定义MyWebChromeClient,代码如下:
代码语言:javascript复制public class MyWebChromeClient extends WebChromeClient {
// For Android 3.0
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
CLog.i("UPFILE", "in openFile Uri Callback"); if (mUploadMessage != null) {
mUploadMessage.onReceiveValue(null);
}
mUploadMessage = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("*/*");
startActivityForResult(
Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE);
}
// For Android 3.0
public void openFileChooser(ValueCallback uploadMsg,
String acceptType) {
CLog.i("UPFILE", "in openFile Uri Callback has accept Type" acceptType);
if (mUploadMessage != null) {
mUploadMessage.onReceiveValue(null);
}
mUploadMessage = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
String type = TextUtils.isEmpty(acceptType) ? "*/*" : acceptType;
i.setType(type);
startActivityForResult(Intent.createChooser(i, "File Chooser"),
FILECHOOSER_RESULTCODE);
} // For Android 4.1
public void openFileChooser(ValueCallback<Uri> uploadMsg,
String acceptType, String capture) {
CLog.i("UPFILE", "in openFile Uri Callback has accept Type" acceptType "has capture" capture);
if (mUploadMessage != null) {
mUploadMessage.onReceiveValue(null);
}
mUploadMessage = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
String type = TextUtils.isEmpty(acceptType) ? "*/*" : acceptType;
i.setType(type);
startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE);
}//Android 5.0
@Override
@SuppressLint("NewApi")
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
if (mUploadMessage != null) {
mUploadMessage.onReceiveValue(null);
}
CLog.i("UPFILE", "file chooser params:" fileChooserParams.toString());
mUploadMessage = filePathCallback;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
if (fileChooserParams != null && fileChooserParams.getAcceptTypes() != null
&& fileChooserParams.getAcceptTypes().length > 0) {
i.setType(fileChooserParams.getAcceptTypes()[0]);
} else {
i.setType("*/*");
}
startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE); return true;
}
}
上面openFileChooser是系统未暴露的接口,因此不需要加Override的注解,同时不同版本有不同的参数,其中的参数,第一个ValueCallback用于我们在选择完文件后,接收文件回调到网页内处理,acceptType为接受的文件mime type。在Android 5.0之后,系统提供了onShowFileChooser来让我们实现选择文件的方法,仍然有ValueCallback,在FileChooserParams参数中,同样包括acceptType。我们可以根据acceptType,来打开系统的或者我们自己创建文件选择器。当然如果需要打开相机拍照,也可以自己去使用打开相机拍照的Intent去打开即可。
处理选择的文件
因为我们前面是使用startActivityForResult来打开的选择页面,我们会在onActivityResult中接收到选择的结果。代码如下:
代码语言:javascript复制@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == FILECHOOSER_RESULTCODE) {
if (null == mUploadMessage)
return;
Uri result = data == null || resultCode != RESULT_OK ? null : data.getData(); if (result == null) {
mUploadMessage.onReceiveValue(null);
mUploadMessage = null;
return;
}
String path = FileUtils.getPath(this, result);
if (TextUtils.isEmpty(path)) {
mUploadMessage.onReceiveValue(null);
mUploadMessage = null;
return;
}
Uri uri = Uri.fromFile(new File(path));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
mUploadMessage.onReceiveValue(new Uri[]{uri});
} else {
mUploadMessage.onReceiveValue(uri);
}
mUploadMessage = null;
}
}
注意事项:
1 由于不同版本的差别,Android 5.0以下的版本,ValueCallback 的onReceiveValue接收的参数类型是Uri, 5.0及以上版本接收的是Uri数组,在传值的时候需要注意。 2 选择文件会使用系统提供的组件或者其他支持的app,返回的uri有的直接是文件的url,有的是contentprovider的uri,因此我们需要统一处理一下,转成文件的uri,可参考以下代码(获取文件的路径)。 3 <font color="red">即使获取的结果为null,也要传给webview,即直接调用mUploadMessage.onReceiveValue(null),否则网页会阻塞。</font> 4 在打release包的时候,因为我们会混淆,要特别设置不要混淆WebChromeClient子类里面的openFileChooser方法,由于不是继承的方法,所以默认会被混淆,然后就无法选择文件了。
FileUtils工具类如下:
代码语言:javascript复制public class FileUtils {
public static boolean isExternalStorageDocument(Uri uri) { return "com.android.externalstorage.documents".equals(uri.getAuthority());
}
public static boolean isDownloadsDocument(Uri uri) {
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}
public static boolean isMediaDocument(Uri uri) {
return "com.android.providers.media.documents".equals(uri.getAuthority());
}
public static String getDataColumn(Context context, Uri uri, String selection,
String[] selectionArgs) {
Cursor cursor = null;
final String column = "_data";
final String[] projection = {
column
};
try {
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
if (cursor != null && cursor.moveToFirst()) { final int column_index = cursor.getColumnIndexOrThrow(column); return cursor.getString(column_index);
}
} finally { if (cursor != null)
cursor.close();
} return null;
}
@SuppressLint("NewApi")
public static String getPath(final Context context, final Uri uri) {
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
// DocumentProvider
if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
// ExternalStorageProvider
if (isExternalStorageDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri); final String[] split = docId.split(":");
final String type = split[0];
if ("primary".equalsIgnoreCase(type)) {
return Environment.getExternalStorageDirectory() "/" split[1];
}
// TODO handle non-primary volumes
}
// DownloadsProvider
else if (isDownloadsDocument(uri)) {
final String id = DocumentsContract.getDocumentId(uri);
final Uri contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
return getDataColumn(context, contentUri, null, null);
} // MediaProvider
else if (isMediaDocument(uri)) { final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":"); final String type = split[0];
Uri contentUri = null; if ("image".equals(type)) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}
final String selection = "_id=?";
final String[] selectionArgs = new String[] {
split[1]
};
return getDataColumn(context, contentUri, selection, selectionArgs);
}
} // MediaStore (and general)
else if ("content".equalsIgnoreCase(uri.getScheme())) {
return getDataColumn(context, uri, null, null);
}
// File
else if ("file".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
}
return null;
}
}
看了上面的代码,你是不是感觉有点复杂呢?下面我们将介绍怎么通过使用腾讯X5 Webview浏览器实现拍照或从相册上传图片功能。
使用腾讯X5 Webview浏览器
TBS腾讯浏览器服务官网:http://x5.tencent.comjar包下载:http://x5.tencent.com/doc?id=1004
集成教程:
http://www.jianshu.com/p/8a7224ff371a http://blog.csdn.net/qq_17387361/article/details/52396338 http://www.jianshu.com/p/e4009688119b
环境调好后,我们就可以愉快的开始调试了。
代码语言:javascript复制public class MyWebChromeClient extends WebChromeClient { @Override
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> valueCallback, FileChooserParams fileChooserParams) {
TLog.error("onShowFileChooser"); return super.onShowFileChooser(webView, valueCallback, fileChooserParams);
} // Android > 4.1.1 调用这个方法
public void openFileChooser(ValueCallback<Uri> uploadMsg,
String acceptType, String capture) {
mUploadMessage = uploadMsg;
choosePicture(); } // 3.0 调用这个方法
public void openFileChooser(ValueCallback<Uri> uploadMsg,
String acceptType) {
mUploadMessage = uploadMsg;
choosePicture(); } // Android < 3.0 调用这个方法
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
mUploadMessage = uploadMsg;
choosePicture();
}
}
这里选择图片使用了三方图片选择组件:PhotoPicker,项目地址:https://github.com/donglua/PhotoPicker
其中choosePicture方法如下
代码语言:javascript复制private void choosePicture() {
PhotoPicker.builder()
.setPhotoCount(1)
.setShowCamera(true)
.setShowGif(true)
.setPreviewEnabled(false)
.start(TBSWebActivity.this, PhotoPicker.REQUEST_CODE);
}
在onActivityResult中接收到选择的结果,处理如下:
代码语言:javascript复制@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) { if (null == mUploadMessage) { return;
}
if (resultCode == RESULT_OK && requestCode == PhotoPicker.REQUEST_CODE) {
ArrayList<String> photos =
intent.getStringArrayListExtra(PhotoPicker.KEY_SELECTED_PHOTOS); Uri result = Uri.parse(photos.get(0));
mUploadMessage.onReceiveValue(result);
mUploadMessage = null;
} else {
mUploadMessage.onReceiveValue(null);
}
}
这里值得强调的是,即使获取的结果为null(比如按back键取消了),也要传给webview,即直接调用mUploadMessage.onReceiveValue(null),否则网页会阻塞。
H5前端
最后简单再说一下H5前端调用。
代码语言:javascript复制<div class="btn1">上传照片
<input type="file" accept="image/*" id="uploadImage" capture="camera" onchange="selectFileImage(this);">
</div>
上传相关的js代码由于微信字数限制请点击原文查看。
http://blog.csdn.net/jdsjlzx/article/details/53546260
---我是分割线---
Tamic开发社区
非专业的移动社区
不只是干货,还有人生
长按二维码关注我们