內(nèi)容摘要
- shouldOverrideUrlLoading
- 全屏播放視頻
- 無法播放視頻問題
- 重定向頁面回退問題
- 網(wǎng)頁加載進(jìn)度
- 自定義WebView彈不出輸入法問題
shouldOverrideUrlLoading
/**
* Give the host application a chance to take over the control when a new
* url is about to be loaded in the current WebView. If WebViewClient is not
* provided, by default WebView will ask Activity Manager to choose the
* proper handler for the url. If WebViewClient is provided, return true
* means the host application handles the url, while return false means the
* current WebView handles the url.
* This method is not called for requests using the POST "method".
*
* @param view The WebView that is initiating the callback.
* @param url The url to be loaded.
* @return True if the host application wants to leave the current WebView
* and handle the url itself, otherwise return false.
*/
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return false;
}
當(dāng)url即將加載到
WebView
時給當(dāng)前應(yīng)用一個機(jī)會去接管控制地啰。如果沒有提供WebViewClient
(也就是沒有調(diào)用WebView#setWebViewClient
方法) ,默認(rèn)請求ActivityManager
選擇一個合適的url處理器(比如系統(tǒng)瀏覽器去加載這個url)款熬。如果設(shè)置了WebViewClient
,返回true
代表當(dāng)前應(yīng)用已經(jīng)處理了url雁比,返回false
意味著當(dāng)前WebView
url蔚袍。該方法對POST請求無效。
全屏播放視頻
inner class MWebChromeClient : WebChromeClient() {
private var mActivityConfig: ActivityConfig? = null
override fun onShowCustomView(view: View, requestedOrientation: Int, callback: CustomViewCallback) {
this.onShowCustomView(view, callback)
}
override fun onShowCustomView(view: View?, callback: CustomViewCallback?) {
super.onShowCustomView(view, callback)
saveActivityConfiguration()
activity?.apply {
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
}
if (mVideoView != null) {
callback?.onCustomViewHidden()
}
mVideoView = view
val parent = mVideoView?.parent as? ViewGroup
parent?.removeView(mVideoView)
mVideoContainer.addView(mVideoView)
mVideoContainer.visibility = View.VISIBLE
}
override fun onHideCustomView() {
super.onHideCustomView()
restoreActivityConfiguration()
mVideoContainer.removeAllViews()
mVideoView = null
mVideoContainer.visibility = View.INVISIBLE
}
private fun restoreActivityConfiguration() {
if (mActivityConfig != null) {
activity?.apply {
requestedOrientation = mActivityConfig!!.orientation
window.decorView.systemUiVisibility = mActivityConfig!!.systemUiVisibility
window.setFlags(mActivityConfig!!.flags, -1)
}
}
}
private fun saveActivityConfiguration() {
mActivityConfig = ActivityConfig(activity!!)
}
}
private class ActivityConfig(activity: Activity) {
internal val orientation = activity.requestedOrientation
internal val systemUiVisibility = activity.window.decorView.systemUiVisibility
internal val flags = activity.window.attributes.flags
}
基本思路就是在WebView
上面蓋一層鋪滿整個屏幕(或者其他需求,如小窗播放)ViewGroup,全面時切換到橫屏模式,將播放器添加到mVideoContainer
.
上面的代碼對橫屏切換時的Activity一些狀態(tài)進(jìn)行了保存,以便退出全屏后恢復(fù)全屏之前的狀態(tài). 另外,屏幕切換Activity會重走生命周期, 需要在AndroidManifest.xml注冊:
<activity
android:name=".activity.WebActivity"
android:configChanges="keyboardHidden|screenSize|orientation"/>
有些網(wǎng)頁沒有回調(diào)onShowCustomView如何處理, 希望有大神支招
無法播放視頻問題
// WebSettings.java
/**
* Configures the WebView's behavior when a secure origin attempts to load a resource from an
* insecure origin.
*
* By default, apps that target {@link android.os.Build.VERSION_CODES#KITKAT} or below default
* to {@link #MIXED_CONTENT_ALWAYS_ALLOW}. Apps targeting
* {@link android.os.Build.VERSION_CODES#LOLLIPOP} default to {@link #MIXED_CONTENT_NEVER_ALLOW}.
*
* The preferred and most secure mode of operation for the WebView is
* {@link #MIXED_CONTENT_NEVER_ALLOW} and use of {@link #MIXED_CONTENT_ALWAYS_ALLOW} is
* strongly discouraged.
*
* @param mode The mixed content mode to use. One of {@link #MIXED_CONTENT_NEVER_ALLOW},
* {@link #MIXED_CONTENT_ALWAYS_ALLOW} or {@link #MIXED_CONTENT_COMPATIBILITY_MODE}.
*/
public abstract void setMixedContentMode(int mode);
/**
* Used with {@link #setMixedContentMode}
*
* In this mode, the WebView will allow a secure origin to load content from any other origin,
* even if that origin is insecure. This is the least secure mode of operation for the WebView,
* and where possible apps should not set this mode.
*/
public static final int MIXED_CONTENT_ALWAYS_ALLOW = 0;
/**
* Used with {@link #setMixedContentMode}
*
* In this mode, the WebView will not allow a secure origin to load content from an insecure
* origin. This is the preferred and most secure mode of operation for the WebView and apps are
* strongly advised to use this mode.
*/
public static final int MIXED_CONTENT_NEVER_ALLOW = 1;
/**
* Used with {@link #setMixedContentMode}
*
* In this mode, the WebView will attempt to be compatible with the approach of a modern web
* browser with regard to mixed content. Some insecure content may be allowed to be loaded by
* a secure origin and other types of content will be blocked. The types of content are allowed
* or blocked may change release to release and are not explicitly defined.
*
* This mode is intended to be used by apps that are not in control of the content that they
* render but desire to operate in a reasonably secure environment. For highest security, apps
* are recommended to use {@link #MIXED_CONTENT_NEVER_ALLOW}.
*/
public static final int MIXED_CONTENT_COMPATIBILITY_MODE = 2;
android 5.0開始修改了內(nèi)容的混合模式, 5.0之前默認(rèn)為MIXED_CONTENT_ALWAYS_ALLOW
, 5.0開始改為默認(rèn)MIXED_CONTENT_NEVER_ALLOW
, 因此需要開啟混合模式, 鑒于安全性優(yōu)先考慮MIXED_CONTENT_COMPATIBILITY_MODE
兼容模式.
val settings = mVebView.settings
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
settings.mixedContentMode = WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE
}
重定向頁面回退問題
WebView具有記錄瀏覽歷史的功能,允許用戶回退/前進(jìn)到頁面, 以回退為例, 通常這樣處理:
class WebActivity: Activity {
private lateinit var mWebView:WebView
override fun onBackPressed() {
if (mWebView.canGoBack()) {
mWebView.goBack();
} else {
super.onBackPressed()
}
}
}
對于重定向的頁面這樣是有問題的. 以A
重定向到B
為例:
loadUrl(A)
時會重定向到B
, 最終顯示的是B頁面,于是A
,B
都在WebView的歷史中. 如果回退到A
那么又會進(jìn)行重定向到B
, 如此反復(fù)導(dǎo)致無法退出的死循環(huán)中.
android-21開始,新增了以下方法
android.webkit.WebViewClient#shouldOverrideUrlLoading(android.webkit.WebView, android.webkit.WebResourceRequest)
可以重寫shouldOverrideUrlLoading
方法,通過WebResourceRequest#isRedirect
來判斷是否為重定向的鏈接, 那5.0之前的系統(tǒng)怎么辦? 況且這種方法如果將非重定向的鏈接存儲, 那么假如初始便是加載了一個重定向的url, 這種情況也會被忽略. 所以判斷鏈接是否為重定向的方式應(yīng)該行不通的.
我們知道一個重定向的請求會攜帶一個Referer
的請求頭, 表示從那個頁面重定向過來的, 保存Referer
的那個url到棧中可以解決重定向問題.
WebView#getOriginalUrl
Added in API level 3
String getOriginalUrl ()
Gets the original URL for the current page. This is not always the same as the URL
passed to WebViewClient.onPageStarted because although the load for that URL has
begun, the current page may not have changed. Also, there may have been redirects
resulting in a different URL to that originally requested.
Returns the URL that was originally requested for the current page
獲得當(dāng)前頁面的原始URL. 原始URL并非總是和傳遞給
WebViewClient.onPageStarted
的URL一樣, 因為即使url已經(jīng)開始加載, 而當(dāng)前頁面卻沒有改變. 此外,可能有重定向
導(dǎo)致與最初請求的URL不同
這個就是我們要的原始鏈接. 下面是我的處理方式(非完整代碼):
class WebActivity: Activity {
private lateinit var mWebView: WebView
private val urlStack = LinkedList<String>()
override fun onBackPressed(){
if(!canGoBack()) {
super.onBackPressed()
}
}
private fun canGoBack(): Boolean {
mWebView.stopLoading()
synchronized(urlStack) {
if (!urlStack.isEmpty()) {
urlStack.pop()
}
if (!urlStack.isEmpty()) {
mWebView.loadUrl(urlStack.pop())
return true
}
return false
}
}
inner class MWebClient : WebViewClient() {
@Suppress("DEPRECATION")
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
return shouldOverrideUrlLoading(view, request.url.toString())
}
override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {
if (url.startsWith("http://") || url.startsWith("https://")) {
val originalUrl = view.originalUrl
if (originalUrl != null && originalUrl != urlStack.peek()) {
urlStack.push(originalUrl)
}
return false
} else {
parseUrl(url)
return true
}
}
}
private fun parseUrl(url: String) {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
if (intent.resolveActivity(context!!.packageManager) != null) {
try {
startActivity(intent)
} catch (e: SecurityException) {
e.printStackTrace()
}
}
}
}
網(wǎng)頁加載進(jìn)度
重寫WebChromeClient#onProgressChanged
即可
class MWebChromeClient : WebChromeClient() {
override fun onProgressChanged(view: WebView?, newProgress: Int) {
when (newProgress) {
100 -> {
mProgress.visibility = View.INVISIBLE
stopRefresh()
}
else -> {
mProgress.visibility = View.VISIBLE
mProgress.progress = newProgress
}
}
}
}
自定義WebView彈不出輸入法問題
一般自定義View都會重寫三個構(gòu)造方法, 默認(rèn)傳入attrs=null, defStyleAttr=0
即可奸柬。但是像
class MyWebView @JvmOverloads constructor(
context: Context?,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : WebView(context, attrs, defStyleAttr)
這樣操作自定義WebView時卻彈不出輸入法生年,直接使用WebView卻可以?問題出在構(gòu)造方法的調(diào)用上:
public WebView(Context context, AttributeSet attrs) {
this(context, attrs, com.android.internal.R.attr.webViewStyle);
}
這里是有默認(rèn)屬性值的廓奕,具體為:
<item name="webViewStyle">@style/Widget.WebView</item>
<style name="Widget.WebView">
<item name="focusable">true</item>
<item name="focusableInTouchMode">true</item>
<item name="scrollbars">horizontal|vertical</item>
</style>
所以給出解決方法:
class MyWebView @JvmOverloads constructor(
context: Context?,
attrs: AttributeSet? = null,
defStyleAttr: Int = android.R.attr.webViewStyle
) : WebView(context, attrs, defStyleAttr)
而知道是焦點引起的輸入法問題(如EditTextView, CheckBox), 通過代碼設(shè)置focusable
和focusableInTouchMode
也可以抱婉。