【发布时间】:2021-12-14 09:05:52
【问题描述】:
我已经为我的应用程序创建了一个用 html 编写的用户手册。它有一个内容页面,其中包含指向其他部分的链接。我将 html 文件加载到 WebView 中,一切正常。我想要做的是当用户从纵向切换到横向时,保持与以前相同的滚动位置,反之亦然,从横向切换回纵向。我用 onScrollPositionChange 方法创建了一个类 WebScrollListener。这作为 JavascriptInterface 添加到 WebView,因此 Javascript 设置方法中变量的值。我第一次从纵向到横向运行良好,但是一旦进入横向,WebScrollListener 就会停止监听滚动事件。这是我的 Fragment 的代码。
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
int fragmentId = scenarioViewModel.getCurrentFragmentId();
// Inflate the layout for this fragment
View root = inflater.inflate(R.layout.fragment_user_manual, container, false);
if (savedInstanceState != null) {
// orientation changed has occurred
initialScrollElement = savedInstanceState.getString("scrollElement");
initialScrollMargin = savedInstanceState.getInt("scrollMargin");
}
webview = root.findViewById(R.id.help_text);
hashTag = getHashTag(fragmentId);
webview.getSettings().setJavaScriptEnabled(true);
WebViewClient webViewClient = new MyWebViewClient();
webview.setWebViewClient(webViewClient);
scrollListener = new WebScrollListener();
webview.addJavascriptInterface(scrollListener, "WebScrollListener");
webview.loadUrl("file:///android_asset/app-user-manual.html");
return root;
}
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("scrollElement", scrollListener.element);
outState.putInt("scrollMargin", scrollListener.margin);
}
private class MyWebViewClient extends WebViewClient {
public void onPageFinished(WebView view, String url) {
if (initialScrollElement != null) {
// It's very hard to detect when web page actually finished loading;
// At the time onPageFinished is called, page might still not be parsed
// Any javascript inside <script>...</script> tags might still not be executed;
// Dom tree might still be incomplete;
// So we are gonna use a combination of delays and checks to ensure
// that scroll position is only restored after page has actually finished loading
view.postDelayed(new Runnable() {
@Override
public void run() {
StringBuilder buf=new StringBuilder("javascript:scrollToPosition('"+initialScrollElement+"','"+initialScrollMargin+"')");
Log.d("MyWebViewClient", "returned string:"+buf.toString());
webview.loadUrl(buf.toString());
initialScrollElement = null;
}
}, 300);
} else {
if (url.contains("#") != true) {
// We only go into this method if the user has clicked on the help button
// and we want to go to a specific hashtag. The page gets reloaded at that hashtag.
if (!hashTag.isEmpty()) {
String myurl = url + hashTag;
// this gets loaded again so as to get the position of the hashtag
webview.loadUrl(myurl);
}
}
super.onPageFinished(view, url);
}
}
}
public class WebScrollListener {
private String element;
private int margin;
@JavascriptInterface
public void onScrollPositionChange(String topElementCssSelector, int topElementTopMargin) {
Log.d("WebScrollListener", "Scroll position changed: " + topElementCssSelector + " " + topElementTopMargin);
element = topElementCssSelector;
margin = topElementTopMargin;
}
}
javascript 包含在 html 文件中,如下所示:
<script type="text/javascript" src="scroll-position.js"></script>
这里是javascript代码:
// We will find first visible element on the screen
// by probing document with the document.elementFromPoint function;
// we need to make sure that we dont just return
// body element or any element that is very large;
// best case scenario is if we get any element that
// doesn't contain other elements, but any small element is good enough;
var findSmallElementOnScreen = function() {
var SIZE_LIMIT = 1024;
var elem = undefined;
var offsetY = 0;
while (!elem) {
var e = document.elementFromPoint(100, offsetY);
if (e.getBoundingClientRect().height < SIZE_LIMIT) {
elem = e;
} else {
offsetY += 50;
}
}
return elem;
};
// Convert dom element to css selector for later use
var getCssSelector = function(el) {
if (!(el instanceof Element))
return;
var path = [];
while (el.nodeType === Node.ELEMENT_NODE) {
var selector = el.nodeName.toLowerCase();
if (el.id) {
selector += '#' + el.id;
path.unshift(selector);
break;
} else {
var sib = el, nth = 1;
while (sib = sib.previousElementSibling) {
if (sib.nodeName.toLowerCase() == selector)
nth++;
}
if (nth != 1)
selector += ':nth-of-type('+nth+')';
}
path.unshift(selector);
el = el.parentNode;
}
return path.join(' > ');
};
// Send topmost element and its top offset to java
var reportScrollPosition = function() {
var elem = findSmallElementOnScreen();
if (elem) {
var selector = getCssSelector(elem);
var offset = elem.getBoundingClientRect().top;
WebScrollListener.onScrollPositionChange(selector, offset);
}
}
// We will report scroll position every time when scroll position changes,
// but timer will ensure that this doesn't happen more often than needed
// (scroll event fires way too rapidly)
var previousTimeout = undefined;
window.addEventListener('scroll', function() {
clearTimeout(previousTimeout);
previousTimeout = setTimeout(reportScrollPosition, 200);
});
function scrollToPosition (selectorToRestore, positionToRestore) {
var previousTop = 0;
var check = function() {
var elem = document.querySelector(selectorToRestore);
if (!elem) {
setTimeout(check, 100);
return;
}
var currentTop = elem.getBoundingClientRect().top;
if (currentTop != previousTop) {
previousTop = currentTop;
setTimeout(check,100);
} else {
window.scrollBy(0, currentTop - positionToRestore);
}
}
check();
}
任何帮助将不胜感激。
【问题讨论】:
标签: javascript android html webview