Home > Back-end >  NestedScrollView: Start scroll after offset
NestedScrollView: Start scroll after offset

Time:10-05

I would like ti achieve this: In my xml I have an ImageView on top and a Nested scrollView below. when user scroll Up or down I would like to transale up the nested scrollview until 50% Height og the imageView and then start to scroll the view inside the NestedScrollview. At moment I can move both the view but I don't know ho to start to move the scroll inside NestesdScrollView after we arrived at 50% of the imageView. here the code inside my fragment:

binding.apply {

        scrollView.viewTreeObserver.addOnScrollChangedListener {
            
            var maxDistance = advContainer.height
            val movement = scrollView.scrollY

            if (movement in 0..maxDistance) {
                scrollView.translationY = (-movement / 2).toFloat()
            }
            if(movement >= maxDistance) {
                Timber.d("max distance")
            }
        }
    }

CodePudding user response:

I suggest using CoordinatorLayout as your root layout since it allows you to create and plug in a custom Behavior that can participate in nested scrolling events. Using scroll listener will never let you do it cleanly.

Example layout - notice app:layout_behavior is on linear layout since it can only be present on direct children of coordinator:

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="com.sample.CollapseBehavior">

        <ImageView
            android:id="@ id/logo"
            android:src="@mipmap/ic_launcher_round"
            android:layout_margin="24dp"
            android:layout_width="48dp"
            android:layout_height="48dp"
            android:layout_gravity="center_horizontal"/>

        <androidx.core.widget.NestedScrollView
            android:id="@ id/scroll"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="500dp"
                android:paddingTop="400dp"
                android:paddingBottom="400dp"
                android:background="@color/colorPrimary"
                android:text="scrolling content"
                android:gravity="center"
                />
        </androidx.core.widget.NestedScrollView>
    </LinearLayout>

</androidx.coordinatorlayout.widget.CoordinatorLayout>

And custom behavior:

class CollapseBehavior : CoordinatorLayout.Behavior<View>{
    constructor() : super()
    constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)

    var currentOffset = 0
    var maxOffset = 0
    lateinit var topView : View

    // have to accept nested scrolling event to participate in it
    override fun onStartNestedScroll(
        coordinatorLayout: CoordinatorLayout, child: View, directTargetChild: View,
        target: View, axes: Int, type: Int
    ): Boolean {
        topView = child.findViewById(R.id.logo)
        maxOffset = topView.height / 2     // tweak maxOffset if needed
        return axes == ViewCompat.SCROLL_AXIS_VERTICAL && target is NestedScrollView
    }

    // this allows us to steal the scrolling distance
    // use it to offset NestedScrollView before it gets to scroll
    override fun onNestedPreScroll(
        coordinatorLayout: CoordinatorLayout, child: View, target: View,
        dx: Int, dy: Int, consumed: IntArray, type: Int
    ) {
        if(dy > 0) performOffset(target, dy, consumed)
    }

    // this allows us to consume remaining scrolling distance
    // use it to offset NestedScrollView after it gets scrolled back to start
    override fun onNestedScroll(
        coordinatorLayout: CoordinatorLayout, child: View, target: View,
        dxConsumed: Int, dyConsumed: Int,
        dxUnconsumed: Int, dyUnconsumed: Int,
        type: Int, consumed: IntArray
    ) {
        if(dyUnconsumed < 0) performOffset(target, dyUnconsumed, consumed)
    }


    private fun performOffset(target: View, dy: Int, consumed: IntArray){
        val targetOffset = currentOffset   dy
        val consumedScroll = targetOffset.coerceIn(0, maxOffset) - currentOffset
        currentOffset  = consumedScroll
        target.translationY = -currentOffset.toFloat()
        consumed[1] = consumedScroll
    }
}
  • Related