8

My Android app uses a WebView to display a bunch of HTML code that I generate 'on the fly'. The HTML code is loaded using the following code:

 StringBuilder builder = new StringBuilder(); // HTML builder.append("<html><head><link rel=\"stylesheet\" href=\"file:///android_asset/style.css\" type=\"text/css\">"); builder.append("</link></head><body>"); builder.append(getThreadBody()); builder.append("</body></html>"); webview.loadDataWithBaseURL("file:///android_asset/", builder.toString(), "text/html", "utf-8", null); 

This all works really nice. Note that I'm not loading an actual HTML file, I'm merely creating a string that represents some (hopefully valid) HTML and load it in the WebView.

Anyway, the HTML I generate (the part in the 'getThreadBody' method) contains named anchors, for example like this;

<div> <a name="949823">Anchor 1</a> <div>All kinds of stuff</div> <a name="895984">Anchor 2</a> <div>...</div> </div> 

Now in some cases I want the WebView to navigate to one of those anchors as soon as I load the HTML. As far as I understand the WebView supports navigating (scrolling in this case) to named anchors, but the catch is that nobody is clicking any hyperlinks to those anchors, I want the WebView to scroll when it is loaded (it is no problem if there is a short delay between loading the HTML and scrolling, my point is that it should not require user interaction).

I believe the behavior can be achieved by using the WebView's loadUrl method and supplying a URL with the anchor in place, eg

webview.loadUrl("file:///android_asset/page.html#895984", ...) 

However, since I am not saving the HTML to any file I cannot use this method... Of course saving the HTML to a temporary file may be a solution but I'd like to keep that as a last resort, there must be a simpler way?

How can I achieve this? Thanks!


Resolved Here's the code that works. I found a short delay was required to let the page load before executing the javascript otherwise it was kind of 50/50 whether it worked or not...

 StringBuilder builder = new StringBuilder(); // HTML builder.append("<html><head><link rel=\"stylesheet\" href=\"file:///android_asset/style.css\" type=\"text/css\">"); builder.append("</link>"); builder.append("<script>"); builder.append("function scrollAnchor(id) {"); builder.append("window.location.hash = id;}"); builder.append("</script>"); builder.append("</head><body>"); builder.append(getThreadBody()); builder.append("</body></html>"); webContents.loadDataWithBaseURL("file:///android_asset/", builder.toString(), "text/html", "utf-8", null); Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { String id = "895884"; webContents.loadUrl("javascript:scrollAnchor(" + id + ");"); } } }, 250); 

5 Answers 5

6

How about control it by JavaScript? I didn't try it but maybe this is a clue.

builder.append(getThreadBody()); builder.append("<script>window.location.hash="949823";</script>"); builder.append("</body></html>"); 

Remember enable javascript for WebView.

----Additional Answer----

I saw that you use TimerTask to load the javascript, That works but I think there is another better way. WebView have a callback named onPageFinished and it will be trigger when WebView finish loading webpage. You could inject your JS there.

webContents.setWebViewClient(new WebViewClient(){ @Override public void onPageFinished(WebView view, String url) { String id = "895884"; webContents.loadUrl("javascript:scrollAnchor(" + id + ");"); } }); 

Hope this is useful!

Sign up to request clarification or add additional context in comments.

3 Comments

Just one notice, when using this the url of the webview does not change. webview.getUrl() returns the same url that was used to load the contents.
You can also use javascript to handle the haschange by using the hashchange event: $j(window).bind('hashchange', function(e) { do your stuff here }
@wayne_bai I used your answer and it works but it jumps back to the top. Any idea what might be wrong?
6

First of all, you don't need to use javascript. If you loaded content with

webContents.loadDataWithBaseURL("some://dummy/url",...); 

you can simply scroll to anchor with

webContents.loadUrl("some://dummy/url#anchor"); 

Secondly, doing that on onPageFinished without additional control results in never ending loop! When loadUrl finishes onPageFinished get's called again and again.

3 Comments

this is not working for me... when i run the "webContents.loadUrl("some_dummy_url#anchor");" i get a blank page... any ideas?
I did: .loadDataWithBaseURL("app:html", ...);. As he says, just some dummy URL.
this is the winner because it avoids excess JavaScript, yet it also does not center the tag; it tries to put it at the top. scrollTo() plus math to center the element also didn't work inside a WebView
1

If you have external action (eg: onclick event), try this code

 public static void scrollToAnchor(String id) { sWebView.loadUrl("javascript:document.getElementById(\"" + id + "\").scrollIntoView()"); } 

Comments

1

If you have in your layout the webview inside a nestedScrollView the nesting will prevent the scrolling events from working properly. Once you take it out it will work without any need to reload or add javascript.

Additionally if you want to keep the nestedScrollView and dont care if the user cannot scroll past that section you can add fillViewPort=true to the nestedScrollView and android:layout_height="wrap_content" to the webview.

It would look something like:

<androidx.coordinatorlayout.widget.CoordinatorLayout> <com.google.android.material.appbar.AppBarLayout> ... </com.google.android.material.appbar.AppBarLayout> <androidx.core.widget.NestedScrollView android:layout_width="match_parent" android:layout_height="match_parent" android:fillViewPort=true app:layout_behavior="@string/appbar_scrolling_view_behavior"> <WebView android:layout_width="match_parent" android:layout_height="wrap_content"/> </androidx.core.widget.NestedScrollView> </androidx.coordinatorlayout.widget.CoordinatorLayout> 

Comments

0

if you load the html from the assets:

 webView.loadUrl("file:///android_asset/htmls/mypage.html#anchor"); 

and you can move to the anchor inside a web page:

webView.loadUrl("http://www.stackoverflow.com/mypage.html#anchor"); 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.