Android webview不会始终打开到最后滚动位置

时间:2014-12-05 19:34:55

标签: javascript android webview

我有一个Android应用程序,它使用KitKat级别的WebView来显示文档(主要是作为使用HTML文件的电子阅读器)。当用户在应用程序中打开文档(因此webview因此为该文档新实例化)时,我想滚动到文档中最后一个已知的光标位置,以便用户可以读取他们中断的位置。显然,当我第一次打开文档时,我保留并保持光标位置与webview无关,以允许这样的滚动。

问题: 当文档第一次打开时,我可以在我的javascript文档中看到最后一个已知位置(光标)的突出显示,但是在大概50%的情况下,我看到文档以某种方式滚动回到顶部,因此光标位置不再显示。在这些情况下它如何以及为什么回滚到顶部对我来说是一个谜(我不相信我以编程方式使其以任何方式滚动回到顶部)。

注意: 为了在webview中实现滚动,我正在使用一个javascript函数,一旦webview已经发出信号已经加载并准备好使用,它就会从我的webview片段(通过webview.loadUrl)调用。这是从文档视图片段到webview的回调:

	@TargetApi(Build.VERSION_CODES.KITKAT) @SuppressLint("ClickableViewAccessibility") @Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
	{		
		View mainView = inflater.inflate(R.layout.document_webview_reader, container, false);
		getReaderActivity().setFooterLocationBarValues(mainView);
		
		mWebviewLoading = true;
		final WebView webview = (WebView) mainView.findViewById(R.id.document_webReader_webView);
		webview.getSettings().setJavaScriptEnabled(true);
		webview.getSettings().setAllowFileAccessFromFileURLs(true); 
		webview.getSettings().setAllowUniversalAccessFromFileURLs(true);
		webview.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
		webview.setBackgroundColor(0);
		webview.setBackgroundColor(Color.LTGRAY);
		
		if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) 
		{
		    WebView.setWebContentsDebuggingEnabled(true);
		}
		
		mJsInterface = new JsCall();
		webview.addJavascriptInterface(mJsInterface, mJsInterface.getInterfaceName());
		
		webview.setWebViewClient(new WebViewClient()
		{			
			public void onPageFinished(WebView view, String url)
			{
				drawInitialCursor(getDocument().getCursorRange());						
			}
		});
		

	private void drawInitialCursor(final WordRange range)
	{
		if( getReaderActivity().activityIsPaused() ) return;
    	
		getActivity().runOnUiThread(new Runnable()
		{
			
			@Override
			public void run()
			{
				Log.d(ReaderApplication.VDREADER_TAG,"highlighting initial range");
				WordRange currentFragmentRange = mTextFragmentRanges.get(mCurrentTextFragmentIndex);
				int relativeLocation = range.getLocation() - currentFragmentRange.getStartRange();
				WordRange rangeInCurrentFragment = new WordRange(relativeLocation, range.getLength());
						
				WebView webview = (WebView) getActivity().findViewById(R.id.document_webReader_webView);
				
				String applyString = String.format("javascript:highlightInitialRange(%d,%d,%d,%d)", 
						rangeInCurrentFragment.getStartRange(),rangeInCurrentFragment.getEndRange(), 
						mCurrentTextFragmentIndex,
						mCursorPositionSetting.ordinal());

				webview.loadUrl(applyString);
			}
		});
	}

'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	myNodelist = document.querySelectorAll(VD_cssSelector);

        selRange.selectCharacters(myNodelist[elementIndex], start, end);
        wordHighlightApplier.applyToRange(selRange);

        var wordCursor = document.getElementsByTagName("vdword");
        wordCursor[0].scrollIntoView(true);

    }

上面的javascript基本上只是找到文档中的相关范围(作为元素索引和该元素中的偏移量的组合),对其应用突出显示(通过rangy),然后将突出显示的元素滚动到视图中。

正如我所说,我知道这个策略通常有效,因为我总能看到它在文档加载和webview准备好使用时突出显示正确的位置;但是在我执行此操作的约50%的时间内,我看到光标突然显示,然后当文档滚动回到顶部时它会消失(假设光标范围超出了文档的初始页面)。

**我会在这里发布正确光标突出显示的图像,但显然我没有足够的信誉来允许这样的微不足道。 **

问题: 我在这里遗漏了一些关于webview文档渲染的内容吗?我知道一切都在突出显示和突出显示的元素的初始滚动视图方面正常工作,并且我只在webview完全加载后执行此突出显示代码(通过webview客户端上的onPageFinished通知)。当文档完全加载以确保文档显示在其最顶部的滚动位置时,webview本身是否存在非确定性执行的内容?

***由所有者决定

事后看来,对这个问题的解决是相当明显的:我正在使用一个'包装'html文档,其中我加载了各种JS库 - JSQuery,Rangy等,并且在其中我使用JSQuery替换文档正文我想加载/读取'真正的'html文档。因此,从HTML文档的角度来看,加载分两个阶段进行:

  1. 加载包装器
  2. Wrapper通过$(“body”)加载真实的html文档.load(“”)
  3. 由于这个两阶段加载,我的应用程序看到Webview.onPageFinished事件被触发 - 但这仅表示包装器html的加载,而不是整个正文。所以我需要做的就是在JSQuery .load方法成功完成后,从我的webview / javascript中添加一个显式调用到应用程序:

    <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'执行以前在onPageFinished方法中执行的Android / java代码,但只有在JSQuery加载了真正的文档主体之后。那样的加载顺序 - &gt;光标绘图都以正确的顺序发生。解决。

1 个答案:

答案 0 :(得分:0)

如更新后的问题所述(添加此答案以便我可以解决此问题):

事后看来,这个问题的解决方案非常明显:我正在使用一个&#39;包装器&#39;我在其中加载了各种JS库的HTML文档 - JSQuery,Rangy等,在其中我使用JSQuery将文档正文替换为&#39; real&#39; html文档我想加载/读取。因此,从HTML文档的角度来看,加载分两个阶段进行:

加载包装器 包装器通过$(&#34; body&#34;)加载真实的html文档.load(&#34;&#34;) 由于这个两阶段加载,我的应用程序看到Webview.onPageFinished事件被触发 - 但这仅表示包装器html的加载,而不是完整的正文。因此,我需要做的就是在JSQuery .load方法成功完成后,将我的webview / javascript中的显式调用添加到应用程序中。