WebView上傳檔案遇到的坑openFileChooser
最近公司專案碰到WebView上傳圖片的問題,在這方面糾結了挺長時間,把解決問題的思路記錄一下。
至於 WebView 需要的配置方面這裡就不說了,自行百度。
首先 WebView 要重寫 WebViewClient 的 shouldOverrideUrlLoading 方法,讓連結直接
在APP內部開啟而不是跳轉到系統瀏覽器或者是第三方瀏覽器:
webview.setWebViewClient(new WebViewClient(){
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return super.shouldOverrideUrlLoading(view, url);
}
});
上面這個還不是遇到的問題,真正的問題就在於在 WebView 的連結裡通過按鈕開啟手機相簿,
但是安卓這麼多版本又不會自動相容,這就需要我們自己去做相容。上網瞭解到在安卓5.0版本
以後SDK中開放的方法是 onShowFileChooser ,而在較低版本中是 openFileChooser 。然後在
API21(由於專案引用23的SDK,此方法被隱藏,不太清楚是不是21)之後 openFileCHooser 就
被 Google 隱藏掉了。
/**
* 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);
}
從上面程式碼可以看出此方法被隱藏了,所以正常情況下這個方法是無法呼叫的。
但是如果非要用被隱藏的方法,那被隱藏的方法怎麼才能引用呢?
第一種方法 可以利用反射機制。《這種方法自行百度搜索》
下面說說我在專案中用到的方法:
因為 openFileChooser 方法被隱藏 ,所以在 WebChromeClient 中重寫的時候就沒有此方法。
於是我便自己寫一個類去繼承 WebChromeClient 。在類中寫相容各個版本的 openFileChooser
方法。程式碼如下:
private ValueCallback<Uri> mUploadMessage;
private ValueCallback<Uri[]> uploadMessage; // 用於5.0以上
private static final int FILECHOOSER_RESULTCODE = 1;
public static final int REQUEST_SELECT_FILE = 100;
public class MyWebChromeClient extends WebChromeClient {
/**
* {@inheritDoc}
* 安卓3.0-
*/
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
openFileChooser(uploadMsg, "");
}
/**
* {@inheritDoc}
* 安卓3.0 - 4.0
*/
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
mUploadMessage = uploadMsg;
Intent i = null;
if (Build.VERSION.SDK_INT < 19) {
i = new Intent(Intent.ACTION_GET_CONTENT);
i.setType("image/*");
} else {
i = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
}
startActivityForResult(Intent.createChooser(i, "選擇圖片"), FILECHOOSER_RESULTCODE);
}
/**
* {@inheritDoc}
* 安卓 4.0 - 5.0
*/
protected void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
openFileChooser(uploadMsg, acceptType);
}
/**
* 安卓5.0+
*/
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public boolean onShowFileChooser(WebView mWebView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) {
if (uploadMessage != null) {
uploadMessage.onReceiveValue(null);
uploadMessage = null;
}
uploadMessage = filePathCallback;
Intent intent = fileChooserParams.createIntent();
try {
startActivityForResult(Intent.createChooser(intent, "選擇圖片"), REQUEST_SELECT_FILE);
} catch (ActivityNotFoundException e) {
uploadMessage = null;
Toast.makeText(getApplicationContext(), "檔案上傳失敗", Toast.LENGTH_LONG).show();
return false;
}
return true;
}
}
然後需要重寫 onActivityResult 方法接收返回的資料即可:
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (requestCode == REQUEST_SELECT_FILE) {
if (uploadMessage == null)
return;
Uri result = (data == null || resultCode != RESULT_OK) ? null : data.getData();
if (result != null) {
uploadMessage.onReceiveValue(new Uri[]{result});
} else {
uploadMessage.onReceiveValue(new Uri[]{});
}
uploadMessage = null; //每次置空是為了防止下次點選按鈕選擇圖片無響應
}
} else if (requestCode == FILECHOOSER_RESULTCODE) {
if (null == mUploadMessage)
return;
Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();
mUploadMessage.onReceiveValue(result); //注意此處
mUploadMessage = null;
} else
Toast.makeText(getApplicationContext(), "上傳檔案失敗", Toast.LENGTH_LONG).show();
return;
}
到這裡工作就完成了90%,什麼?到這裡還沒完成?
是的,在部分機型上上傳圖片之後,但是在WebView中顯示圖片縮圖確實TXT文件型別。
這是為什麼?我個人理解認為是5.0以下與5.0以上圖片返回的Uri格式不一致造成的。
一時間就把我難住了,嘗試了各種方法,包括安卓不同版本系統開啟相簿的方式都無效。
突然大神附體似的就想到了會不會和返回 Uri 的格式不同有關?選擇的圖片肯定都是有
自己的路徑的,我可不可以獲取到圖片的路徑呢?
同時又發現有一個 Uri.fromFile() 的方法,而又可以將圖片的路徑轉換成File型別。於是
拿起就是幹馬上嘗試。
於是有了以下程式碼:
// 根據圖片的Uri得到圖片的路徑
private String getImagePath(Uri originalUri) {
String[] pro = {MediaStore.Images.Media.DATA};
Cursor cursor = getContentResolver().query(originalUri, pro, null, null, null);
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
String path = cursor.getString(column_index);
return path;
}
然後在onActivityResult中呼叫,完整程式碼:
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (requestCode == REQUEST_SELECT_FILE) {
if (uploadMessage == null)
return;
Uri result = (data == null || resultCode != RESULT_OK) ? null : data.getData();
if (result != null) {
uploadMessage.onReceiveValue(new Uri[]{result});
} else {
uploadMessage.onReceiveValue(new Uri[]{});
}
uploadMessage = null;
}
} else if (requestCode == FILECHOOSER_RESULTCODE) {
if (null == mUploadMessage)
return;
Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();
Uri uri = Uri.fromFile(new File(getImagePath(result)));
mUploadMessage.onReceiveValue(uri); // 轉換後
mUploadMessage = null;
} else
Toast.makeText(getApplicationContext(), "檔案上傳失敗", Toast.LENGTH_LONG).show();
return;
}
正如上面,因為mUploadMessage.onReceiveValue(uri)中需要接收一個Uri型別的引數
因此又需要將圖片路徑轉換成Uri。這樣就奇蹟般的起效了。然後就能正常的顯示圖片。
**
以上純屬個人理解,如有錯誤歡迎指出,一起共同學習。
**