WebView選擇檔案
之前開發的時候都沒有注意到,在webview的頁面裡面,如果有個地方要選擇圖片,不管怎麼點選都不會自動幫你跳轉到選擇圖片或者選擇檔案的頁面,不管怎麼點選都是沒反應。
在ios裡面是點選能自動跳轉的,而android不行,所以需要自己去寫跳轉的邏輯。
1.監聽H5頁面點選選擇檔案
在WebChromeClient中,有一個方法可以監聽H5選擇檔案。低版本是openFileChooser,高版本是onShowFileChooser,我們需要在WebChromeClient重寫這些方法。
public class MyWebChromeClient extends WebChromeClient { private WebFileChoseListener webFileChoseListener; // 3.0 + 呼叫這個方法 public void openFileChooser(ValueCallback filePathCallback, String acceptType) { if (webFileChoseListener!= null){ webFileChoseListener.getFile(filePathCallback); } } // Android > 4.1.1 呼叫這個方法 public void openFileChooser(ValueCallback<Uri> filePathCallback, String acceptType, String capture) { if (webFileChoseListener!= null){ webFileChoseListener.getFile(filePathCallback); } } // Android > 5.0 呼叫這個方法 @Override public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) { if (webFileChoseListener!= null){ webFileChoseListener.getFile(filePathCallback); } return true; } public void setBnWebFileChoseListener(WebFileChoseListener webFileChoseListener) { this.webFileChoseListener= webFileChoseListener; } }
不要在WebChromeClient 裡面寫邏輯,單一職權嘛,所以我們寫個監聽回撥給外層。這裡寫了3個函式是因為我的最小版本是3.1,所以都寫上,如果最小版本是3.0以下,還要多寫個方法,這個可以在網上找到。最後看看我們監聽回撥的介面。
public interface WebFileChoseListener { void getFile(ValueCallback valueCallback); }
接收返回的filePathCallback是ValueCallback型別,之後我們會用這個物件把本地圖片的uri傳回去。
2.跳轉相簿
在外部重寫getFile方法
@Override public void getFile(ValueCallback valueCallback) { this.valueCallback = valueCallback; Intent intent = new Intent(Intent.ACTION_PICK,MediaStore.Images.Media.EXTERNAL_CONTENT_URI); intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,"image/*"); startActivityForResult(intent, Constant.INTENT_PHONE); }
就直接跳轉相簿就好了,這裡只是我的寫法,跳轉相簿、跳轉資料夾這些愛怎麼寫怎麼寫,這裡的邏輯就已經變成普通跳轉邏輯了,我的目的就是拿到uri就行,我不管你跳不跳轉或者跳轉到哪個頁面,只要最終拿到uri返回給h5就行。
3.返回結果給h5
我上面是跳轉系統相簿,獲取圖片成功肯定會回撥給onActivityResult
@Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == BnConstant.INTENT_PHONE){ if (resultCode == Activity.RESULT_OK) { WebUtils.seleteH5Phone(data, valueCallback); } } }
valueCallback在上面this.valueCallback = valueCallback有儲存到全域性變數。WebUtils寫個工具類,免得每次都要重複寫這堆邏輯。
public class WebUtils { public static void seleteH5File(Intent data, ValueCallback valueCallback){ if (valueCallback == null){ // todo valueCallback 為空的邏輯 return; } try { Uri[] results = null; String dataString = data.getDataString(); if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) { ClipData clipData = data.getClipData(); if (clipData != null) { results = new Uri[clipData.getItemCount()]; for (int i = 0; i < clipData.getItemCount(); i++) { ClipData.Item item = clipData.getItemAt(i); results[i] = item.getUri(); } } } if (dataString != null) { results = new Uri[]{Uri.parse(dataString)}; valueCallback.onReceiveValue(results); } }catch (Exception e){ e.printStackTrace(); } valueCallback = null; } }
這個valueCallback.onReceiveValue()在5.0 以上要傳Uri[]才行,單傳uri是沒用的。回撥中的引數也有泛型ValueCallback<Uri[]>,傳對應的就行,所以我這裡是沒對舊版本的進行相容。
4. H5頁面的按鈕無法再次點選
有的朋友可能會發生這種情況,H5頁面的按鈕第二次沒法點選。加入第一次點選之後,我跳到了相簿,我沒有選圖片,直接返回。之後再點H5頁面的按鈕就會沒反應。
這是因為前一次沒把結果回傳給H5。
所以要在某些地方加上valueCallback.onReceiveValue(null),就算選擇失敗,也要返回。
將上面程式碼改為
@Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == Constant.INTENT_PHONE){ if (resultCode == Activity.RESULT_OK) { BnWebUtils.seleteH5File(data, valueCallback); }else { valueCallback.onReceiveValue(null); valueCallback = null; } } }
public class WebUtils { public static void seleteH5File(Intent data, ValueCallback valueCallback){ ...... try { ...... }catch (Exception e){ e.printStackTrace(); valueCallback.onReceiveValue(null); } valueCallback = null; } }
這部分邏輯也並非很複雜,只是很容易被忽視,就算你在某個專案寫了,隔時間長了,下個專案也不一定會記得,所以要記錄下來。