【版權申明】非商業目的可自由轉載
博文地址:https://blog.csdn.net/ShuSheng0007/article/details/97634019
出自:shusheng007
系列文章
Android開發之Webview中原生與JS交互
Android開發之在Webview中全屏播放視頻
概述
出於對成本或者其他原因的考慮,在移動開發中有時會採用混合開發的方式。即Web頁面可以跑在原生的WebView裏面,那麼就會遇到各種各樣的交互需求 ,我們今天要談的從web頁面選擇圖片或者文件就是其中之一。
實現方法
這個需求大概有兩種實現方式:
第一種:使用Webview
與JS
交互的方式。當點擊H5
頁面中的選擇文件按鈕(不要求是<input>
標籤),通過JS
調用原生的方法在這個方法裏面使用原生的方式打開文件選擇器。
第二種:使用H5
的<input type="file">
標籤,通過webview
提供的內置機制來打開文件選擇器。
如何選擇
哪種方式更加合適呢,這個需要放到具體應用場景去談論纔有意義。
-
當你的APP中已經存在一套H5與原生交互的機制,而且App對文件選擇器的定製要求較高,例如點擊上傳圖片按鈕打開像微信那樣的自定義圖片選擇器,那麼第一種方式更加適合。
-
當選擇文件這個功能不太重要而且使用非常低頻,或者要求不是很高,那麼選擇第二種較爲方便。
總體來說,第一種方式靈活性更大,自定義能力更強。第二種會存在各種適配性問題,但是簡單粗暴。
本文主要介紹第二種方式,第一種方式請參考 Android開發之Webview中原生與JS交互
WebView內置方案
主要是通過重寫WebChromeClient
來實現的,如下面的代碼所示。基本思想也很簡單:通過WebChromeClient
的方法以startActivityForResult
的方式打開系統的文件選擇器,選擇文件後在onActivityResult
中將結果回傳給Webview
即可。
當你的App最低支持版本爲Android5.0
及以上就很簡單了,只要重寫WebChromeClient
中的 onShowFileChooser()
的方法即可。但是如果是5.0以下,那麼還需要提供幾個適配的方法,如下面的代碼所示。
public class WebviewFileChooserAct extends AppCompatActivity{
private static final int REQUEST_CODE_FILE_CHOOSER = 1;
private ValueCallback<Uri> mUploadCallbackForLowApi;
private ValueCallback<Uri[]> mUploadCallbackForHighApi;
private WebView mWebView;
private WebChromeClient myWebChromeClient = new WebChromeClient() {
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
mUploadCallbackForHighApi = filePathCallback;
Intent intent = fileChooserParams.createIntent();
intent.addCategory(Intent.CATEGORY_OPENABLE);
try {
startActivityForResult(intent, REQUEST_CODE_FILE_CHOOSER);
} catch (ActivityNotFoundException e) {
mUploadCallbackForHighApi = null;
Toast.makeText(EShopAiCustomServiceAct.this, R.string.cant_open_file_chooser, Toast.LENGTH_LONG).show();
return false;
}
return true;
}
// For 3.0+
protected void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
openFilerChooser(uploadMsg);
}
//For Android 4.1+
protected void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
openFilerChooser(uploadMsg);
}
};
private WebViewClient myWebViewClient = new WebViewClient() {
@Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return;
}
//Web頁面加載失敗
}
@TargetApi(Build.VERSION_CODES.M)
@Override
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
super.onReceivedError(view, request, error);
if (request.isForMainFrame()) {
//Web頁面加載失敗
}
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_market_ai_custom_service);
mWebView = findViewById(R.id.webview);
configWebView(mWebView);
mWebView.loadUrl("you webpage url");
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CODE_FILE_CHOOSER && (resultCode == RESULT_OK || resultCode == RESULT_CANCELED)) {
afterFileChooseGoing(resultCode, data);
}
}
private void afterFileChooseGoing(int resultCode, Intent data) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (mUploadCallbackForHighApi == null) {
return;
}
mUploadCallbackForHighApi.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(resultCode, data));
mUploadCallbackForHighApi = null;
} else {
if (mUploadCallbackForLowApi == null) {
return;
}
Uri result = data == null ? null : data.getData();
mUploadCallbackForLowApi.onReceiveValue(result);
mUploadCallbackForLowApi = null;
}
}
private void configWebView(WebView webView) {
WebSettings settings = webView.getSettings();
settings.setCacheMode(WebSettings.LOAD_NO_CACHE);
settings.setAllowFileAccess(true);
settings.setDomStorageEnabled(true);
settings.setJavaScriptEnabled(true);
webView.setWebViewClient(myWebViewClient);
webView.setWebChromeClient(myWebChromeClient);
}
private void openFilerChooser(ValueCallback<Uri> uploadMsg) {
mUploadCallbackForLowApi = uploadMsg;
startActivityForResult(Intent.createChooser(getFilerChooserIntent(), "File Chooser"), REQUEST_CODE_FILE_CHOOSER);
}
private Intent getFilerChooserIntent() {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
return intent;
}
}
讓我們對上面的實現方式做一個小的梳理
- 在web頁面中加入
<input type="file">
標籤
<input id="uploadImage" type="file" accept="image/*" name="myPhoto" >
accept 是接收文件的類型,上面的標籤只接受image的類型
- 重寫
WebChromeClient
中的onShowFileChooser()
的方法,並在其中打開文件選擇器。 - 提供兼容低版本的方法
openFileChooser
,注意方法名稱不可改變。 - 在
onActivityResult()
方法中接收選擇結果並處理
notes:
1:注意同時處理成功選擇文件及放棄選擇文件兩種情況,如果不對放棄這種操作進行處理就會遇到下次點擊沒有響應的情況。
2:Android 4.4據說不起作用,我沒有做過相關驗證。
概述
用心傳播知識,只爲後輩生活更美好(少加班,多陪家人,或者有更多的時間去做其他有意義的事情)。