Search code examples
androidwebviewandroid-coordinatorlayoutandroid-nestedscrollview

Webview content inside Nestedscrollview not scrolling smoothly


I have got a web view inside a NestedScrollView due to some animation and transition that I want in my layout. Now the thing is the vertical scroll is working perfectly fine (which I guess is due to the scroll of nested scroll view and not the web view), but the horizontal scroll is smooth. Horizontal scroll as in the horizontal scroll of the content inside the webview (say a carousel inside a webview).

Here's my layout:-

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/frameLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/bg">

    <androidx.coordinatorlayout.widget.CoordinatorLayout
        android:id="@+id/coordinator"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintTop_toTopOf="parent">

        <com.google.android.material.appbar.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent">

            <androidx.appcompat.widget.SearchView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_margin="8dp"
                android:background="@color/white"
                app:layout_scrollFlags="scroll|enterAlways" />
        </com.google.android.material.appbar.AppBarLayout>

        <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
            android:id="@+id/activity_main_swipe_refresh_layout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_behavior="@string/appbar_scrolling_view_behavior">

            <androidx.core.widget.NestedScrollView
                android:id="@+id/nested_scroll_view"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior" >

               <WebView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    app:layout_behavior="@string/appbar_scrolling_view_behavior" />
            </androidx.core.widget.NestedScrollView>
        </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
    </androidx.coordinatorlayout.widget.CoordinatorLayout>

    <fr.castorflex.android.smoothprogressbar.SmoothProgressBar
        android:id="@+id/progress_bar"
        android:layout_width="match_parent"
        android:layout_height="3.5dp"
        android:indeterminate="true"
        app:layout_constraintTop_toTopOf="parent"
        app:spb_color="#FB9043"
        app:spb_mirror_mode="false"
        app:spb_reversed="false"
        android:layout_marginTop="60dp"
        app:spb_sections_count="5"
        app:spb_speed="1.0"
        app:spb_stroke_separator_length="4.0dp"
        app:spb_stroke_width="4.0dp" />
</androidx.constraintlayout.widget.ConstraintLayout>

Can anyone help me with this?


Solution

  • I was able to get the answer with the help of the solution at this link https://stackoverflow.com/a/34310846/6840443

    So what he did was that he basically made a custom NestedScrollView, and overrode the onInterceptTouchEvent method and handled the touch action as to how it should be consumed, so while the vertical scroll was still handled by the NestedScrollView, the horizontal scroll was not handled by it and subsequently, it propagated to the child of view which happened to be the webView which already handles this kind of scroll.

    Here's the code for it:-

    package com.smartprix.main
    
    import android.content.Context
    import android.util.AttributeSet
    import android.view.MotionEvent
    import android.view.ViewConfiguration
    import androidx.core.widget.NestedScrollView
    
    
    class SmartNestedScrollView : NestedScrollView {
        private var slop: Int = 0
        private val mInitialMotionX: Float = 0.toFloat()
        private val mInitialMotionY: Float = 0.toFloat()
    
    
        private var xDistance: Float = 0.toFloat()
        private var yDistance: Float = 0.toFloat()
        private var lastX: Float = 0.toFloat()
        private var lastY: Float = 0.toFloat()
    
        constructor(context: Context) : super(context) {
            init(context)
        }
    
        private fun init(context: Context) {
            val config = ViewConfiguration.get(context)
            slop = config.scaledEdgeSlop
        }
    
        constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
            init(context)
        }
    
        constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
            init(context)
        }
    
        override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
            val x = ev.x
            val y = ev.y
            when (ev.action) {
                MotionEvent.ACTION_DOWN -> {
                    yDistance = 0f
                    xDistance = yDistance
                    lastX = ev.x
                    lastY = ev.y
    
                    // This is very important line that fixes
                    computeScroll()
                }
                MotionEvent.ACTION_MOVE -> {
                    val curX = ev.x
                    val curY = ev.y
                    xDistance += Math.abs(curX - lastX)
                    yDistance += Math.abs(curY - lastY)
                    lastX = curX
                    lastY = curY
    
                    if (xDistance > yDistance) {
                        return false
                    }
                }
            }
            return super.onInterceptTouchEvent(ev)
        }
    }```