Chirag Patel
Chirag Patel

Reputation: 467

HideBottomViewOnScrollBehavior not working on recyclerview item expand/collapse

I am trying to hide text view on scroll down and show on scroll up it's working fine if I have an item like 10 or 15 but it's not working the same if I have less item

in recyclerview, I have expanded/collapse functionality so it's not the same sometimes

textview not hiding/visible some times I don't understand I added this line to my view which I want to hide/show on scroll

app:layout_behavior="com.google.android.material.behavior.HideBottomViewOnScrollBehavior"

XML

<androidx.coordinatorlayout.widget.CoordinatorLayout
        android:id="@+id/lnMain"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@mipmap/bg"
        tools:context=".tab.history.view.HistoryFragment">

        <com.google.android.material.appbar.AppBarLayout
            android:id="@+id/mAppBarLayout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:elevation="0dp">

            <RelativeLayout
                android:id="@+id/lnActionBar"
                android:layout_width="match_parent"
                android:layout_height="?android:attr/actionBarSize"
                android:background="@color/colorPrimary">

                <TextView
                    android:id="@+id/tvtitle"
                    style="@style/fontMedium"
                    android:layout_width="wrap_content"
                    android:layout_height="?android:attr/actionBarSize"
                    android:layout_centerHorizontal="true"
                    android:gravity="center_vertical"
                    android:text="@string/beacon"
                    android:textColor="@color/white"
                    android:textSize="@dimen/header_font_size" />

            </RelativeLayout>

        </com.google.android.material.appbar.AppBarLayout>

       
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rvBeacon"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:clipToPadding="false"
        android:overScrollMode="never"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        android:paddingBottom="@dimen/_40sdp"
        android:scrollbars="none"
        android:visibility="visible"
        app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
        tools:itemCount="10"
        tools:listitem="@layout/raw_beacon" />

<TextView
            android:id="@+id/btnBack"
            style="@style/fontBold"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_centerHorizontal="true"
            android:layout_marginTop="@dimen/_10sdp"
            android:layout_marginBottom="@dimen/_20sdp"
            android:background="@drawable/background_button_yellow_20dp"
            android:contentDescription="@string/back_button"
            android:drawableStart="@drawable/ic_back"
            android:layout_gravity="bottom|center_horizontal"
            android:drawablePadding="@dimen/_5sdp"
            android:gravity="center"
            android:padding="@dimen/_10sdp"
            android:text="@string/back_to_search"
            android:textColor="@color/white"
            android:textSize="@dimen/button_font_size"
            android:visibility="@{!isScanning ? View.VISIBLE: View.GONE}"
            app:layout_behavior="com.google.android.material.behavior.HideBottomViewOnScrollBehavior" />

</androidx.coordinatorlayout.widget.CoordinatorLayout>

this is what I did so far but it's not working every time

Note:- Please not I have a recyclerview item which expands onclick so scroll and textview must behave according to that

Any help would be highly appriciated

Upvotes: 2

Views: 2042

Answers (3)

Vitali_Ac
Vitali_Ac

Reputation: 11

There was a similar problem on Android 13. If there are fewer elements in the recyclerView and scrolling is not required, while you can open the keyboard, then if you swipe up, then HideBottomViewOnScrollBehavior hid the view and no longer showed it. Used S T's answer and extended the standard HideBottomViewOnScrollBehavior class:

    class HideBottomViewOnRealScrollBehavior<V : View> @JvmOverloads constructor(
    context: Context? = null,
    attrs: AttributeSet? = null
) : HideBottomViewOnScrollBehavior<V>(context, attrs) {

    private var dyDirectionSum = 0

    override fun onNestedPreScroll(
        coordinatorLayout: CoordinatorLayout,
        child: V,
        target: View,
        dx: Int,
        dy: Int,
        consumed: IntArray,
        type: Int
    ) {
        if (dy > 0 && dyDirectionSum < 0 || dy < 0 && dyDirectionSum > 0) {
            child.animate().cancel()
            dyDirectionSum = 0
        }
        dyDirectionSum += dy
        if (dyDirectionSum > child.height) {
            slideDown(child)
        } else if (dyDirectionSum < -child.height) {
            slideUp(child)
        }
    }

}

Usage:

app:layout_behavior="your.package.HideBottomViewOnRealScrollBehavior"

Upvotes: 0

mitesh makwana
mitesh makwana

Reputation: 279

i don't know why HideBottomViewOnScrollBehavior is not working for you

app:layout_behavior="com.google.android.material.behavior.HideBottomViewOnScrollBehavior"

may be it's beacause you have a expand/collapse functionality since you have only recyclerview only in screen so you can also perform this task by adding Custom ScrollListener

MyRecyclerScroll class

public abstract class MyRecyclerScroll extends RecyclerView.OnScrollListener {
    private static final float HIDE_THRESHOLD = 100;
    private static final float SHOW_THRESHOLD = 50;

    int scrollDist = 0;
    private boolean isVisible = true;

    //    We dont use this method because its action is called per pixel value change
    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);

        //  Check scrolled distance against the minimum
        if (isVisible && scrollDist > HIDE_THRESHOLD) {
            //  Hide fab & reset scrollDist
            hide();
            scrollDist = 0;
            isVisible = false;
        }
        //  -MINIMUM because scrolling up gives - dy values
        else if (!isVisible && scrollDist < -SHOW_THRESHOLD) {
            //  Show fab & reset scrollDist
            show();

            scrollDist = 0;
            isVisible = true;
        }

        //  Whether we scroll up or down, calculate scroll distance
        if ((isVisible && dy > 0) || (!isVisible && dy < 0)) {
            scrollDist += dy;
        }

    }


    public abstract void show();

    public abstract void hide();
}

Activity/Fragment

binding.rvBeacon.addOnScrollListener(object : MyRecyclerScroll() {
            override fun show() {
                binding.btnBack.animate().translationY(0f).setInterpolator(DecelerateInterpolator(2f)).start()
            }

            override fun hide() {
                binding.btnBack.animate().translationY(binding.btnBack.getHeight() + 60f)
                    .setInterpolator(AccelerateInterpolator(2f)).start()
            }
        })

you can change animation, delay and margin according to your requirement

for more detail refer to this blog

Note: it will not work if your recyclerview inside scrollview

Upvotes: 2

S T
S T

Reputation: 1134

how about make custom behavior?

for example.

public class QuickReturnFooterBehavior extends CoordinatorLayout.Behavior<View> {

    private static final Interpolator INTERPOLATOR = new FastOutSlowInInterpolator();
    private static final long ANIMATION_DURATION = 200;

    private int dyDirectionSum;
    private boolean isShowing;
    private boolean isHiding;
    private boolean isNeedOption = true;

    public boolean isNeedOption() {
        return isNeedOption;
    }

    public void setNeedOption(boolean needOption) {
        isNeedOption = needOption;
    }

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

    @Override
    public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View directTargetChild, @NonNull View target, int axes, int type) {
        return axes == ViewCompat.SCROLL_AXIS_VERTICAL;
    }

    @Override
    public void onNestedPreScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View target, int dx, int dy, @NonNull int[] consumed, int type) {
        // scroll chhange up and down
        if (isNeedOption) {
            showView(child);
        } else {
            if (dy > 0 && dyDirectionSum < 0
                    || dy < 0 && dyDirectionSum > 0) {
                child.animate().cancel();
                dyDirectionSum = 0;
            }

            dyDirectionSum += dy;

            if (dyDirectionSum > child.getHeight()) {
                hideView(child);
            } else if (dyDirectionSum < -child.getHeight()) {
                showView(child);
            }

        }

    }

    private void hideView(final View view) {
        if (isHiding || view.getVisibility() != View.VISIBLE) {
            return;
        }

        ViewPropertyAnimator animator = view.animate()
                .translationY(view.getHeight())
                .setInterpolator(INTERPOLATOR)
                .setDuration(ANIMATION_DURATION);

        animator.setListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animator) {
                isHiding = true;
            }

            @Override
            public void onAnimationEnd(Animator animator) {
                isHiding = false;
                view.setVisibility(View.INVISIBLE);
            }

            @Override
            public void onAnimationCancel(Animator animator) {
                // show when cancle
                isHiding = false;
                showView(view);
            }

            @Override
            public void onAnimationRepeat(Animator animator) {
                // no-op
            }
        });

        animator.start();
    }

    private void showView(final View view) {
        if (isShowing || view.getVisibility() == View.VISIBLE) {
            return;
        }
        ViewPropertyAnimator animator = view.animate()
                .translationY(0)
                .setInterpolator(INTERPOLATOR)
                .setDuration(ANIMATION_DURATION);

        animator.setListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animator) {
                isShowing = true;
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationEnd(Animator animator) {
                isShowing = false;
            }

            @Override
            public void onAnimationCancel(Animator animator) {
                // show when cancle
                isShowing = false;
                hideView(view);
            }

            @Override
            public void onAnimationRepeat(Animator animator) {
                // no-op
            }
        });

        animator.start();
    }
}

UPDATE : for check can scroll in behavior

child.canScrollVertically(1)  // "Top of list"
child.canScrollVertically(-1)  // "End of list"

**UPDATE : add setter and getter **

private boolean isNeedOption = true;

Upvotes: 1

Related Questions