Reputation: 379
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
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
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
Upvotes: 5