なぜあなたは受け入れ答えの上にこの答えを考慮する必要があります。
受け入れ答えはまともな提供し、スクロール位置を保存するシンプル方法は、しかし、それははるかに完璧からです。このアプローチの問題点は、回転中に画面上で見た要素が回転していないことがあります。画面の上部にあった要素は、回転後に下部に表示されるようになりました。スクロールの割合で位置を保存することはあまり正確ではなく、大きな文書ではこの不正確さが加わることがあります。
これは別の方法です:より複雑な方法ですが、回転前に見た回転の後で同じ要素を正確に見ることがほぼ保証されています。私の意見では、これは特に大きな文書で、はるかに優れたユーザーエクスペリエンスにつながります。すべての
======
まず、我々はjavascriptを介して、現在のスクロール位置を追跡します。これにより、現在どの要素が画面の最上部にあるか、どれだけスクロールされているかを正確に知ることができます。
まず、あなたのWebViewのために有効になっているのJavaScriptのことを確認します。
webView.getSettings().setJavaScriptEnabled(true);
次に、我々はjavascriptの内部からの情報を受け入れるJavaクラスを作成する必要があります。
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;
}
}
その後、我々は、このクラスを追加to WebView:
scrollListener = new WebScrollListener(); // save this in an instance variable
webView.addJavascriptInterface(scrollListener, "WebScrollListener");
これで、htmlページにjavascriptコードを挿入する必要があります。このスクリプトは、(あなたが生成するHTMLであれば、ちょうどこのスクリプトを追加し、そうでない場合、あなたはwebView.loadUrl("javascript:document.write(" + script + ")");
経由document.write()
を呼び出すに頼る必要がある場合があります)Javaへスクロールデータを送信します:
<script>
// 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);
});
</script>
あなたは、この時点でアプリケーションを実行する場合、新しいスクロール位置が受信されたことを知らせるメッセージがすでにlogcatに表示されているはずです。
今、私たちは、WebViewの状態を保存する必要があります。我々はまた、追加する必要が
if (savedInstanceState != null) {
webView.restoreState(savedInstanceState);
initialScrollElement = savedInstanceState.getString("scrollElement");
initialScrollMargin = savedInstanceState.getInt("scrollMargin");
}
:
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
webView.saveState(outState);
outState.putString("scrollElement", scrollListener.element);
outState.putInt("scrollMargin", scrollListener.margin);
}
はその後、我々はのonCreate(活動)またはonCreateView(フラグメントのための)方法でそれを読みます私たちのWebViewにWebViewClient
と上書きonPageFinished
方法:
@Override
public void onPageFinished(final 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
webView.postDelayed(new Runnable() {
@Override
public void run() {
String javascript = "(function (selectorToRestore, positionToRestore) {\n" +
" var previousTop = 0;\n" +
" var check = function() {\n" +
" var elem = document.querySelector(selectorToRestore);\n" +
" if (!elem) {\n" +
" setTimeout(check, 100);\n" +
" return;\n" +
" }\n" +
" var currentTop = elem.getBoundingClientRect().top;\n" +
" if (currentTop !== previousTop) {\n" +
" previousTop = currentTop;\n" +
" setTimeout(check, 100);\n" +
" } else {\n" +
" window.scrollBy(0, currentTop - positionToRestore);\n" +
" }\n" +
" };\n" +
" check();\n" +
"}('" + initialScrollElement + "', " + initialScrollMargin + "));";
webView.loadUrl("javascript:" + javascript);
initialScrollElement = null;
}
}, 300);
}
}
これはそれです。画面を回転させた後、画面上部にあった要素がそのまま残ります。
@ ol_v-er情報をありがとう。 'scrollTo'が動作するのを見るためにあなたが記述しているように遅延を導入しようと考えています(遅れが必要ですが、とにかく...)。ついに、javascriptのソリューション(私はまもなく掲載予定)を介して動作するようにスクロールしていますが、結果を見るためにまだ試みていないパーセンテージスクロールの考えがありました。 – PJL
実際にonPageFinishedコールバックが呼び出されると、WebViewは単に描画を開始します。遅延は、WebViewが描画を完了していることを確認することです(正確な高さを持つ)。あなたのjavascriptソリューションを楽しみにしています。 –
ありがとう、私のjavascriptソリューションを追加しました。 – PJL