前言
自從交房后,每天除了上班奈惑,大部分時(shí)間都是在地鐵和公交上了吭净。不過有了這些時(shí)間,可以好好看看文章打打基礎(chǔ)肴甸,方便之后換新的環(huán)境寂殉。玩Android收錄了很多值得閱讀的文章,好的文章需要多讀幾次才有所收獲原在。但收錄但文章在手機(jī)上閱讀有一些東西比較影響閱讀體驗(yàn)友扰,比如廣告,比如要點(diǎn)擊取消折疊展開文章晤斩。這兩個(gè)已經(jīng)在Wandroid客戶端做了優(yōu)化焕檬,同時(shí)將文章內(nèi)容改成了深色模式姆坚,總的來說閱讀體驗(yàn)提高了好多澳泵。
當(dāng)在地鐵中到了某些路段,網(wǎng)絡(luò)信號(hào)很差兼呵,網(wǎng)頁經(jīng)常加載不出來兔辅。因此離線閱讀對(duì)我來說變得很重要了。所以在端午期間击喂,我新增了離線閱讀功能维苔,同時(shí)為了能更好的查看文章中的圖片,加入了圖片展示功能懂昂。具體可以看如下效果:
WebView網(wǎng)頁保存
為了能夠?qū)崿F(xiàn)文章離線閱讀介时,需要將整個(gè)網(wǎng)頁保存下來。我們主要關(guān)注的是html內(nèi)容和相關(guān)的圖片Gif資源》腥幔基于Chromium實(shí)現(xiàn)的WebView本身也會(huì)在網(wǎng)頁加載時(shí)緩存網(wǎng)頁的資源(css/js/圖片等)循衰。為了方便圖片控制展示,這邊選擇通過Glide
緩存WebView中的圖片與Gif資源褐澎。
文本保存
通過document.documentElement.outerHTML
可以獲得網(wǎng)頁的html內(nèi)容会钝,可以在webview中通過addJavascriptInterface
方法傳入用于js層調(diào)用java層的對(duì)象如android
。于是我們可以通過如下方式保存網(wǎng)頁內(nèi)容:
private fun downloadHtml() {
val script = """
javascript:(function(){
var url = document.URL.toString();
var html = document.documentElement.outerHTML;
android.saveHtml(url,html);
})();
""".trimIndent()
webView.loadUrl(script)
}
特別注意的是js代碼中不要寫注釋工三,否則會(huì)加載失敗迁酸。WebView中加載js腳本比較難調(diào)試,我們可以在
chrome://inspect
中Console
控制臺(tái)下調(diào)試代碼的正確性俭正。
對(duì)應(yīng)addJavascriptInterface
對(duì)象需要有如下方法奸鬓,我們可以將html內(nèi)容保存到sd卡下或者/data/data/${application}
目錄下
/**
* 離線保存html
*/
@JavascriptInterface
fun saveHtml(url: String, html: String) {
loading.postValue(true)
Constants.IO.execute {
FileUtil.saveHtml(url, html)
msg.postValue("下載成功")
loading.postValue(false)
}
}
圖片緩存
為了方便控制webview中的圖片,保證點(diǎn)擊縮放展示功能中圖片的流暢性掸读,我們將圖片資源放到Glide中緩存全蝶。這樣webview中的圖片使用Glide加載,點(diǎn)擊圖片展示再用Glide加載時(shí)可以共享緩存資源寺枉。
我們可以通過重寫WebViewClient
類的shouldInterceptRequest
重定向一些資源請(qǐng)求抑淫。不過一些圖片資源的url并不是嚴(yán)格按照.jpg/png/gif
的格式,無法判斷一些url是否是圖片資源姥闪。因此需要通過head
請(qǐng)求獲取content-type
始苇。同時(shí)還需要將結(jié)果保存起來(用于離線情況,okhttp并不支持head請(qǐng)求的緩存)筐喳。
private val typeDao = AppDataBase.get().urlTypeDao()
fun head(url: String?): String {
val md5 = MD5Utils.stringToMD5(url)
val value = typeDao.getType(md5)
if (value == null) {
val client = OkHttpClient.Builder()
.addNetworkInterceptor(CacheInterceptor())
.build()
val request = Request.Builder()
.url(url)
.head()
.build()
val res = client.newCall(request).execute()
val type = res.header("content-type")
val result = type ?: ""
typeDao.insert(UrlTypeVO(md5, result))
return result
}
return value
}
于是催式,shouldInterceptRequest
方法中就可以重定向圖片類型的請(qǐng)求了。
val head = Wget.head(url)
if (head.startsWith("image")) {
val bytes = GlideUtil.syncLoad(url, head)
if (bytes != null) {
return WebResourceResponse(
head,
"utf-8",
ByteArrayInputStream(bytes)
)
}
}
這里我們需要通過Glide同步獲取圖片的byte[]
數(shù)據(jù)避归,還要區(qū)分圖片
與gif
荣月。
public class GlideUtil {
public static byte[] syncLoad(String url, String type) {
boolean isGif = type.endsWith("gif");
if (isGif) {
try {
FutureTarget<byte[]> target = Glide.with(App.instance)
.as(byte[].class)
.load(url)
.decode(GifDrawable.class).submit();
return target.get();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
FutureTarget<Bitmap> target = Glide.with(App.instance)
.asBitmap().load(url).submit();
try {
Bitmap bitmap = target.get();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
return baos.toByteArray();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
至此webview中圖片的加載最終通過的是Glide
,圖片也會(huì)通過它緩存到內(nèi)存和磁盤中梳毙。
圖片展示
為了頂部的效果圖哺窄,需要添加圖片的點(diǎn)擊事件,還要知道圖片所在屏幕中的位置和尺寸(用于轉(zhuǎn)場(chǎng)效果)账锹。
添加點(diǎn)擊事件萌业,獲取圖片位置
在webview加載網(wǎng)頁結(jié)束后,我們給每個(gè)img
添加onclick
事件奸柬,獲取圖片地址生年,尺寸,位置信息廓奕。一些站點(diǎn)(如微信)圖片是懶加載的抱婉,在離線模式下由于跨域問題最終導(dǎo)致圖片無法加載档叔。因此需要從dataset中取出url重新設(shè)置。還有一些站點(diǎn)(CSDN)本身有點(diǎn)擊展示效果蒸绩,需要stopPropagation
阻止事件冒泡屏蔽蹲蒲。
var imgs = document.getElementsByTagName("img");
for(var i=0;i<imgs.length;i++){
var dataset = imgs[i].dataset;
if(dataset && dataset.src && dataset.src!=imgs[i].src){
imgs[i].src = dataset.src;
}
imgs[i].onclick = function(e){
var target = e.target;
var rect = target.getBoundingClientRect();
android.showImage(target.src,rect.x,rect.y,rect.width,rect.height,outerWidth);
e.stopPropagation();
};
}
這里為什么還要在傳outerWidth
(瀏覽器寬度)呢,在調(diào)試中(見下圖)侵贵,我們發(fā)現(xiàn)通過getBoundingClientRect
獲取的尺寸寬度和手機(jī)屏幕的寬度并不是一個(gè)單位届搁。因此需要傳outerWidth
用于Android端ImageView
實(shí)際尺寸的計(jì)算。
圖片共享元素轉(zhuǎn)場(chǎng)效果
在頁面加載完成后窍育,我們手動(dòng)注入設(shè)置圖片點(diǎn)擊事件的js代碼卡睦。當(dāng)點(diǎn)擊圖片時(shí),就可以得到圖片url漱抓,尺寸表锻,位置信息。在Android端就可以通過共享元素實(shí)現(xiàn)轉(zhuǎn)場(chǎng)效果了乞娄。再次之前我們需要在WebView所在的布局文件中加入ImageView
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<io.github.iamyours.wandroid.widget.WanWebView
android:id="@+id/webView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:overScrollMode="never" />
<io.github.iamyours.wandroid.widget.TouchImageView
android:id="@+id/showImage"
android:layout_width="100dp"
android:layout_height="100dp"
android:visibility="invisible"
app:showImage="@{vm.image}" />
</FrameLayout>
通過DataBinding
中綁定自定義屬性瞬逊,實(shí)現(xiàn)共享元素轉(zhuǎn)場(chǎng)效果遏考。
@BindingAdapter(value = ["showImage"])
fun bindImage(iv: ImageView, showImage: PositionImage?) {
showImage?.run {
val lp = iv.layoutParams as ViewGroup.MarginLayoutParams
val parentWidth = iv.context.resources.displayMetrics.widthPixels
val scale = parentWidth / clientWidth
lp.width = (width * scale).toInt()
lp.height = (height * scale).toInt()
lp.leftMargin = (x * scale).toInt()
lp.topMargin = (y * scale).toInt()
iv.layoutParams = lp
iv.requestLayout()
iv.displayWithUrl(url, lp.width, lp.height) {
iv.postDelayed({
val activity = iv.getActivity()
activity?.let {
val pair: Pair<View, String> = Pair(iv, "image")
val option =
ActivityOptionsCompat.makeSceneTransitionAnimation(
it,
pair
)
val intent = Intent(it, ImageShowActivity::class.java)
intent.putExtra("url", url)
it.startActivityForResult(intent, 1, option.toBundle())
}
}, 200)
}
}
}
項(xiàng)目地址
https://github.com/iamyours/Wandroid
- 暗黑系列
- 全網(wǎng)獨(dú)一適配 掘金/簡(jiǎn)書/CSDN/公眾號(hào)/玩Android文章黑夜模式
- 無廣告理盆,無需點(diǎn)擊展開
- 圖片顯示,支持縮放科雳,共享元素?zé)o縫轉(zhuǎn)場(chǎng)
- 支持離線閱讀范删,地鐵上閱讀更方便
后續(xù)功能
- 代碼圖片展示(開發(fā)中蕾域,現(xiàn)支持掘金,簡(jiǎn)書)
- 文章分類收藏