WebView使用的一些注意事項

內(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è)置focusablefocusableInTouchMode也可以抱婉。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市桌粉,隨后出現(xiàn)的幾起案子蒸绩,更是在濱河造成了極大的恐慌,老刑警劉巖铃肯,帶你破解...
    沈念sama閱讀 222,729評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件患亿,死亡現(xiàn)場離奇詭異,居然都是意外死亡押逼,警方通過查閱死者的電腦和手機(jī)窍育,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,226評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來宴胧,“玉大人漱抓,你說我怎么就攤上這事∷∑耄” “怎么了乞娄?”我有些...
    開封第一講書人閱讀 169,461評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長显歧。 經(jīng)常有香客問我仪或,道長,這世上最難降的妖魔是什么士骤? 我笑而不...
    開封第一講書人閱讀 60,135評論 1 300
  • 正文 為了忘掉前任范删,我火速辦了婚禮,結(jié)果婚禮上拷肌,老公的妹妹穿的比我還像新娘到旦。我一直安慰自己,他們只是感情好巨缘,可當(dāng)我...
    茶點故事閱讀 69,130評論 6 398
  • 文/花漫 我一把揭開白布添忘。 她就那樣靜靜地躺著,像睡著了一般若锁。 火紅的嫁衣襯著肌膚如雪搁骑。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,736評論 1 312
  • 那天,我揣著相機(jī)與錄音仲器,去河邊找鬼煤率。 笑死,一個胖子當(dāng)著我的面吹牛乏冀,可吹牛的內(nèi)容都是我干的蝶糯。 我是一名探鬼主播,決...
    沈念sama閱讀 41,179評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼煤辨,長吁一口氣:“原來是場噩夢啊……” “哼裳涛!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起众辨,我...
    開封第一講書人閱讀 40,124評論 0 277
  • 序言:老撾萬榮一對情侶失蹤端三,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后鹃彻,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體郊闯,經(jīng)...
    沈念sama閱讀 46,657評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,723評論 3 342
  • 正文 我和宋清朗相戀三年蛛株,在試婚紗的時候發(fā)現(xiàn)自己被綠了团赁。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,872評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡谨履,死狀恐怖欢摄,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情笋粟,我是刑警寧澤怀挠,帶...
    沈念sama閱讀 36,533評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站害捕,受9級特大地震影響绿淋,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜尝盼,卻給世界環(huán)境...
    茶點故事閱讀 42,213評論 3 336
  • 文/蒙蒙 一吞滞、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧盾沫,春花似錦裁赠、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,700評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至祖娘,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背渐苏。 一陣腳步聲響...
    開封第一講書人閱讀 33,819評論 1 274
  • 我被黑心中介騙來泰國打工掀潮, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人琼富。 一個月前我還...
    沈念sama閱讀 49,304評論 3 379
  • 正文 我出身青樓仪吧,卻偏偏與公主長得像,于是被迫代替她去往敵國和親鞠眉。 傳聞我的和親對象是個殘疾皇子薯鼠,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,876評論 2 361