Android開發筆記(一百五十二)H5通過WebView上傳圖片
阿新 • • 發佈:2019-01-31
上一篇文章介紹了WebView與JS之間的資料互動,其實就是把字串傳來傳去,這對文字格式的資訊傳輸來說倒還湊合,倘若要傳輸圖片資訊就不管用了。所以,要想讓h5網頁支援從手機上傳圖片,還得另外想辦法,當然各版本的Android系統也都提供了相應的解決辦法。在Android 4.*系統上面,開發者可以重寫WebChromeClient的openFileChooser函式;在Android 5.0以上的系統,開發者可以重寫WebChromeClient的onShowFileChooser函式。話雖如此,可實際編碼的時候,會發現並不容易,因為不但要相容各種版本的安卓系統,而且要考慮不同操作方式下面的處理步驟。
首先是Android不同系統的適配問題,對於4.*版本要重寫openFileChooser方法,對於5.0以上版本要重寫onShowFileChooser方法。另外注意二者的回撥方式也不一樣,4.*的回撥引數型別是ValueCallback<Uri>,而5.0以上的回撥引數型別是ValueCallback<Uri[]>,因此要宣告兩個回撥引數變數,分別用來儲存二者各自的回撥資訊。相關程式碼如下所示:
然後就上傳圖片這個功能而言,既要支援從手機相簿中挑選已有的圖片,也要支援現場拍照並即時上傳拍攝好的照片。如此一來,就不能僅僅從相簿選擇檔案,而要彈出一個列表對話方塊,好讓使用者決定是從相簿上傳圖片,還是當場拍照當場上傳。所以接下來得同時實現這兩種上傳方式,示例程式碼如下:
選擇好圖片確定後(含拍照和從相簿選取),App程式碼進入到onActivityResult方法內部,開發者在此校驗結果程式碼,根據圖片選取形式分別獲得具體的圖片資料,然後區分4.*系統和5.+系統將圖片傳給h5頁面。下面是onActivityResult方法的處理程式碼:
其後還要注意,使用者開啟相簿或者開啟相機的時候,也有可能什麼都不做就返回到原頁面,由於這個取消選擇的操作沒有走完全流程,導致h5網頁的回撥資源沒有回收,使用者再去上傳圖片之時會發現頁面不會響應了,因此開發者要在程式碼中手工替h5頁面回收回撥資源,這樣下次使用者才能繼續上傳圖片。手工回收資源的辦法是重寫Activity的onResume函式,具體實現程式碼見下:
接著即可開啟實際的h5頁面進行圖片上傳測試啦,這裡的h5測試網址用的是http://m.54php.cn/demo/h5_upload,測試的呼叫程式碼很簡單,設定好WebView的訪問地址以及瀏覽器物件就好了,例子程式碼如下所示:
最後觀察一下WebView配合上述測試網址的執行介面,先看看Android4.4手機的測試畫面,下面的左圖為開啟測試網址的初始介面,右圖為點選上傳按鈕後在螢幕中央彈出選擇對話方塊:
先在對話方塊中選擇從相簿上傳,成功上傳圖片後的h5頁面如下面的左圖所示;重新點選上傳按鈕,這次選擇使用相機拍照,並把照片成功上傳後的h5頁面如下面的右圖所示:
再來看看Android6.0手機的測試畫面,下面的左圖為開啟測試網址的初始介面,右圖為點選上傳按鈕後在螢幕下方彈出選擇對話方塊:
先在對話方塊中選擇從相簿上傳,成功上傳圖片後的h5頁面如下面的左圖所示;重新點選上傳按鈕,這次選擇使用相機拍照,並把照片成功上傳後的h5頁面如下面的右圖所示:
點此檢視Android開發筆記的完整目錄
首先是Android不同系統的適配問題,對於4.*版本要重寫openFileChooser方法,對於5.0以上版本要重寫onShowFileChooser方法。另外注意二者的回撥方式也不一樣,4.*的回撥引數型別是ValueCallback<Uri>,而5.0以上的回撥引數型別是ValueCallback<Uri[]>,因此要宣告兩個回撥引數變數,分別用來儲存二者各自的回撥資訊。相關程式碼如下所示:
private static ValueCallback<Uri> mUploadMessage; private static ValueCallback<Uri[]> mUploadMessageLollipop; private class MyWebChromeClient extends WebChromeClient { // Android 4.*(包括4.1、4.2、4.3、4.4) public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) { Log.d(TAG, "openFileChooser 4.*"); mUploadMessage = uploadMsg; openSelectDialog(); } // Android 5.0+(包括5.*、6.0、7.*、8.*) @Override public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) { Log.d(TAG, "openFileChooser 5.0+"); mUploadMessageLollipop = filePathCallback; openSelectDialog(); return true; } }
然後就上傳圖片這個功能而言,既要支援從手機相簿中挑選已有的圖片,也要支援現場拍照並即時上傳拍攝好的照片。如此一來,就不能僅僅從相簿選擇檔案,而要彈出一個列表對話方塊,好讓使用者決定是從相簿上傳圖片,還是當場拍照當場上傳。所以接下來得同時實現這兩種上傳方式,示例程式碼如下:
private String mCameraPhotoPath = null; private void openSelectDialog() { // 宣告相機的拍照行為 Intent photoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); if (photoIntent.resolveActivity(getPackageManager()) != null) { mCameraPhotoPath = "file:" + getExternalFilesDir(Environment.DIRECTORY_PICTURES) .toString() + "/" + DateUtil.getNowDateTime("") + ".jpg"; Log.d(TAG, "photoFile=" + mCameraPhotoPath); photoIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.parse(mCameraPhotoPath)); } Intent[] intentArray = new Intent[] { photoIntent }; // 宣告相簿的開啟行為 Intent selectionIntent = new Intent(Intent.ACTION_GET_CONTENT); selectionIntent.addCategory(Intent.CATEGORY_OPENABLE); selectionIntent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); selectionIntent.setType("image/*"); // 彈出含相機和相簿在內的列表對話方塊 Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER); chooserIntent.putExtra(Intent.EXTRA_INTENT, selectionIntent); chooserIntent.putExtra(Intent.EXTRA_TITLE, "請拍照或選擇圖片"); chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray); startActivityForResult(Intent.createChooser(chooserIntent, "選擇圖片"), 1); }
選擇好圖片確定後(含拍照和從相簿選取),App程式碼進入到onActivityResult方法內部,開發者在此校驗結果程式碼,根據圖片選取形式分別獲得具體的圖片資料,然後區分4.*系統和5.+系統將圖片傳給h5頁面。下面是onActivityResult方法的處理程式碼:
private static final int FILE_SELECT_CODE = 1; private int mResultCode = Activity.RESULT_CANCELED; @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { Log.d(TAG, "onActivityResult requestCode=" + requestCode + ", resultCode=" + resultCode); if (requestCode != FILE_SELECT_CODE || (mUploadMessage == null && mUploadMessageLollipop == null)) { super.onActivityResult(requestCode, resultCode, data); return; } mResultCode = resultCode; Log.d(TAG, "mCameraPhotoPath=" + mCameraPhotoPath); if (resultCode == Activity.RESULT_OK) { uploadPhoto(resultCode, data); } } private void uploadPhoto(int resultCode, Intent data) { long fileSize = 0; try { String file_path = mCameraPhotoPath.replace("file:", ""); File file = new File(file_path); fileSize = file.length(); } catch (Exception e) { e.printStackTrace(); } if (data != null || mCameraPhotoPath != null) { Integer count = 1; ClipData images = null; try { images = data.getClipData(); } catch (Exception e) { e.printStackTrace(); } if (images == null && data != null && data.getDataString() != null) { count = data.getDataString().length(); } else if (images != null) { count = images.getItemCount(); } Uri[] results = new Uri[count]; // Check that the response is a good one if (resultCode == Activity.RESULT_OK) { Log.d(TAG, "fileSize=" + fileSize); if (fileSize != 0) { // If there is not data, then we may have taken a photo if (mCameraPhotoPath != null) { results = new Uri[] { Uri.parse(mCameraPhotoPath) }; } } else if (data.getClipData() == null) { results = new Uri[] { Uri.parse(data.getDataString()) }; } else { for (int i = 0; i < images.getItemCount(); i++) { results[i] = images.getItemAt(i).getUri(); } } } // 區分不同系統分別返回上傳結果 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { mUploadMessageLollipop.onReceiveValue(results); mUploadMessageLollipop = null; } else { mUploadMessage.onReceiveValue(results[0]); mUploadMessage = null; } } }
其後還要注意,使用者開啟相簿或者開啟相機的時候,也有可能什麼都不做就返回到原頁面,由於這個取消選擇的操作沒有走完全流程,導致h5網頁的回撥資源沒有回收,使用者再去上傳圖片之時會發現頁面不會響應了,因此開發者要在程式碼中手工替h5頁面回收回撥資源,這樣下次使用者才能繼續上傳圖片。手工回收資源的辦法是重寫Activity的onResume函式,具體實現程式碼見下:
@Override
protected void onResume() {
super.onResume();
// 取消選擇時需要回調onReceiveValue,否則網頁會掛住,不會再響應點選事件
if (mResultCode == Activity.RESULT_CANCELED) {
try {
if (mUploadMessageLollipop != null) {
mUploadMessageLollipop.onReceiveValue(null);
}
if (mUploadMessage != null) {
mUploadMessage.onReceiveValue(null);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
接著即可開啟實際的h5頁面進行圖片上傳測試啦,這裡的h5測試網址用的是http://m.54php.cn/demo/h5_upload,測試的呼叫程式碼很簡單,設定好WebView的訪問地址以及瀏覽器物件就好了,例子程式碼如下所示:
WebView webView = (WebView) findViewById(R.id.webView);
WebSettings webSettings = webView.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setBuiltInZoomControls(true);
webView.loadUrl("http://m.54php.cn/demo/h5_upload");
webView.setWebViewClient(new MyWebViewClient(this));
webView.setWebChromeClient(new MyWebChromeClient());
最後觀察一下WebView配合上述測試網址的執行介面,先看看Android4.4手機的測試畫面,下面的左圖為開啟測試網址的初始介面,右圖為點選上傳按鈕後在螢幕中央彈出選擇對話方塊:
先在對話方塊中選擇從相簿上傳,成功上傳圖片後的h5頁面如下面的左圖所示;重新點選上傳按鈕,這次選擇使用相機拍照,並把照片成功上傳後的h5頁面如下面的右圖所示:
再來看看Android6.0手機的測試畫面,下面的左圖為開啟測試網址的初始介面,右圖為點選上傳按鈕後在螢幕下方彈出選擇對話方塊:
先在對話方塊中選擇從相簿上傳,成功上傳圖片後的h5頁面如下面的左圖所示;重新點選上傳按鈕,這次選擇使用相機拍照,並把照片成功上傳後的h5頁面如下面的右圖所示:
點此檢視Android開發筆記的完整目錄