前段時間因為客戶需求,做一個客戶端結合web微網站的應用。其中,這個應用設計到了要修改頭像,但是這個頁面卻是在微網站上實現的,意味著網站要調用到Android的打開文件的方法,那麼這個通過webview是怎麼實現的呢?
經過跟服務器的同事討論發現,方法都是跟pc上是一樣的,都是調用一個叫input type=file的屬性,於是我就開始找,webview是怎麼響應這個屬性的了。
於是翻網站找到資料,不難查到,想要適用php上調用打開文件的方法,webview就要重寫一個名為openFileChooser的方法。
但是這個方法的使用卻不簡單,這個方法是要調用webview的setWebChromeClient方法,然後重寫一個WebChromeClient類。來到這一步,相信有點開發經驗的同行都不難解決。問題的關鍵就在於,當你重寫WebChromeClient這個類的時候會發現,根本就沒有openFileChooser這個方法,那要怎麼重寫呢?是不是意味著這個方法其實行不通?於是再次翻查資料,發現原來這個方法居然是隱藏方法,並不不存在顯性的繼承重寫關系。
最後,我發現要使用這個方法,還得自己繼承WebChromeClient這個類把openFileChooser(ValueCallback<Uri> uploadFile)這個方法給寫出來,代碼如下:
abstract class TestWebChromeClient extends WebChromeClient
{
private WebChromeClient mWrappedClient;
protected TestWebChromeClient(WebChromeClient wrappedClient)
{
mWrappedClient = wrappedClient;
}
/** {@inheritDoc} */
@Override
public void onProgressChanged(WebView view, int newProgress)
{
mWrappedClient.onProgressChanged(view, newProgress);
}
/** {@inheritDoc} */
@Override
public void onReceivedTitle(WebView view, String title)
{
mWrappedClient.onReceivedTitle(view, title);
}
/** {@inheritDoc} */
@Override
public void onReceivedIcon(WebView view, Bitmap icon)
{
mWrappedClient.onReceivedIcon(view, icon);
}
/** {@inheritDoc} */
@Override
public void onReceivedTouchIconUrl(WebView view, String url, boolean precomposed)
{
mWrappedClient.onReceivedTouchIconUrl(view, url, precomposed);
}
/** {@inheritDoc} */
@Override
public void onShowCustomView(View view, CustomViewCallback callback)
{
mWrappedClient.onShowCustomView(view, callback);
}
/** {@inheritDoc} */
@Override
public void onHideCustomView()
{
mWrappedClient.onHideCustomView();
}
/** {@inheritDoc} */
@Override
public boolean onCreateWindow(WebView view, boolean dialog, boolean userGesture,
Message resultMsg)
{
return mWrappedClient.onCreateWindow(view, dialog, userGesture, resultMsg);
}
/** {@inheritDoc} */
@Override
public void onRequestFocus(WebView view)
{
mWrappedClient.onRequestFocus(view);
}
/** {@inheritDoc} */
@Override
public void onCloseWindow(WebView window)
{
mWrappedClient.onCloseWindow(window);
}
/** {@inheritDoc} */
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result)
{
return mWrappedClient.onJsAlert(view, url, message, result);
}
/** {@inheritDoc} */
@Override
public boolean onJsConfirm(WebView view, String url, String message, JsResult result)
{
return mWrappedClient.onJsConfirm(view, url, message, result);
}
/** {@inheritDoc} */
@Override
public boolean onJsPrompt(WebView view, String url, String message,
String defaultValue, JsPromptResult result)
{
return mWrappedClient.onJsPrompt(view, url, message, defaultValue, result);
}
/** {@inheritDoc} */
@Override
public boolean onJsBeforeUnload(WebView view, String url, String message,
JsResult result)
{
return mWrappedClient.onJsBeforeUnload(view, url, message, result);
}
/** {@inheritDoc} */
@Override
public void onExceededDatabaseQuota(String url, String databaseIdentifier,
long currentQuota, long estimatedSize, long totalUsedQuota,
WebStorage.QuotaUpdater quotaUpdater)
{
mWrappedClient.onExceededDatabaseQuota(url, databaseIdentifier, currentQuota,
estimatedSize, totalUsedQuota, quotaUpdater);
}
/** {@inheritDoc} */
@Override
public void onReachedMaxAppCacheSize(long spaceNeeded, long totalUsedQuota,
WebStorage.QuotaUpdater quotaUpdater)
{
mWrappedClient
.onReachedMaxAppCacheSize(spaceNeeded, totalUsedQuota, quotaUpdater);
}
/** {@inheritDoc} */
@Override
public void onGeolocationPermissionsShowPrompt(String origin,
GeolocationPermissions.Callback callback)
{
mWrappedClient.onGeolocationPermissionsShowPrompt(origin, callback);
}
/** {@inheritDoc} */
@Override
public void onGeolocationPermissionsHidePrompt()
{
mWrappedClient.onGeolocationPermissionsHidePrompt();
}
/** {@inheritDoc} */
@Override
public boolean onJsTimeout()
{
return mWrappedClient.onJsTimeout();
}
/** {@inheritDoc} */
@Override
@Deprecated
public void onConsoleMessage(String message, int lineNumber, String sourceID)
{
mWrappedClient.onConsoleMessage(message, lineNumber, sourceID);
}
/** {@inheritDoc} */
@Override
public boolean onConsoleMessage(ConsoleMessage consoleMessage)
{
return mWrappedClient.onConsoleMessage(consoleMessage);
}
/** {@inheritDoc} */
@Override
public Bitmap getDefaultVideoPoster()
{
return mWrappedClient.getDefaultVideoPoster();
}
/** {@inheritDoc} */
@Override
public View getVideoLoadingProgressView()
{
return mWrappedClient.getVideoLoadingProgressView();
}
/** {@inheritDoc} */
@Override
public void getVisitedHistory(ValueCallback<String[]> callback)
{
mWrappedClient.getVisitedHistory(callback);
}
/** {@inheritDoc} */
public void openFileChooser(ValueCallback<Uri> uploadFile)
{
((TestWebChromeClient) mWrappedClient).openFileChooser(uploadFile);
}
}
以上代碼我是在eoe上發現的,並不是我自己參詳發現的哈。但是原作是誰我就不知道了,因為這段代碼你只要百度一下openFileChooser這個方法你就能找到。
當你這樣寫好,然後就是去設置WebChromeClient的時候了。設置方法如下:
mContentView.web_main_web.setWebChromeClient(new TestWebChromeClient(
new WebChromeClient())
{
public void openFileChooser(ValueCallback<Uri> uploadFile)
{
if (mUploadMessage != null)
return;
mUploadMessage = uploadFile;
//自己寫你的調用圖片方法,我這裡是封裝了調用圖片的方法的
//關鍵點在於這個mUploadMessage參數,獲取到圖片以後傳給這個參數回去就可以了。
//具體用法百度一下就有。
Select_Activity.start(mContext,mUploadMessage,indexUrlString,FILE_SELECTED);
}
});
當你設置完後,這個時候你就該興高采烈地去測試了。
於是,你有可能會興高采烈地發現,不行!!完全沒有反應!
是不是開始懷疑這個方法是坑人的?是不是在努力罵撸主?來來來,別生氣,讓我告訴你真相。
我告訴你哦,這個方法其實一點都不吭人,真的可以啊,不過~這是在3.0以前的sdk上有效...但是現在的主流Android機基本都是4.0以上了,哪裡還有3.0以前的系統?
於是你又開始了新的一輪罵娘,為什麼我會知道?因為我那個時候也是這個反應!
那時候我努力地翻資料,把百度、eoe、CSDN、德問、博客園、安卓巴士、DEVDIV都翻爛了,終於找到了原因,原來泥煤的3.0的要多加一個參數才能生效!
於是我傻乎乎的仿照人家重寫的openFileChooser方法,給TestWebChromeClient這個類添加了一個openFileChooser(ValueCallback<Uri> uploadFile, String acceptType)方法。(具體這個acceptType參數有什麼用,我還不怎麼清楚,有知道的大神麻煩告知一下哈)在webview的setWebChromeClient方法裡也添加了一個對應調用方法。
於是新一輪測試又開始了。
終於,你又一次興高采烈地罵娘了,泥煤的還是不行啊!(po主:喂!別打頭,把我打傻了以後就不能分享技術了!)
於是,我終於相信了國內搜索引擎和論壇的不靠譜,我投靠了谷歌和stackoverflow。
說實話,po主的英文很爛,爛得掉渣了,只有小學5年級的水准(po主那個時候是四年紀開始學的英語)所以不到逼不得已都不想投靠外國網站,實在是看不到,這搜索不來啊!
我找了足足一天得谷歌,最後通過谷歌找到了stackoverflow上有這個相同的問題(我這英文的水平只能通過谷歌使用了,捂臉)
人家大神解答到,原來尼瑪的4.0以後的版本又多了一個參數於是乎,再加一個openFileChooser(ValueCallback<Uri> uploadFile, String acceptType,String capture)方法就可以了。
下面我貼上TestWebChromeClient的完整代碼。
abstract class TestWebChromeClient extends WebChromeClient
{
private WebChromeClient mWrappedClient;
protected TestWebChromeClient(WebChromeClient wrappedClient)
{
mWrappedClient = wrappedClient;
}
/** {@inheritDoc} */
@Override
public void onProgressChanged(WebView view, int newProgress)
{
mWrappedClient.onProgressChanged(view, newProgress);
}
/** {@inheritDoc} */
@Override
public void onReceivedTitle(WebView view, String title)
{
mWrappedClient.onReceivedTitle(view, title);
}
/** {@inheritDoc} */
@Override
public void onReceivedIcon(WebView view, Bitmap icon)
{
mWrappedClient.onReceivedIcon(view, icon);
}
/** {@inheritDoc} */
@Override
public void onReceivedTouchIconUrl(WebView view, String url, boolean precomposed)
{
mWrappedClient.onReceivedTouchIconUrl(view, url, precomposed);
}
/** {@inheritDoc} */
@Override
public void onShowCustomView(View view, CustomViewCallback callback)
{
mWrappedClient.onShowCustomView(view, callback);
}
/** {@inheritDoc} */
@Override
public void onHideCustomView()
{
mWrappedClient.onHideCustomView();
}
/** {@inheritDoc} */
@Override
public boolean onCreateWindow(WebView view, boolean dialog, boolean userGesture,
Message resultMsg)
{
return mWrappedClient.onCreateWindow(view, dialog, userGesture, resultMsg);
}
/** {@inheritDoc} */
@Override
public void onRequestFocus(WebView view)
{
mWrappedClient.onRequestFocus(view);
}
/** {@inheritDoc} */
@Override
public void onCloseWindow(WebView window)
{
mWrappedClient.onCloseWindow(window);
}
/** {@inheritDoc} */
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result)
{
return mWrappedClient.onJsAlert(view, url, message, result);
}
/** {@inheritDoc} */
@Override
public boolean onJsConfirm(WebView view, String url, String message, JsResult result)
{
return mWrappedClient.onJsConfirm(view, url, message, result);
}
/** {@inheritDoc} */
@Override
public boolean onJsPrompt(WebView view, String url, String message,
String defaultValue, JsPromptResult result)
{
return mWrappedClient.onJsPrompt(view, url, message, defaultValue, result);
}
/** {@inheritDoc} */
@Override
public boolean onJsBeforeUnload(WebView view, String url, String message,
JsResult result)
{
return mWrappedClient.onJsBeforeUnload(view, url, message, result);
}
/** {@inheritDoc} */
@Override
public void onExceededDatabaseQuota(String url, String databaseIdentifier,
long currentQuota, long estimatedSize, long totalUsedQuota,
WebStorage.QuotaUpdater quotaUpdater)
{
mWrappedClient.onExceededDatabaseQuota(url, databaseIdentifier, currentQuota,
estimatedSize, totalUsedQuota, quotaUpdater);
}
/** {@inheritDoc} */
@Override
public void onReachedMaxAppCacheSize(long spaceNeeded, long totalUsedQuota,
WebStorage.QuotaUpdater quotaUpdater)
{
mWrappedClient
.onReachedMaxAppCacheSize(spaceNeeded, totalUsedQuota, quotaUpdater);
}
/** {@inheritDoc} */
@Override
public void onGeolocationPermissionsShowPrompt(String origin,
GeolocationPermissions.Callback callback)
{
mWrappedClient.onGeolocationPermissionsShowPrompt(origin, callback);
}
/** {@inheritDoc} */
@Override
public void onGeolocationPermissionsHidePrompt()
{
mWrappedClient.onGeolocationPermissionsHidePrompt();
}
/** {@inheritDoc} */
@Override
public boolean onJsTimeout()
{
return mWrappedClient.onJsTimeout();
}
/** {@inheritDoc} */
@Override
@Deprecated
public void onConsoleMessage(String message, int lineNumber, String sourceID)
{
mWrappedClient.onConsoleMessage(message, lineNumber, sourceID);
}
/** {@inheritDoc} */
@Override
public boolean onConsoleMessage(ConsoleMessage consoleMessage)
{
return mWrappedClient.onConsoleMessage(consoleMessage);
}
/** {@inheritDoc} */
@Override
public Bitmap getDefaultVideoPoster()
{
return mWrappedClient.getDefaultVideoPoster();
}
/** {@inheritDoc} */
@Override
public View getVideoLoadingProgressView()
{
return mWrappedClient.getVideoLoadingProgressView();
}
/** {@inheritDoc} */
@Override
public void getVisitedHistory(ValueCallback<String[]> callback)
{
mWrappedClient.getVisitedHistory(callback);
}
/** {@inheritDoc} */
public void openFileChooser(ValueCallback<Uri> uploadFile)
{
((TestWebChromeClient) mWrappedClient).openFileChooser(uploadFile);
}
/** {@inheritDoc} */
public void openFileChooser(ValueCallback<Uri> uploadFile, String acceptType)
{
((TestWebChromeClient) mWrappedClient).openFileChooser(uploadFile, acceptType);
}
/** {@inheritDoc} */
public void openFileChooser(ValueCallback<Uri> uploadFile, String acceptType,
String capture)
{
((TestWebChromeClient) mWrappedClient).openFileChooser(uploadFile, acceptType,
capture);
}
}
一下是setWebChromeClient需要添加的方法。
public void openFileChooser(ValueCallback<Uri> uploadFile, String acceptType)
{
openFileChooser(uploadFile);
}
public void openFileChooser(ValueCallback<Uri> uploadFile, String acceptType,
String capture)
{
openFileChooser(uploadFile);
}
盡管前面很多部分都不難找到,但是後面這段3.0和4.0坑爹隱藏代碼實在讓人慘死。我當初都差點放棄了,國內論壇我還沒有發現到關於這個描述,所以我就在這裡分享一下,也當作是馬克,省得以後忘記了。
這回終於不用再被罵娘了,感謝CCAV,感謝TVC,感謝老爸,感謝老媽,感謝老外。
各位有什麼不懂的歡迎交流,可以來關注我新浪微博琴弦欲奏,歡迎共同探討Android的應用開發的問題。