Shruti Majajan
Shruti Majajan

Reputation: 379

Hide and show viewgroup when we scroll up and down the recyclerView

I want to hide the view group when we scroll down, and show the view when we scroll up in the recyclerview.

This is my code, in it rvSearchItems is Recyclerview, and rlSearch is the Relative Layout that I want to hide and show:

rvSearchItems.addOnScrollListener(new RecyclerView.OnScrollListener() {
    @Override
    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
        super.onScrollStateChanged(recyclerView, newState);
    }

    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);
        if (dy >= 0) {
            if (rlSearch.getVisibility() != View.GONE)
                rlSearch.setVisibility(View.GONE);
        } else if(dy<-5) {
            if (rlSearch.getVisibility() != View.VISIBLE)
                rlSearch.setVisibility(View.VISIBLE);
        }
    }
});

The main issue here is that it works fine when we're scrolling fast. If we scroll slow it flings multiple times.

Upvotes: 6

Views: 4947

Answers (2)

Evgenii Doikov
Evgenii Doikov

Reputation: 431

Here is my code to extend the RecyclerView class for Kotlin. Just add it to your project and use in Fragment or Activity as this:

myRecyclerView.addOnScrollHiddenView(myAnyHiddenView, resources.dpToPx(myValuedp))

.........

 fun RecyclerView.addOnScrollHiddenView(
    hiddenView: View,
    translationX: Float = 0F,
    translationY: Float = 0F,
    duration: Long = 200L
) {
    var isViewShown = true
    this.addOnScrollListener(object : RecyclerView.OnScrollListener() {
        override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
            when{
                dy> 0 && isViewShown -> {
                    isViewShown = false
                    hiddenView.animate()
                        .translationX(translationX)
                        .translationY(translationY)
                        .duration = duration
                }
                dy < 0 && !isViewShown ->{
                    isViewShown = true
                    hiddenView.animate()
                        .translationX(0f)
                        .translationY(0f)
                        .duration = duration
                }
            }
        }
    })
}

Upvotes: 2

Kathi
Kathi

Reputation: 1071

I take advantage of coordinateLayout to implement this behavior

instead of LinerLayout I want to show/hide CardView when recyclerView scrolls (You can use any ViewGroup in your case)

Basically this answers is implemented with use of FAB and AppBarLayout Behavior as Iin Lake describe in FAB Behavior

CardViewAwareScrollingViewBehavior :: *

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import android.support.design.widget.AppBarLayout;
import android.support.design.widget.CoordinatorLayout;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.CardView;
import android.util.AttributeSet;
import android.view.View;

import java.util.List;

public class CardViewAwareScrollingViewBehavior extends AppBarLayout.ScrollingViewBehavior {
    int mAnimState = ANIM_STATE_NONE;
    static final int ANIM_STATE_NONE = 0;
    static final int ANIM_STATE_HIDING = 1;
    static final int ANIM_STATE_SHOWING = 2;
    static final int SHOW_HIDE_ANIM_DURATION = 200;
    public CardViewAwareScrollingViewBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
        return super.layoutDependsOn(parent, child, dependency) ||
                dependency instanceof CardView;
    }

    @Override
    public boolean onStartNestedScroll(final CoordinatorLayout coordinatorLayout, final View child,
            final View directTargetChild, final View target, final int nestedScrollAxes) {
        // Ensure we react to vertical scrolling
        return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL
                || super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes);
    }

    @Override
    public void onNestedScroll(final CoordinatorLayout coordinatorLayout, final View child,
            final View target, final int dxConsumed, final int dyConsumed,
            final int dxUnconsumed, final int dyUnconsumed) {
        super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
        if (dyConsumed > 0) {
            // User scrolled down -> hide the CardView
            List<View> dependencies = coordinatorLayout.getDependencies(child);
            for (View view : dependencies) {
                if (view instanceof CardView) {
                   hide( ((CardView) view));

                }
            }
        } else if (dyConsumed < 0) {
            // User scrolled up -> show the CardView
            List<View> dependencies = coordinatorLayout.getDependencies(child);
            for (View view : dependencies) {
                if (view instanceof CardView) {
                    show((CardView) view);
                }
            }
        }
    }

    void show(final View mView) {
        if (isOrWillBeShown(mView)) {
            // We either are or will soon be visible, skip the call
            return;
        }

        mView.animate().cancel();

        if (shouldAnimateVisibilityChange(mView)) {
            mAnimState = ANIM_STATE_SHOWING;

            if (mView.getVisibility() != View.VISIBLE) {
                // If the view isn't visible currently, we'll animate it from a single pixel
                mView.setAlpha(0f);
                mView.setScaleY(0f);
                mView.setScaleX(0f);
            }

            mView.animate()
                    .scaleX(1f)
                    .scaleY(1f)
                    .alpha(1f)
                    .setDuration(SHOW_HIDE_ANIM_DURATION)
                   // .setInterpolator(AnimationUtils.LINEAR_OUT_SLOW_IN_INTERPOLATOR)
                    .setListener(new AnimatorListenerAdapter() {
                        @Override
                        public void onAnimationStart(Animator animation) {
//                            mView.internalSetVisibility(View.VISIBLE, false);
                            mView.setVisibility(View.VISIBLE);
                        }

                        @Override
                        public void onAnimationEnd(Animator animation) {
                            mAnimState = ANIM_STATE_NONE;

                        }
                    });
        } else {
//            mView.internalSetVisibility(View.VISIBLE, fromUser);
            mView.setVisibility(View.VISIBLE);
            mView.setAlpha(1f);
            mView.setScaleY(1f);
            mView.setScaleX(1f);

        }
    }

    void hide(final View mView) {
        if (isOrWillBeHidden(mView)) {
            // We either are or will soon be hidden, skip the call
            return;
        }

        mView.animate().cancel();

        if (shouldAnimateVisibilityChange(mView)) {
            mAnimState = ANIM_STATE_HIDING;

            mView.animate()
                    .scaleX(0f)
                    .scaleY(0f)
                    .alpha(0f)
                    .setDuration(SHOW_HIDE_ANIM_DURATION)
//                    .setInterpolator(AnimationUtils.FAST_OUT_LINEAR_IN_INTERPOLATOR)
                    .setListener(new AnimatorListenerAdapter() {
                        private boolean mCancelled;

                        @Override
                        public void onAnimationStart(Animator animation) {
//                            mView.internalSetVisibility(View.VISIBLE, fromUser);
                            mView.setVisibility(View.VISIBLE);
                            mCancelled = false;

                        }

                        @Override
                        public void onAnimationCancel(Animator animation) {
                            mCancelled = true;
                        }

                        @Override
                        public void onAnimationEnd(Animator animation) {
                            mAnimState = ANIM_STATE_NONE;

                            if (!mCancelled) {
//                                mView.internalSetVisibility(fromUser ? View.GONE : View.INVISIBLE,
//                                        fromUser);
                                mView.setVisibility(View.GONE);
//                                if (listener != null) {
//                                    listener.onHidden();
//                                }
                            }
                        }
                    });
        } else {
            // If the view isn't laid out, or we're in the editor, don't run the animation
//            mView.internalSetVisibility(true ? View.GONE : View.INVISIBLE, fromUser);
            mView.setOverScrollMode(View.GONE);
//            if (listener != null) {
//                listener.onHidden();
//            }
        }
    }



    boolean isOrWillBeShown(View mView) {
        if (mView.getVisibility() != View.VISIBLE) {
            // If we not currently visible, return true if we're animating to be shown
            return mAnimState == ANIM_STATE_SHOWING;
        } else {
            // Otherwise if we're visible, return true if we're not animating to be hidden
            return mAnimState != ANIM_STATE_HIDING;
        }
    }

    private boolean shouldAnimateVisibilityChange(View mView) {
        return ViewCompat.isLaidOut(mView) && !mView.isInEditMode();
    }

    boolean isOrWillBeHidden(View mView) {
        if (mView.getVisibility() == View.VISIBLE) {
            // If we currently visible, return true if we're animating to be hidden
            return mAnimState == ANIM_STATE_HIDING;
        } else {
            // Otherwise if we're not visible, return true if we're not animating to be shown
            return mAnimState != ANIM_STATE_SHOWING;
        }
    }


}

CustomerCardView that use CardViewAwareScrollingViewBehavior :: *

import android.content.Context;
import android.support.design.widget.CoordinatorLayout;
import android.support.v7.widget.CardView;
import android.util.AttributeSet;

@CoordinatorLayout.DefaultBehavior(CardViewAwareScrollingViewBehavior.class)
public class CustomerCardView extends CardView {

    public CustomerCardView(Context context) {
        super(context);
    }

    public CustomerCardView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public CustomerCardView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }


}

and use this customCardView in xml like below

main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">
        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:titleTextColor="@android:color/white">
        </android.support.v7.widget.Toolbar>
    </android.support.design.widget.AppBarLayout>

    <include layout="@layout/content_main"/>

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

and content_main.xml which is included in main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="prathap.recyclerviewsample.adapters.UI.CardViewAwareScrollingViewBehavior"
    tools:context="prathap.recyclerviewsample.MainActivity"
    tools:showIn="@layout/acitivity_main">

<!--this is out custom Cardview with custom behaviour -->

    <prathap.recyclerviewsample.CustomViews.CustomerCardView
        android:id="@+id/card_"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <ImageButton
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@mipmap/ic_launcher"/>

    </prathap.recyclerviewsample.CustomViews.CustomerCardView>

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_behavior="prathap.recyclerviewsample.adapters.UI.CardViewAwareScrollingViewBehavior"
        />
</android.support.design.widget.CoordinatorLayout>

With this we got CardView hide when recyclerView scroll up and show when recycleView scroll down.

here is my sample screencast

enter image description here

Upvotes: 5

Related Questions