1. 程式人生 > >webview夜間模式適配小結

webview夜間模式適配小結

做webview的夜間模式,來來回回折騰了好幾天,基本上都是copy網上的各種css程式碼。

1. 一般實現webview夜間模式,要麼就是核心自己實現了這個功能,一個api就能搞定(像uc核心:目前沒有實現真正的開放;騰訊x5核心:開放,不知道能不能自定義夜間模式的顏色值之類的);

2. 要麼就是自定義一個html模板,從後臺獲取json資料,把資料解析出來,把各個節點的資料通過js寫入到這個模板中,相當於自己在本地組裝了一個新的html,丟到webview裡面,這樣web載入起來也比較快,因為都是一些本地的資料;

3. 再者就是像我這樣很無奈的,需要通過js程式碼向原來的web中注入css程式碼來改變網頁的風格了,說白了就是從後臺拿到一個url網頁地址,把這個url丟給webview,然後在webview載入完成後注入css程式碼來改變網頁的風格。

用3這種方式最主要的問題就是這個css程式碼都有些啥,世界上的網頁多種多樣,要儘可能的適配各種標籤,但是也未必能覆蓋得全面。對於一個從來沒有接觸過html,js, css的我來說,著實著急了一把。但是最終還是勉勉強強的實現了這個功能。在這裡記錄一下新路歷程。

首先,去網上搜羅了一堆css程式碼:

html,body,header,div,a,span,table,tr,td,th,tbody,p,form,input,ul,ol,li,dl,dt,dd,section,footer,nav,h1,h2,h3,h4,h5,h6,em,pre {
	background: #333 !important;
	color: #616161!important;
	border-color: #454530!important;
	text-shadow: 0!important;
	-webkit-text-fill-color: none!important;
}

html a,html a * {
	color: #5a8498!important;
	text-decoration: underline!important;
}

html a:visited,html a:visited *,html a:active,html a:active * {
	color: #505f64!important;
}

html a:hover,html a:hover * {
	color: #cef!important;
}

html input,html select,html button,html textarea {
	background: #4d4c40!important;
	border: 1px solid #5c5a46!important;
	border-top-color: #494533!important;
	border-bottom-color: #494533!important;
}

html input[type=button],html input[type=submit],html input[type=reset],html input[type=image],html button {
	border-top-color: #494533!important;
	border-bottom-color: #494533!important;
}

html input:focus,html select:focus,html option:focus,html button:focus,html textarea:focus {
	background: #5c5b3e!important;
	color: #fff!important;
	border-color: #494100 #635d00 #474531!important;
	outline: 1px solid #041d29!important;
}

html input[type=button]:focus,html input[type=submit]:focus,html input[type=reset]:focus,html input[type=image]:focus,html button:focus {
	border-color: #494533 #635d00 #474100!important;
}

html input[type=radio] {
	background: none!important;
	border-color: #333!important;
	border-width: 0!important;
}

html img[src],html input[type=image] {
	opacity: .5;
}

html img[src]:hover,html input[type=image]:hover {
	opacity: 1;
}

html,html body {
	scrollbar-base-color: #4d4c40 !important;
	scrollbar-face-color: #5a5b3c !important;
	scrollbar-shadow-color: #5a5b3c !important;
	scrollbar-highlight-color: #5c5b3c !important;
	scrollbar-dlight-color: #5c5b3c !important;
	scrollbar-darkshadow-color: #474531 !important;
	scrollbar-track-color: #4d4c40 !important;
	scrollbar-arrow-color: #000 !important;
	scrollbar-3dlight-color: #6a6957 !important;
}

dt a {
	background-color: #333 !important;
}
原文裡的程式碼直接是字串的,這裡使用了線上格式化程式碼格式了一下,這樣看起來比較清晰,我自己實現的是新建了一個css檔案,把css程式碼寫入這個檔案中,然後從檔案裡面解析出這個css程式碼(這個後面會給出)。
以上程式碼主要是設定了各個標籤的backgroud和color, 我自己的理解是backgroud是背景色,color是作用到文字上的。但是在我實際使用的過程中,backgroud會把這個標籤下的圖片隱藏掉,如果我設定了div{backgroud:#顏色} 這個的話,這個article-top-image這個div下的圖片就不顯示了,猜想可能是它的style=“backgroud”原來應該載入一個圖片出來,結果我給設定了顏色值,所以就顯示不出來了。解決的辦法就是把backgroud換成backgroud-color,也就是隻作用backgroud-color。


html,body,applet,object,h1,h2,h3,h4,h5,h6,blockquote,pre,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,p,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,th,td {
	background: rgba(0,0,0,0) !important;
	color: #fff !important;
	border-color: #A0A0A0 !important;
}

div,input,button,textarea,select,option,optgroup {
	background-color: #000 !important;
	color: #fff !important;
	border-color: #A0A0A0 !important;
}

a,a * {
	color: #ffffff !important;
	text-decoration: none !important;
	font-weight: bold !important;
	background-color: rgba(0,0,0,0) !important;
}

a:active,a:hover,a:active *,a:hover * {
	color: #1F72D0 !important;
	background-color: rgba(0,0,0,0) !important;
}

p,span {
	font color: #FF0000 !important;
	color: #ffffff !important;
	background-color: rgba(0,0,0,0) !important;
}

html {
	-webkit-filter: contrast(50%);
}
看這裡的大多數節點就是使用了backgroud-color屬性,並且使用rgba顏色值。對,就是這個rgba中的a解決了我的另一個問題。a代表了透明度。在我的是一個適配的頁面中
https://www.thestartmagazine.com/article/f0195498-9a5e-4131-a5e1-67136f192bed?ref=TWVpWnUtU0RLJSQlSHlYNFBrZmVIRmRtMUNoN1lGNEw3MTBod004ODU5N0olJCUxMzU3OTg2NDI%3D&recommendationId=TIME_BL&theme=template6&userId=d7451302-3494-4e9c-945c-66d0de7adea4

在網頁最後有一個資訊列表:


最開始了我設定:

div {
	background-color: #201f26 !important;
}
結果右邊的圖片就沒有了:(為了方便看,我這裡設定了另一個顏色:#e4d5d5)


其實看其原始碼,就是一個簡單的<div>節點:


目前尚不清楚為何會出現這個現象。後來無意中把顏色值調整成rgba模式,結果就能顯示圖片了:

backgroud-color:rgba(228, 213, 213, 0.27)


這裡順便提一下,我們在開發手機網頁的時候,可以在chrome中模擬,載入網頁的時候,按F12會進入審查元素,右邊可以看到html整個網頁的原始碼資訊,選擇toggle device toolbar可以選擇需要的裝置資訊,然後重新整理一下頁面就能載入和手機上顯示的一致的網頁了。


一般會在原始碼中新增一些css元素來改變網頁的效果,即改即顯,還是比較方便的。

在除錯的過程中,還實驗了另外一種方式:

html {
    background-color : #2e2e2e !important;
    zoom             : 1.055;
  }
body {
    text-rendering             : optimizeSpeed;
    image-rendering            : optimizeSpeed;
    -webkit-image-rendering    : optimizeSpeed;

    text-shadow            : 0 0 0 #000;
    -webkit-font-smoothing : antialiased;

    -webkit-filter         : invert(1)hue-rotate(180deg);
    -ms-filter             : invert(1)hue-rotate(180deg);
    filter                 : invert(1)hue-rotate(180deg);
}

input,textarea,select{color:purple}

img, video, iframe, canvas, svg,
embed[type='application/x-shockwave-flash'],
object[type='application/x-shockwave-flash'],
*[style*='url('] {
    -webkit-filter : invert(1)hue-rotate(180deg) !important;
    -ms-filter     : invert(1)hue-rotate(180deg) !important;
    filter         : invert(1)hue-rotate(180deg) !important;
}
也就是利用css的filter濾鏡效果來實現,filter濾鏡有各種效果可實現,具體可參考:

上面的程式碼主要是利用invert反色和hue-rotate色調來實現,但是實際效果不是很理想。也就是作用於整個頁面,把白的地方變成黑的,其他顏色值都進行了一定的反轉,像上面那種操作,只是反轉了顏色,並不能使用自己想要的顏色,也就是說反轉後的顏色值依賴於原網頁的顏色值,這並不是想要的結果。

當時就在想,filter就不能指定我需要的顏色嗎?

其實在色彩界,有三種表示顏色的方式:

RGB、HEX、HSL

我開始使用的是HEX,就是這種形式:#232323,三種顏色之間可以自由轉換:

轉換器

所以我就想把之前的hex模式的顏色轉換成hsl的,正好,filter的濾鏡可以來表示這個色值:

filter的hue-rotate就代表了H色相,saturate代表了S飽和度,brightness代表了L亮度
所以通過轉換之後得到了:


然後打算這麼使用:

-webkit-filter : hue-rotate(149deg)saturate(10.1%)brightness(13.5%);
實際效果簡直慘不忍睹,不經顏色值不對,圖片失真,文字失真。

回想一下,不失真才怪呢,filter是把整個頁面做了一次濾鏡效果,肯定就是在原來的影象上覆蓋一層濾鏡,畢竟我只是想簡單的改變一下背景,在改變一下文字效果而已。遂放棄這種方式。

最後,終極方式來了:

/*所有元素的背景色都設定為黑夜模式,原來使用#201f26(rgb)會影響celltick資訊詳情頁裡列表項的圖片顯示,這裡改用rgba,有0.8的透明度*/
*, *:before, *:after {
    box-sizing: inherit;
    background-color: rgba(32, 31, 38, 0.8) !important;
}

/*背景顏色和一般字型顏色*/
div,h1,h2,h3,h4,h5,h6,p,body,em,html,link,textarea,form,select,input,span,button,em,menu,aside,table,tr,td,nav,dl,dt,dd,amp-iframe,main{
    /*background-color: rgba(32, 31, 38, 0.8) !important;*/
    color: #888888!important;
    border-color: #555555 !important;
    text-shadow            : 0 0 0 #000; /*去掉文字的陰影效果*/
}

/*超連結*/
a{
    color:#3c5180 !important;
}

strong {
	display: block;
}

img, video, iframe, canvas, svg,amp-social-share,
embed[type='application/x-shockwave-flash'],
object[type='application/x-shockwave-flash'],
*[style*='url('] {
    -webkit-filter : opacity(50%);
    -ms-filter     : opacity(50%);
    filter         : opacity(50%);
}
實現方式還是修改backgroud-color屬性,使用rgba模式設定。

對一些特殊的標籤,比如img,視訊,動畫,廣告之類的進行了透明度的改變。整體效果下來非常不錯了。
目前的話,還有一些個別的標籤沒有適配到,後期再慢慢優化了。


網上都說在webview的onPageFinished回撥的時候再去執行css的注入,但是在實際使用過程中,某些個網頁並不會回撥這個方法,並且在載入前會顯示原來網頁的顏色。所以為了能一開始載入網頁 的時候就顯示夜間模式,我在幾個地方都呼叫了注入方法:

webviewClient的onPageStarted,onPageFinished,

webviewChromeClient的onProgressChanged

這幾個回撥的時候都去執行注入方法:

webView.loadUrl("javascript:(function() {" + "var parent = document.getElementsByTagName('head').item(0);" + "var style = document.createElement('style');" + "style.type = 'text/css';" + "style.innerHTML = window.atob('" + code + "');" + "parent.appendChild(style)" + "})();");
其中code是個String型別,是css檔案轉換後的字串。
InputStream is = AppContextUtils.getAppContext().getResources().openRawResource(R.raw.night);
                    byte[] buffer = new byte[0];
                    try {
                        buffer = new byte[is.available()];
                        is.read(buffer);
                    } catch (IOException e) {
                        e.printStackTrace();
                    } finally {
                        try {
                            is.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    nightCode = Base64.encodeToString(buffer, Base64.NO_WRAP);
R.raw.night是存放在工程raw檔案加下的css檔案,裡面的內容就是終極版本的css程式碼。

這裡存放到檔案中是方便以後修改,然後在應用初始化的時候去載入這個css檔案,轉換成String型別的nightCode,在需要使用夜間模式的時候,就把這個code注入到webview中了。

夜間模式適配,基本上告一段落了。

總結了幾點:

1. 需要熟悉js html css,這樣才能排坑

2. 使用chrome先調好,再在程式碼中實驗,一般chrome調好的樣式在程式碼中有同樣的效果。