序言
最近項(xiàng)目需求中需要實(shí)現(xiàn)WebView顯示內(nèi)容瞪醋,下方顯示評(píng)論列表,列表還可以分頁加載践盼。我最近做了技術(shù)預(yù)研宾巍,難度主要是實(shí)時(shí)獲取WebView的高度。
效果
1.分頁加載
2.動(dòng)態(tài)獲取高度顶霞,點(diǎn)擊閱讀更多,會(huì)將幾個(gè)隱藏的div蓝厌,顯示出來,造成WebView內(nèi)容高度變化拓提。
實(shí)現(xiàn)
使用的ScrollView包裹一層LinearLayout隧膘,LinearLayout中依次防止WebView和RecyclerView寺惫。布局如下:
<?xml version="1.0" encoding="utf-8"?>
<com.trs.studyview.view.TRSScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/scrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.trs.studyview.MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.trs.studyview.view.TRSWebView
android:id="@+id/webview"
android:layout_width="match_parent"
android:layout_height="100dp" />
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
</com.trs.studyview.view.TRSScrollView>
問題一西雀,如果獲取WebView高度
1.通過js獲取
以下是js獲取高度的方法
網(wǎng)頁可見區(qū)域?qū)挘?document.body.clientWidth
網(wǎng)頁可見區(qū)域高: document.body.clientHeight
網(wǎng)頁可見區(qū)域?qū)挘?document.body.offsetWidth (包括邊線的寬)
網(wǎng)頁可見區(qū)域高: document.body.offsetHeight (包括邊線的高)
網(wǎng)頁正文全文寬: document.body.scrollWidth
網(wǎng)頁正文全文高: document.body.scrollHeight
網(wǎng)頁被卷去的高: document.body.scrollTop
網(wǎng)頁被卷去的左: document.body.scrollLeft
網(wǎng)頁正文部分上: window.screenTop
網(wǎng)頁正文部分左: window.screenLeft
屏幕分辨率的高: window.screen.height
屏幕分辨率的寬: window.screen.width
屏幕可用工作區(qū)高度: window.screen.availHeight
屏幕可用工作區(qū)寬度: window.screen.availWidth
下面是測試代碼
package com.trs.studyview;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
import android.webkit.JavascriptInterface;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import com.trs.studyview.adpater.CommentAdapter;
import com.trs.studyview.view.TRSScrollView;
import com.trs.studyview.view.TRSWebView;
public class MainActivity extends AppCompatActivity {
TRSWebView webView;
RecyclerView recyclerView;
String url = "http://app.nxnews.net/ningxia/cfwz/zt/201708/t20170807_1859270.html";
CommentAdapter adapter;
TRSScrollView scrollView;
Mobile mobile = new Mobile();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
scrollView = $(R.id.scrollView);
webView = $(R.id.webview);
recyclerView = $(R.id.recyclerView);
WebSettings setting = webView.getSettings();
setting.setJavaScriptEnabled(true);//支持js
webView.addJavascriptInterface(mobile, "mobile");
webView.setWebViewClient(mClient);
webView.loadUrl(url);
adapter = new CommentAdapter();
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
scrollView.setRecyclerView(recyclerView);
}
private class Mobile {
@JavascriptInterface
public void onGetWebContentHeight(String type, int height) {
Log.i("zzz", "onGetWebContentHeight type=" + type + " height=" + height);
}
}
private String[] getHeightJs = new String[]{
"document.body.clientHeight",
"document.body.offsetHeight",
"document.body.scrollHeight",
"document.body.scrollTop",
"window.screen.availHeight"
};
private WebViewClient mClient = new WebViewClient() {
@Override
public void onPageFinished(WebView view, String url) {
for (int i = 0; i < getHeightJs.length; i++) {
String js = "javascript:mobile.onGetWebContentHeight(\"" + getHeightJs[i] + "\"," + getHeightJs[i] + ")";
view.loadUrl(js);
}
}
};
protected <T extends View> T $(int id) {
return (T) findViewById(id);
}
}
結(jié)果如下:
可以告訴大家以上高度都不對蒋搜。
2.通過getContentHeight()獲取高度
思路是在網(wǎng)頁加載完成以后通過WebView的getContentHeight()方法獲取高度
代碼如下:
private WebViewClient mClient = new WebViewClient() {
@Override
public void onPageFinished(WebView view, String url) {
Log.i("zzz", "webview contentHeight=" + view.getContentHeight());
}
};
運(yùn)行結(jié)果
結(jié)論:高度不對
3.通過measure()進(jìn)行測量
思路,在WebView加載網(wǎng)頁完畢以后育谬,通過webview.measure(0,0)進(jìn)行測量,兩個(gè)0表示對寬高無限制锰镀。
代碼改動(dòng)部分
private class Mobile {
@JavascriptInterface
public void onGetWebContentHeight() {
//重新調(diào)整webview高度
webView.post(() -> {
webView.measure(0, 0);
int measuredHeight = webView.getMeasuredHeight();
Log.i("zzz", "measuredHeight=" + measuredHeight);
});
}
}
private WebViewClient mClient = new WebViewClient() {
@Override
public void onPageFinished(WebView view, String url) {
mobile.onGetWebContentHeight();
}
};
運(yùn)行結(jié)果
結(jié)論:大家可以看到這個(gè)高度是最高的咖刃,也的確是webView網(wǎng)頁內(nèi)容的高度。
總結(jié)
至此大家以后就知道怎么獲取WebView內(nèi)容高度了
問題二嚎杨,如果監(jiān)聽網(wǎng)頁高度變化
這是第一次打開APP的時(shí)候,獲取到網(wǎng)頁高度刨肃,在重新設(shè)置webView的高度,和RecyclerView的高度
代碼如下
private class Mobile {
@JavascriptInterface
public void onGetWebContentHeight() {
//重新調(diào)整webview高度
webView.post(() -> {
webView.measure(0, 0);
int measuredHeight = webView.getMeasuredHeight();
ViewGroup.LayoutParams layoutParams = webView.getLayoutParams();
layoutParams.height = measuredHeight;
webView.setLayoutParams(layoutParams);
ViewGroup.LayoutParams layoutParams1 = recyclerView.getLayoutParams();
layoutParams1.height = scrollView.getHeight();
Log.i("zzz", "scrollView height=" + scrollView.getHeight());
recyclerView.setLayoutParams(layoutParams1);
});
}
}
private WebViewClient mClient = new WebViewClient() {
@Override
public void onPageFinished(WebView view, String url) {
mobile.onGetWebContentHeight();
}
};
效果如下:可以看到網(wǎng)頁全部顯示了真友,這已經(jīng)滑動(dòng)到網(wǎng)頁的最底部紧帕。
但問題是點(diǎn)擊閱讀更多,網(wǎng)頁中有幾個(gè)隱藏的新聞被顯示出來愈案,造成網(wǎng)頁內(nèi)容高度變化,但是我們檢測不到網(wǎng)頁內(nèi)容高度變化刻帚,所以變成這樣了。
下面是我的解決思路崇众。
思路1.通過js監(jiān)聽內(nèi)容變化
我通過設(shè)置window的resize監(jiān)聽來實(shí)現(xiàn)高度變化的監(jiān)聽。下面使用瀏覽器來測試:
結(jié)果點(diǎn)擊閱讀更多顷歌,沒有觸發(fā)相關(guān)的函數(shù)。只有調(diào)整window大學(xué)的時(shí)候才會(huì)
思路2.在WebView的onDraw方法中監(jiān)聽
經(jīng)過我的實(shí)驗(yàn)在點(diǎn)擊閱讀更多以后webView的contentHeight會(huì)發(fā)生變化芹扭,于是我就擴(kuò)展了WebView使其能監(jiān)聽內(nèi)容高度變化
這是我的自定義WebView:
package com.trs.studyview.view;
import android.content.Context;
import android.graphics.Canvas;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.webkit.WebView;
/**
* Created by zhuguohui on 2017/8/8.
*/
public class TRSWebView extends WebView {
private int lastContentHeight = 0;
private static final int MSG_CONTENT_CHANGE = 1;
private onContentChangeListener onContentChangeListener = null;
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_CONTENT_CHANGE:
if (onContentChangeListener != null) {
onContentChangeListener.onContentChange();
}
break;
}
}
};
public TRSWebView(Context context) {
this(context, null);
}
public TRSWebView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (getContentHeight() != lastContentHeight) {
handler.sendEmptyMessage(MSG_CONTENT_CHANGE);
lastContentHeight = getContentHeight();
Log.i("www", "contentChange height=" + getContentHeight());
}
}
public void setOnContentChangeListener(TRSWebView.onContentChangeListener onContentChangeListener) {
this.onContentChangeListener = onContentChangeListener;
}
/**
* 監(jiān)聽內(nèi)容高度發(fā)生變化
*/
public interface onContentChangeListener {
void onContentChange();
}
}
點(diǎn)擊多次閱讀更多
結(jié)論:可以監(jiān)聽網(wǎng)頁內(nèi)容的變化
問題三 recyclerView的滑動(dòng)沖突舱卡。
我是通過自定義ScrollView來實(shí)現(xiàn)的队萤,具體代碼如下,測試可用
package com.trs.studyview.view;
import android.content.Context;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.ScrollView;
/**
* Created by zhuguohui on 2017/8/8.
*/
public class TRSScrollView extends ScrollView {
RecyclerView recyclerView;
public TRSScrollView(Context context) {
this(context, null);
}
public TRSScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setRecyclerView(RecyclerView recyclerView) {
this.recyclerView = recyclerView;
}
int downY;
int moveY = 0;
MotionEvent downEvent;
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
//如果RecyclerView
if (recyclerView != null) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
downEvent = ev;
downY = (int) ev.getX();
moveY = 0;
break;
case MotionEvent.ACTION_MOVE:
int moveY = (int) ev.getY() - downY;
if (Math.abs(moveY) > 20) {
//表示滑動(dòng)
if (moveY > 0) {
//向下滑動(dòng),如果recyclerView還沒有顯示則攔截事件
if (getScrollY() < recyclerView.getTop()) {
return true;
}
} else {
//向上滑動(dòng)
if (getScrollY() == recyclerView.getTop()) {
//如果第一個(gè)item已經(jīng)顯現(xiàn)才攔截事件
LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
if (layoutManager.findFirstVisibleItemPosition() == 0) {
return true;
}
}
}
}
break;
}
}
return super.onInterceptTouchEvent(ev);
}
}
最終運(yùn)行代碼
package com.trs.studyview;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.JavascriptInterface;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import com.trs.studyview.adpater.CommentAdapter;
import com.trs.studyview.view.TRSScrollView;
import com.trs.studyview.view.TRSWebView;
public class MainActivity extends AppCompatActivity {
TRSWebView webView;
RecyclerView recyclerView;
String url = "http://app.nxnews.net/ningxia/cfwz/zt/201708/t20170807_1859270.html";
CommentAdapter adapter;
TRSScrollView scrollView;
Mobile mobile = new Mobile();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
scrollView = $(R.id.scrollView);
webView = $(R.id.webview);
recyclerView = $(R.id.recyclerView);
WebSettings setting = webView.getSettings();
setting.setJavaScriptEnabled(true);//支持js
webView.addJavascriptInterface(mobile, "mobile");
// webView.setWebViewClient(mClient);
webView.setOnContentChangeListener(() -> {
mobile.onGetWebContentHeight();
});
webView.loadUrl(url);
adapter = new CommentAdapter();
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
scrollView.setRecyclerView(recyclerView);
}
private class Mobile {
@JavascriptInterface
public void onGetWebContentHeight() {
//重新調(diào)整webview高度
webView.post(() -> {
webView.measure(0, 0);
int measuredHeight = webView.getMeasuredHeight();
ViewGroup.LayoutParams layoutParams = webView.getLayoutParams();
layoutParams.height = measuredHeight;
webView.setLayoutParams(layoutParams);
ViewGroup.LayoutParams layoutParams1 = recyclerView.getLayoutParams();
layoutParams1.height = scrollView.getHeight();
Log.i("zzz", "scrollView height=" + scrollView.getHeight());
recyclerView.setLayoutParams(layoutParams1);
});
}
}
private WebViewClient mClient = new WebViewClient() {
@Override
public void onPageFinished(WebView view, String url) {
}
};
protected <T extends View> T $(int id) {
return (T) findViewById(id);
}
}
總結(jié)
這個(gè)需求難度在于知識(shí)的廣度,例如對JavaScript的熟悉程度既绩,對View事件攔截的理解还惠。研究的過程很有趣,克服重重困難吸重,完成任務(wù)的感覺很不錯(cuò)的。