3

I have a NestedScrollView with a WebView inside. The WebView contains an HTML file with anchors, that are linked to the same file but difference places (imagine "menu" and "content" containers. when you click on a menu item the corresponding section in "content" should appear on the screen).

<android.support.v4.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:id="@+id/svNested"
    android:layout_height="match_parent"
    tools:showIn="@layout/activity_single"
    android:background="@color/colorPrimary">

        <WebView
            android:id="@+id/webView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>

</android.support.v4.widget.NestedScrollView>

Loading data:

webView?.loadDataWithBaseURL(null, htmlContent, "text/html", "UTF-8", null)

The problem is that these anchors doesn't work as expected.

P. Savrov
  • 1,064
  • 4
  • 17
  • 29

1 Answers1

1

I got anchors to work by calculating their position with Javascript inside the WebView and then programmatically scrolling the NestedScrollView by calling a custom URL and catching it with the WebViewClient. Here are some snippets (I only scroll vertically, but you can easily extend it to also work horizontally):

In your Activity or Fragment (where the NestedScrollView and WebView are referenced):

webView.webViewClient = object : WebViewClient() {
        override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {
            if(url.startsWith("scrollto")) {
                val offset = url.replace("scrollto://", "");
                val offsetInt = MapUtils.getDensityIndependentPixels(offset.toFloat(), requireContext()).toInt()
                (webView.parent as NestedScrollView).scrollTo(0, offsetInt)
               return true
            }
            return false
        }
    }

//We need this, because the measured pixels in the WebView don't use density of the screen
fun getDensityIndependentPixels(pixels: Float, context: Context): Float {
    return TypedValue.applyDimension(
        TypedValue.COMPLEX_UNIT_DIP,
        pixels,
        context.resources.displayMetrics
    )
}

Then in your Javascript (or <script>-Tag in the HTML):

function getOffset(el) {
    const rect = el.getBoundingClientRect();
    return {
        top: rect.top + window.pageYOffset,
        left: rect.left + window.pageXOffset
    };
}
                    
function makeItScroll(id) {
     var element = document.getElementById(id)
     var offsetTop = getOffset(element).top
     window.location = "scrollto://"+offsetTop
}

And finally use it inside your HTML like this:

div you want to scroll to: <div id="here">...</div>

a to scroll there: <a href="javascript:makeItScroll('here');">...</a>

Robert
  • 353
  • 3
  • 12