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
}
}