前段时间因为客户需求,做一个客户端结合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的应用开发的问题。