2014-12-05 49 views
1

我有一個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文檔。通過$(「身體」)

  1. 裝入包裝
  2. 包裝加載實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加載了真正的文檔體之後。這樣的加載順序 - >光標繪製都按正確的順序進行。解決了。

+0

解決了我自己的問題 - 查看更新的問題文本。 – Scotty 2014-12-06 13:33:57

+0

請考慮發佈一個實際的答案,爲社區着想,並解決它。 – Ercan 2015-01-21 09:20:29

回答

0

如更新問題(加入這個答案,所以我可以解決這個)提到:

在事後解決這個問題是相當明顯的:我用我在其中加載各種JS一個「包裝」的html文件庫 - JSQuery,Rangy等,並在其中我使用JSQuery來替換文檔正文與我想要加載/閱讀的'真正'的HTML文檔。因此,從HTML文檔的角度來看,加載發生在兩個階段:

加載包裝 包裝加載真正的HTML文檔通過$(「body」)。load(「」) 由於這兩個階段加載,我的應用程序看到Webview.onPageFinished事件被觸發 - 但這只是表明加載的包裝HTML,而不是全身。所以我需要做的就是在JSQuery .load方法成功完成後,從我的webview/javascript嚮應用添加一個明確的調用。