我有一個Android應用程序,它使用KitKat級別的WebView來顯示文檔(主要是作爲一個電子閱讀器消耗HTML文件)。當用戶在應用程序中打開一個文檔(因此webview因此爲該文檔重新實例化)時,我想滾動到文檔中最後一個已知的光標位置,以便用戶可以從他們離開的位置繼續閱讀。很明顯,我保留並堅持web瀏覽器的光標位置,以便在我第一次打開文檔時進行滾動。Android的webview不會始終打開到最後滾動位置
問題: 當文檔第一次打開時,我可以看到最後已知的位置(光標)的初始突出我的JavaScript文件中,但隨後的情況下,也許50%是我見過的文件莫名其妙地向後翻頁到頂部,所以光標位置不再顯示。在這些情況下,如何以及爲什麼滾動回頂端對我來說是個謎(我不相信我會以任何方式讓它滾動回頂端)。
注意: 爲了實現webview中的滾動我使用了一個javascript函數,它從webview片段(通過webview.loadUrl)調用,一旦webview發出信號,它已加載並準備使用。這是從文檔視圖片段回調web視圖:
\t @TargetApi(Build.VERSION_CODES.KITKAT) @SuppressLint("ClickableViewAccessibility") @Override
\t public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
\t { \t \t
\t \t View mainView = inflater.inflate(R.layout.document_webview_reader, container, false);
\t \t getReaderActivity().setFooterLocationBarValues(mainView);
\t \t
\t \t mWebviewLoading = true;
\t \t final WebView webview = (WebView) mainView.findViewById(R.id.document_webReader_webView);
\t \t webview.getSettings().setJavaScriptEnabled(true);
\t \t webview.getSettings().setAllowFileAccessFromFileURLs(true);
\t \t webview.getSettings().setAllowUniversalAccessFromFileURLs(true);
\t \t webview.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
\t \t webview.setBackgroundColor(0);
\t \t webview.setBackgroundColor(Color.LTGRAY);
\t \t
\t \t if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
\t \t {
\t \t WebView.setWebContentsDebuggingEnabled(true);
\t \t }
\t \t
\t \t mJsInterface = new JsCall();
\t \t webview.addJavascriptInterface(mJsInterface, mJsInterface.getInterfaceName());
\t \t
\t \t webview.setWebViewClient(new WebViewClient()
\t \t { \t \t \t
\t \t \t public void onPageFinished(WebView view, String url)
\t \t \t {
\t \t \t \t drawInitialCursor(getDocument().getCursorRange()); \t \t \t \t \t \t
\t \t \t }
\t \t });
\t \t
\t private void drawInitialCursor(final WordRange range)
\t {
\t \t if(getReaderActivity().activityIsPaused()) return;
\t
\t \t getActivity().runOnUiThread(new Runnable()
\t \t {
\t \t \t
\t \t \t @Override
\t \t \t public void run()
\t \t \t {
\t \t \t \t Log.d(ReaderApplication.VDREADER_TAG,"highlighting initial range");
\t \t \t \t WordRange currentFragmentRange = mTextFragmentRanges.get(mCurrentTextFragmentIndex);
\t \t \t \t int relativeLocation = range.getLocation() - currentFragmentRange.getStartRange();
\t \t \t \t WordRange rangeInCurrentFragment = new WordRange(relativeLocation, range.getLength());
\t \t \t \t \t \t
\t \t \t \t WebView webview = (WebView) getActivity().findViewById(R.id.document_webReader_webView);
\t \t \t \t
\t \t \t \t String applyString = String.format("javascript:highlightInitialRange(%d,%d,%d,%d)",
\t \t \t \t \t \t rangeInCurrentFragment.getStartRange(),rangeInCurrentFragment.getEndRange(),
\t \t \t \t \t \t mCurrentTextFragmentIndex,
\t \t \t \t \t \t mCursorPositionSetting.ordinal());
\t \t \t \t webview.loadUrl(applyString);
\t \t \t }
\t \t });
\t }
關於 'highlightInitialRange' 的JavaScript是:
function highlightInitialRange(start, end, elementIndex, cursorPagePositionSetting)
{
if(wordHighlightApplier == null) {
wordHighlightApplier = rangy.createClassApplier("voice-dream-spoken-word",
{ elementTagName: "vdword"}
);
}
if(selRange == null) {
selRange = rangy.createRange();
}
wordHighlightApplier.undoToRange(selRange);
var \t myNodelist = document.querySelectorAll(VD_cssSelector);
selRange.selectCharacters(myNodelist[elementIndex], start, end);
wordHighlightApplier.applyToRange(selRange);
var wordCursor = document.getElementsByTagName("vdword");
wordCursor[0].scrollIntoView(true);
}
以上的JavaScript基本上只是找到文件中的相關範圍(如a元素索引和該元素內的偏移量的組合),向其應用突出顯示(通過rangy),然後將突出顯示的元素滾動到視圖中。
正如我所說,我知道這種策略通常工作,因爲我總是可以看到它突出顯示正確的位置,當文檔加載和Web視圖準備使用;但在大約50%的時間裏,我運行這個程序時,我看到光標短暫高亮顯示,然後隨着文檔滾動回頂端(假設光標範圍超出了文檔的初始頁面),它會消失。
**我會在這裏發佈正確光標高亮的圖像,但顯然我沒有足夠的信譽來允許這樣的瑣事。 **
問題: 我在這裏錯過了關於webview文檔渲染的內容嗎?我知道一切工作正確,就突出顯示和突出顯示的元素的初始滾動視圖而言,並且我只在webview完全加載後(通過webview客戶端上的onPageFinished通知)執行此突出顯示代碼。 webview本身是否存在一些當文檔完全加載時非確定性執行的內容,以確保文檔顯示在其最上方的滾動位置?
***議決由業主
在事後解決這個問題是相當明顯的:我用我在其中加載各種JS庫一「包裝」 html文件 - JSQuery,四肢瘦長等,以及內我正在使用JSQuery將文檔正文替換爲我要加載/讀取的「真實」html文檔。通過$(「身體」)
- 裝入包裝
- 包裝加載實HTML文檔加載(「」)
由於這種兩階段加載,我的應用程序正在看到Webview.onPageFinished事件被觸發 - 但這只是表示加載包裝器html,而不是完整的正文。因此,所有我需要做的是從我的web視圖/ javascript來的應用程序添加的顯式調用一旦JSQuery .load方法已成功地完成:
<body>
<script>
$("body").load("%s", function(response, status, xhr)
{
if (status == "error")
{
var msg = "Sorry but there was an error: ";
$("#error").html(msg + xhr.status + " " + xhr.statusText);
}
else
{
// append our line highlight div, floating
var newDiv = $("<div id='vd-spoken-line' class='voice-dream-spoken-line'/>");
$("body").append(newDiv);
notifyDocumentBodyLoaded(); <-- new callback to android app here
}
});
</script>
</body>
所以後來「notifyDocumentBodyLoaded」執行了Android /以前在onPageFinished方法中執行的java代碼,但只有在JSQuery加載了真正的文檔體之後。這樣的加載順序 - >光標繪製都按正確的順序進行。解決了。
解決了我自己的問題 - 查看更新的問題文本。 – Scotty 2014-12-06 13:33:57
請考慮發佈一個實際的答案,爲社區着想,並解決它。 – Ercan 2015-01-21 09:20:29