seemuch seemuch - 18 days ago 5
Android Question

How to make a view move as part of RecyclerView using CoordinatorLayout

I am learning Android CoordinatorLayout/RecyclerView/Behavior/Nested scroll stuff. I found the APIs are not very straight forward. Thus I did a little experiment but found the behaviors of views inside CoordinatorLayout do not come out as expected.

Here is what I want to do: I want to have a CoordinatoryLayout as the parent container. Inside it, there is a RecyclerView as its child. Also as its child, there is another View that is in red color. I want the red view to move as the RecyclerView scrolls. I want the movement of the two views so synchronized that it seems that the red view is part of the RecyclerView.

Note: I am aware that to achieve what I just said, there is much more simpler ways. But I just want to do it with CoordinatorLayout and Behavior class so that I can learn how they work.

Here is my layout file:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context="io.github.seemuch.coordinatorlayoutexperiment.MainActivity">

<android.support.v7.widget.RecyclerView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="40dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:dividerHeight="10dp"
/>

<View
android:id="@+id/red_view"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_marginBottom="20dp"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:background="#ff0000"
app:layout_scrollFlags="scroll|enterAlways"
/>

</android.support.design.widget.CoordinatorLayout>


And here is my behavior class:

package io.github.seemuch.coordinatorlayoutexperiment;

import android.support.design.widget.CoordinatorLayout;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.RecyclerView;
import android.view.View;


public class MyBehavior extends CoordinatorLayout.Behavior<View> {
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, View toolbar, View dependency) {
return dependency instanceof RecyclerView;
}


@Override
public boolean onStartNestedScroll (CoordinatorLayout coordinatorLayout,
View child,
View directTargetChild,
View target,
int nestedScrollAxes) {

int vertical = (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL);
int horizontal = (nestedScrollAxes & ViewCompat.SCROLL_AXIS_HORIZONTAL);

return (vertical != 0 && horizontal == 0);
}

@Override
public void onNestedPreScroll (CoordinatorLayout coordinatorLayout,
View child,
View target,
int dx,
int dy,
int[] consumed) {

float currY = child.getY();
if (currY <= 0 && dy >= 0) {
return;
}
child.setY(currY - dy);
}

}


As you can see in the onNestedPreScroll method, I want the child, which is the red_view, move as much as the view it depends on, which is the RecyclerView.

What actually happened was this: if you scroll the RecyclerView with very big movement, the red view does move with it, as expected; but if you move very little, the red_view moves much faster than the RecyclerView.

Here is a link to the screen record video: https://youtu.be/zK4g61F2aa0
And here is the Github repo of the project, incase you want to download it and run it yourself: https://github.com/seemuch/CoordinatorLayoutExperiment

So, anybody knows what happened?
Thanks!

Answer

Found a solution: Do not override onNestedPreScroll, instead, override onNestedScroll(). Simple as that. It does not handle fling, though. Flings need to be handled separately.

Comments