Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Recyclerview with scrolling background

I'm trying to create a RecyclerView with scrolling background, like the one shown below. The idea is, as I scroll up/down the viewholders, the background (light-green) image should also move up/down in sync. Any clue as to how to accomplish this?

enter image description here

Here is my basic RecyclerView configuration

<android.support.v7.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="@dimen/item_margin"
android:clipToPadding="false"
android:background="@drawable/ic_light_green_image"/>
like image 768
stackoverflowN Avatar asked Apr 04 '15 17:04

stackoverflowN


2 Answers

I had the same use-case -- scrolling a list of cards that lies above the app bar along the Z axis, just like Google Play Music. It's actually pretty simple, but the documentation for RecyclerView#computeVerticalScrollOffset() is completely misleading. It doesn't calculate the scrollbar thumb's offset, instead it calculates by how much the RecyclerView itself has scrolled (which is exactly what we need here).

mPostList.addOnScrollListener(new RecyclerView.OnScrollListener() {
    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);
        int scrollY = mPostList.computeVerticalScrollOffset();
        // mAppBarBg corresponds to your light green background view
        mAppBarBg.setTranslationY(-scrollY);
        // I also have a drop shadow on the Toolbar, this removes the
        // shadow when the list is scrolled to the top
        mToolbarCard.setCardElevation(scrollY <= 0 ? 0 : toolbarElevation);
    }
});

My layout looks like this, if it helps:

<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- this corresponds to your light green background -->
    <FrameLayout
        android:id="@+id/app_bar_bg"
        android:layout_width="match_parent"
        android:layout_height="@dimen/toolbar_container_height"
        android:background="@color/primary" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <!-- CardView adds a drop shadow to the Toolbar -->    
        <android.support.v7.widget.CardView
            android:id="@+id/toolbar_card"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:cardCornerRadius="0dp">

            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="?attr/colorPrimary"
                app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
                app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />

        </android.support.v7.widget.CardView>

        <android.support.v7.widget.RecyclerView
            android:id="@+id/post_list"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:clipToPadding="false"
            android:scrollbarStyle="outsideOverlay"
            android:scrollbars="vertical" />

    </LinearLayout>

</FrameLayout>

Hope this helps!

like image 134
Vicky Chijwani Avatar answered Oct 04 '22 21:10

Vicky Chijwani


I probably wouldn't use the background of the RecyclerView for the actual thing that moves. Maybe don't put a background on the RecyclerView and instead of a different view that is behind the RecyclerView that actually moves. Then you could override onDraw or onLayout on the RecyclerView and update the position of your background to wherever you want it to be relative to the scroll percentage of the RecyclerView.

Something like this... XML:

<RelativeLayout ...>
    <SomeBackgroundView id="backgroundView" ...>
    <MyRecyclerView ...>
</RelativeLayout>

Code:

class MyRecyclerView extends RecyclerView {
    protected int mLastScroll = Integer.MAX_VALUE;
    protected ScrollChangedListener mScrollChangedListener;

    // ...

    @Override
    void onDraw(Canvas c) {
        int scrollY = getScrollY();
        if (scrollY != mLastScroll) {
            mLastScroll = scrollY;
            if (mScrollChangedListener!= null)
                mScrollChangedListener.onScrollChanged(scrollY);
        }

        super.onDraw(c);
    }

    public void setScrollChangedListener(ScrollChangedListener listener) {
        mScrollChangedListener = listener;
    }

    public interface ScrollChangedListener {
        void onScrollChanged(int newScroll);
    }
}

class SomeActivity extends Activity implements ScrollChangedListener {
    // ... 

    @Override
    void onScrollChanged(int newScroll) {
        // Set the background view's position based on newScroll
    }

}
like image 21
Jim Avatar answered Oct 04 '22 20:10

Jim