在Android開(kāi)發(fā)中,WebView的使用頻率越來(lái)越高,這里跟大家分享下WebView使用中的一些技巧或者注意點(diǎn)
11月21日更新:
修復(fù)Android 6.0網(wǎng)頁(yè)title獲取的bug,具體見(jiàn)-2.網(wǎng)頁(yè)title的獲取
11月4日更新:
新增-9.WebView視頻全屏播放
1.WebView頂部展示加載進(jìn)度
自定義控件,繼承WebView
public class ErmWebView extends WebView {
private ProgressBar progressbar;
public ErmWebView(Context context, AttributeSet attrs) {
super(context, attrs);
progressbar = new ProgressBar(context, null, android.R.attr.progressBarStyleHorizontal);
progressbar.setProgressDrawable(context.getResources().getDra(R.drawable.style_progressbar_web));//設(shè)置樣式
progressbar.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, 5, 0, 0));
addView(progressbar);
setWebChromeClient(new WebChromeClient());
}
public class WebChromeClient extends android.webkit.WebChromeClient {
@Override
public void onProgressChanged(WebView view, int newProgress) {//進(jìn)度
if (newProgress == 100) {
progressbar.setVisibility(GONE);
} else {
if (progressbar.getVisibility() == GONE)
progressbar.setVisibility(VISIBLE);
progressbar.setProgress(newProgress);
}
super.onProgressChanged(view, newProgress);
}
}
2.網(wǎng)頁(yè)title的獲取
需要在WebChromeClient的onReceivedTitle()中獲取,其他api獲取不可靠
webview控件在Android6.0上有一個(gè)bug,那就是onReceivedTitle()會(huì)調(diào)用兩次,一次為網(wǎng)頁(yè)的url,一次為網(wǎng)頁(yè)真正的title,故這里需要做一個(gè)過(guò)濾
mWebview.setWebChromeClient(new WebChromeClient(){
@Override
public void onReceivedTitle(WebView view, String title) {
super.onReceivedTitle(view, title);
//由于webview在title在6.0上會(huì)調(diào)用兩次,故這里過(guò)濾掉title為url的那次
if (!getUrl().contains(title)) mTitleBar.setTitle(title);//設(shè)置title
}
});
3.本地圖片(文件)選擇
web頁(yè)面中經(jīng)常需要用戶選取手機(jī)中的圖片(文件)上傳,這種默認(rèn)的js調(diào)用,在iOS和PC端是OK的,但是Android端需要處理,否則不會(huì)做任何操作
private ValueCallback<Uri> mUploadMessage;//回調(diào)圖片選擇,4.4以下
private ValueCallback<Uri[]> mUploadCallbackAboveL;//回調(diào)圖片選擇便瑟,5.0以上
mWebview.setWebChromeClient(new WebChromeClient(){
//由于是隱藏API,故沒(méi)有@Override注解
// For Android 3.0+單參數(shù)
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
mUploadMessage = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
if (getContext() instanceof Activity) {
((Activity) getContext()).startActivityForResult(Intent.createChooser(i, "File Chooser"), FILE_SELECT_CODE);
}
}
// For Android 3.0+多參數(shù)
public void openFileChooser(ValueCallback uploadMsg, String acceptType) {
mUploadMessage = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("*/*");
if (getContext() instanceof Activity) {
((Activity) getContext()).startActivityForResult(Intent.createChooser(i, "File Browser"), FILE_SELECT_CODE);
}
}
// For Android 4.1
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
mUploadMessage = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
if (getContext() instanceof Activity) {
((Activity) getContext()).startActivityForResult(Intent.createChooser(i, "File Chooser"), FILE_SELECT_CODE);
}
}
// For Android 5.0+
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) {
mUploadCallbackAboveL = filePathCallback;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("*/*");
if (getContext() instanceof Activity) {
((Activity) getContext()).startActivityForResult(Intent.createChooser(i, "File Browser"), FILE_SELECT_CODE);
}
return true;
}
}
//將選擇結(jié)果回調(diào)給網(wǎng)頁(yè)
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode != Activity.RESULT_OK) {
if (Build.VERSION.SDK_INT >= 21) //5.0以上版本處理
mWebview.getUploadCallbackAboveL().onReceiveValue(null);//取消選擇必須回調(diào)null,否則web處于阻塞狀態(tài),無(wú)法繼續(xù)操作
else
mWebview.getUploadMessage().onReceiveValue(null);
return;
}
switch (requestCode) {
case FILE_SELECT_CODE: {
if (Build.VERSION.SDK_INT >= 21) {//5.0以上版本處理
Uri uri = data.getData();
Uri[] uris = new Uri[]{uri};
mWebview.getUploadCallbackAboveL().onReceiveValue(uris);//回調(diào)給js
} else {//4.4以下處理
Uri uri = data.getData();
Logger.i(uri.toString());
mWebview.getUploadMessage().onReceiveValue(uri);
}
}
break;
}
}
- 以上代碼在大部分手機(jī)測(cè)試通過(guò),但不排除個(gè)別機(jī)型存在bug的可能
4.寬度自適應(yīng)
如果網(wǎng)頁(yè)比較寬,webView就可以左右滑動(dòng),用戶一屏看不到所有的內(nèi)容,體驗(yàn)會(huì)比較差,我們可以設(shè)置頁(yè)面自適應(yīng)
WebSettings settings = getSettings();
settings.setJavaScriptEnabled(true);//開(kāi)啟js
settings.setUseWideViewPort(true);//寬度自適應(yīng)
settings.setLoadWithOverviewMode(true);
settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
- html中的圖片需要自適應(yīng),否則可能會(huì)出現(xiàn)圖片特別寬,導(dǎo)致整個(gè)頁(yè)面無(wú)法自適應(yīng)
5.自定義網(wǎng)頁(yè)加載出錯(cuò)頁(yè)面
對(duì)于客戶端來(lái)說(shuō),加載出錯(cuò)包括 網(wǎng)絡(luò)出錯(cuò)+網(wǎng)頁(yè)Load出錯(cuò).默認(rèn)的出錯(cuò)頁(yè)面比較丑,并且會(huì)直接顯示URL,導(dǎo)致我們的url暴露.
mWebview.setWebViewClient(new WebViewClient() {
@SuppressWarnings("deprecation")
@Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {//低版本
mWebview.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);//去除默認(rèn)的404頁(yè)面
mEmpty.showErrorType("網(wǎng)頁(yè)加載出錯(cuò),請(qǐng)點(diǎn)擊重試");
}
@TargetApi(android.os.Build.VERSION_CODES.M)//編譯版本>23
@Override
public void onReceivedError(WebView view, WebResourceRequest req, WebResourceError rerr) {
onReceivedError(view, rerr.getErrorCode(), rerr.getDescription().toString(), req.getUrl().toString());
}
});
- 多次測(cè)試之后,上面這樣寫才能捕捉到所有加載出錯(cuò)的回調(diào),并且在大部分機(jī)型及API版本測(cè)試通過(guò).
6.返回鍵處理
用戶希望能夠返回到上一個(gè)網(wǎng)頁(yè),而不是直接退出當(dāng)前webActivity
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
mLeftTv.setVisibility(View.VISIBLE);
if (keyCode == KeyEvent.KEYCODE_BACK && mWebview.canGoBack()) {
mWebview.goBack();
return true;
}
return super.onKeyDown(keyCode, event);
}
7.退出WebView
WebView的底層調(diào)用WebKit內(nèi)核,加載也會(huì)另開(kāi)線程,所以當(dāng)WebView所在的Activity退出時(shí),WebView內(nèi)部的組建和線程可能并沒(méi)有銷毀,導(dǎo)致持續(xù)占用資源,甚至視頻或者音頻還在播放
@Override
protected void onPause() {
super.onPause();
if (mWebview != null) mWebview.onPause();//退出關(guān)閉webview
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mWebview != null) mWebview.destroy();//退出關(guān)閉webview
}
- 以上代碼在大部分手機(jī)上含視頻播放的web頁(yè)面測(cè)試通過(guò),視頻及視頻聲音不會(huì)再播放.但音頻web頁(yè)面退出未測(cè)試
8.與JS代碼互相調(diào)用
有時(shí)候JS與原生互相調(diào)用各自的方法
mWebview.addJavascriptInterface(new InfoJs(), "infojs");//增加js交互的方法
public class InfoJs {
@JavascriptInterface
public void showToast(String s) {
ToastUtils.showToast("展示toast " + s);
}
}
- JS調(diào)原生也可以用URL重定向來(lái)調(diào),這樣iOS比較好實(shí)現(xiàn).
- JS與原生的交互,網(wǎng)上有很多資料,寫的比較詳細(xì),如:android中Webview與javascript的交互(互相調(diào)用)
9.WebView視頻全屏播放
webview雖然默認(rèn)支持全屏播放的事件,但是在大部分手機(jī)上都是無(wú)法全屏的,要么點(diǎn)擊全屏是空白,要么沒(méi)有任何反應(yīng),好在WebView有提供全屏及取消全屏的回調(diào)事件,所以如果需要支持WebView的全屏播放,就需要處理一下:
1.布局文件中增加一個(gè)與WebView同等級(jí)的FrameLayout,用于裝載全屏的Video控件
<WebView
android:id="@+id/webView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<FrameLayout
android:id="@+id/video"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
2.處理全屏及取消全屏事件:
private class CustomWebViewChromeClient extends WebChromeClient{
@Override
public void onShowCustomView(View view, CustomViewCallback callback) {
fullScreen();
mWebview.setVisibility(View.GONE);
mVideo.setVisibility(View.VISIBLE);
mVideo.addView(view);
mCallBack=callback;
mWebTitle.setVisibility(View.GONE);//如果有titleBar,一并隱藏
super.onShowCustomView(view, callback);
}
@Override
public void onHideCustomView() {
fullScreen();
if (mCallBack!=null){
mCallBack.onCustomViewHidden();
}
mWebview.setVisibility(View.VISIBLE);
mVideo.removeAllViews();
mVideo.setVisibility(View.GONE);
mWebTitle.setVisibility(View.VISIBLE);//如果有titleBar,一并顯示出來(lái)
super.onHideCustomView();
}
}
private void fullScreen() {//強(qiáng)制切換屏幕方向
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
} else {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
}
關(guān)于作者
- 簡(jiǎn) 書:uncochen
- github:ChenZhen
- 新浪微博:@Chen丶振
- Email:18620156376@163.com