I want to make some hidden filters options above recyclerview (for example like in old versions of spotify):
How to do that? I use AppBar above my recycler.
You can use this code to do what you want
mRecyclerView.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) {
//You should HIDE filter view here
} else if (dy < 0) {
System.out.println("Scrolled Upwards");
if (mLayoutManager.findFirstCompletelyVisibleItemPosition() == 0) {
//this is the top of the RecyclerView
System.out.println("==================================== >>>> Detect top of the item List");
//You should visible filter view here
} else {
//You should HIDE filter view here
}
} else {
System.out.println("No Vertical Scrolled");
//You should HIDE filter view here
}
}
});
You can do it as mentioned in answer above (with changing visibility of searching view so when it is visibility is View.GONE the recycleView will move up, using ContraintLayout). Or to add aditional ViewHolder to yours adapter (like TopSearchViewHolder which contains yours searching view).
Like this (in getItemViewType you would decide when to display particular viewType) :
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val orderViewHolder = OrderViewHolder.create(parent).apply {
listener = this#OrdersAdapter.listener
}
return when (viewType) {
R.layout.list_item1 -> orderViewHolder
R.layout.list_item2 -> NetworkStateViewHolder.create(parent, retryCallback)
else -> throw IllegalAccessException("Unknown view type $viewType")
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (getItemViewType(position)) {
R.layout.list_item1 -> (holder as OrderViewHolder).bind(getItem(position))
R.layout.list_item2 -> (holder as NetworkStateViewHolder).bind(getItem(position))
}
}
override fun getItemViewType(position: Int): Int {
return if (hasExtraRow() && position == itemCount - 1) {
R.layout.list_item1
} else {
R.layout.list_item2
}
}
Use the RecyclerView scroll listener as I mentioned in the comment like this
private static int firstVisibleInListview;
firstVisibleInListview = yourLayoutManager.findFirstVisibleItemPosition();
#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);
int currentFirstVisible =
yourLayoutManager.findFirstVisibleItemPosition();
if(currentFirstVisible > firstVisibleInListview){
image.setVisibility(View.VISIBLE);
}else{
filterView.setVisibvility(View.GONE);
}
}
}
});
XML for RecyclerView layout
<LinearLayout android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/White"
android:orientation="vertical"
android:layout_gravity="center"
xmlns:android="http://schemas.android.com/apk/res/android">
//use FilterView here
//hide this view initially.
<Filter-view
android:visibility="gone"
android:id="#+id/filterView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/moviesRecyclerView"
android:layout_marginBottom="?attr/actionBarSize"/>
<TextView
android:visibility="gone"
android:layout_marginTop="#dimen/dimen_200dp"
android:id="#+id/noMoviesMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:padding="#dimen/dimen_10dp"
android:fontFamily="#font/noirden_bold"
android:textColor="#color/Black"
android:textSize="#dimen/text_18sp"
android:text="#string/the_are_no_movies_showing_right_now_please_check_later"/>
</LinearLayout>
let me know after you try this
My solution for now:
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
int dy = 0;
Handler handler = new Handler();
#Override
public void onScrollStateChanged(RecyclerView recyclerView, final int newState) {
super.onScrollStateChanged(recyclerView, newState);
final boolean isOnTop = layoutManager.findFirstCompletelyVisibleItemPosition() == 0;
if (isOnTop && newState == 1)
handler.postDelayed(new Runnable() {
#Override
public void run() {
if (dy <= 0) {
KLog.d("show filter");
}
}
}, 20);
else if (newState == 1 && dy > 0)
KLog.e("gone filter");
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
this.dy = dy;
}
});
Not super elegant and still need to find a way to animate hidden view.
Well, I'm trying to make a top bar like the one that is on Youtube's app. It works on almost all cases but when I use the ScrollListener from RecyclerView I get a problem.
As you can see the View doesn't change its position at certain moment when scrolled.
Here is my code:
rvTop.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
LinearLayoutManager layoutManager = (LinearLayoutManager) layoutManagerTop;
if(filterTopAdapter.lastSelected >= layoutManager.findFirstVisibleItemPosition() && filterTopAdapter.lastSelected <= layoutManager.findLastVisibleItemPosition()) {
selectionLine.setVisibility(View.VISIBLE);
final View view = rvTop.findViewHolderForAdapterPosition(filterTopAdapter.lastSelected).itemView;
selectionLine.setTranslationX(view.getLeft() - rvTop.getScrollX());
Log.d("Scroll", String.valueOf(view.getLeft() - rvTop.getScrollX()));
}
else {
selectionLine.setVisibility(View.GONE);
}
}
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
LinearLayoutManager layoutManager = (LinearLayoutManager) layoutManagerTop;
if(filterTopAdapter.lastSelected >= layoutManager.findFirstVisibleItemPosition() && filterTopAdapter.lastSelected <= layoutManager.findLastVisibleItemPosition()) {
selectionLine.setVisibility(View.VISIBLE);
final View view = rvTop.findViewHolderForAdapterPosition(filterTopAdapter.lastSelected).itemView;
selectionLine.setTranslationX(view.getLeft() - rvTop.getScrollX());
Log.d("Scroll", String.valueOf(view.getLeft() - rvTop.getScrollX()));
}
else {
selectionLine.setVisibility(View.GONE);
}
}
});
You should try TabLayout with ViewPager
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="fill_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context="stellar.kade_c.com.MainActivity">
<android.support.design.widget.TabLayout
android:id="#+id/sliding_tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabMode="scrollable" />
<android.support.v4.view.ViewPager
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
You should use TabLayout with ViewPager for this
I am using a RecyclerView inside SwipeRefreshLayout.The RecyclerView has another 2 RecyclerView (for now; it may increase). In my second RecyclerView i am trying to implement infinite scrolling. But my RecyclerView.getItemCount() and RecyclerView.getChildCount() are giving same value. Also the 2nd re has GridLayoutManager and GridlayoutManager.findFirstVisibleItemPosition() always gives 0 and GridLayoutManager.findLastVisibleItemPosition() always gives list size - 1 in OnScrolled of the RecyclerView. What is causing this and what should i do to implement the infinite scrolling.
fragment.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.SwipeRefreshLayout
android:id="#+id/swipe_container"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="#+id/main_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:paddingTop="#dimen/padding_standard"
android:paddingBottom="#dimen/padding_standard">
</android.support.v7.widget.RecyclerView>
</android.support.v4.widget.SwipeRefreshLayout>
parent_recyclerview.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:tools="http://schemas.android.com/tools"
android:layout_marginBottom="#dimen/padding_standard"
android:orientation="vertical">
<TextView
android:id="#+id/section_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="#dimen/padding_standard"
android:layout_marginBottom="#dimen/margin_standard"
android:textColor="#color/label_text"
android:textSize="#dimen/text_size_standard"
android:textStyle="bold"
tools:text="MOments"/>
<android.support.v7.widget.RecyclerView
android:id="#+id/section_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false"/>
<ProgressBar
android:id="#+id/events_progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:visibility="gone"/>
</LinearLayout>
onScrollListener for child recycler view
RecyclerView.OnScrollListener scrollListener = new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
GridLayoutManager manager = (GridLayoutManager) recyclerView.getLayoutManager();
int itemSize = manager.getItemCount();
int firstVisibleItem = manager.findFirstVisibleItemPosition();
int visibleChIldCount = manager.getChildCount();
Logger.e(TAG,"=============== START =====================");
Logger.e(TAG, "itemSize: " + itemSize);
Logger.e(TAG, "firstVisibleitem: " + firstVisibleItem);
Logger.e(TAG, "visibleChIldCount: " + visibleChIldCount);
Logger.e(TAG,"mLayoutManager.firstCOmpletely: "+ manager.findFirstCompletelyVisibleItemPosition());
Logger.e(TAG,"mLayoutManager. lastcompletey: "+ manager.findLastCompletelyVisibleItemPosition());
Logger.e(TAG,"mLayoutManager.findLastVisible: "+ manager.findLastVisibleItemPosition());
Logger.e(TAG,"=================END ================");
if (itemSize >= firstVisibleItem + visibleChIldCount){
Logger.e("", "loading");
mLoadMoreListener.loadMore();
} else {
Logger.e(TAG, "not Loading");
}
}
};
Sorry for the late reply. But i will post my solution here if in case someone else is looking for them.
In the adapter of the parent recycler view i have set tag for the view
#Override
public SectionRowHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = mLayoutInflater.inflate(R.layout.view_section, parent, false);
view.setTag(viewType);
return new SectionRowHolder(view);
}
The SectionRowHolder is simple ViewHolder with a RecyclerView.OnScrollListener property with getter and setter.
public class SectionRowHolder extends RecyclerView.ViewHolder {
protected RecyclerView recyclerView;
RecyclerView.OnScrollListener mOnScrollListener;
public SectionRowHolder(View view) {
super(view);
this.recyclerView = (RecyclerView) view.findViewById(R.id.section_list);
}
public RecyclerView.OnScrollListener getCustomScrollListener() {
return mOnScrollListener;
}
public void setCustomScrollListener(RecyclerView.OnScrollListener mOnScrollListener) {
this.mOnScrollListener = mOnScrollListener;
}
}
Then in the onBindViewHolder for the child with infinite scrolling I have implemented the load more logic in scroll listener and set to the child RecyclerView.
RecyclerView.OnScrollListener scrollListener = new RecyclerView.OnScrollListener() {
boolean loadEnable = false;
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
mTotalScrolled += dy;
if ((mTotalScrolled + LOAD_MORE_ENABLE_HEIGHT) > recyclerView.getHeight() && loadEnable) {
loadEnable = false;
mLoadMoreListener.loadMore();
} else {
loadEnable = true;
}
}
};
holder.setCustomScrollListener(scrollListener);
holder.recyclerView.addOnScrollListener(scrollListener);
Here LOAD_MORE_ENABLE_HEIGHT is offset from bottom of the child recycler view to initialize the loadmore() logic and mLoadMoreListener is callback to the fragment or activity.
Finally for passing the scoll listener from parent recycler view to the child recycler vew, in my parent RecyclerView's onScrollListener
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
View v = mRecyclerView.findViewWithTag(CHILD_RECYCLERVIEW_TAG);
SectionedLifeAtAdapter.SectionRowHolder viewHolder =
(SectionedLifeAtAdapter.SectionRowHolder) mRecyclerView
.findContainingViewHolder(v);
if (viewHolder.getCustomScrollListener() != null)
viewHolder.getCustomScrollListener().onScrolled((RecyclerView) v
.findViewById(R.id.section_list), dx, dy);
Logger.e(TAG, ">>> call to on scrolled listener >>>");
}
});
Here CHILD_RECYCLERVIEW_TAG is the view type that you set in the onCreateViewHolder of the parent Adapter.
It kind of look like messy but it did the job for me without any issues.
I have three recyclerview inside a nestedscrollview and I need that the third one keep recycling like if he was out of the nestedscrollview.
It all goes great except the recycling feature is missing.
Here is my xml :
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/scroll_discovery"
android:fillViewport="true"
android:fitsSystemWindows="true"
>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/category_recycler"/>
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#id/category_recycler"
android:id="#+id/user_recycler"/>
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#id/user_recycler"
android:id="#+id/experiences"/>
</RelativeLayout>
</android.support.v4.widget.NestedScrollView>
and here is the java part
experienceRecycler=(RecyclerView)findViewById(R.id.experiences);
experienceRecycler.setHasFixedSize(true);
mLayoutManager =new LinearLayoutManager(this) {
#Override
public boolean canScrollVertically() {
return false;
}
};
page= 1;
experienceRecycler.setLayoutManager(mLayoutManager);
experienceAdapter=new FeedCardAdapter(experiences,this,currentUser);
experienceRecycler.setAdapter(experienceAdapter);
/*experienceRecycler.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);
feedCardAdapter.setScrolled(true);
}
});*/
categoryRecycler=(RecyclerView)findViewById(R.id.category_recycler);
categoryRecycler.setLayoutManager(new LinearLayoutManager(this){
#Override
public boolean canScrollVertically() {
return false;
}
});
categoryAdapter=new CategoryAdapter(this,categories);
categoryRecycler.setHasFixedSize(true);
categoryRecycler.setAdapter(categoryAdapter);
userRecycler=(RecyclerView)findViewById(R.id.user_recycler);
userRecycler.setLayoutManager(new LinearLayoutManager(this){
#Override
public boolean canScrollVertically() {
return false;
}
});
userAdapter=new UserAdapter(this,users);
userRecycler.setAdapter(userAdapter);
userRecycler.setHasFixedSize(true);
I finaly find the solution by myself.
I just had a height to the recyclerview I want to keep recycling.
Im using the FloatingActionButton from the android.support.design.widget package:
<android.support.design.widget.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_marginBottom="20dp"
android:layout_marginEnd="16dp"
android:clickable="true"
android:backgroundTint="#color/primaryColor"
android:src="#drawable/ic_search_white_24dp"
app:borderWidth="0dp"
app:elevation="6dp"
app:backgroundTint="#color/primaryColorDark"
app:rippleColor="#color/accentColor" />
Is it possible to configure that button to hide with an animation when the listview is scrolling down and to show it again when listview is scrolling up to the top?
Those who are looking to make it with recyclerview can do this:
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
if (dy > 0 || dy < 0 && fab.isShown())
fab.hide();
}
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
if (newState == RecyclerView.SCROLL_STATE_IDLE)
fab.show();
super.onScrollStateChanged(recyclerView, newState);
}
});
Sorry! I am late by years to answer this. I hope this still helps someone. This is also my first answer.
Mates! No need to implement scroll listeners.
Add the following to the floating action button xml:
app:layout_behavior="com.google.android.material.behavior.HideBottomViewOnScrollBehavior"
giving:
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="#+id/fabAddOItransferIn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="#dimen/fab_margin"
android:text="#string/btn_text_transfer_in"
app:icon="#android:drawable/ic_input_add"
app:layout_behavior="com.google.android.material.behavior.HideBottomViewOnScrollBehavior"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent" />
In response to the following comment of mine,
"Sorry! I just noticed this has a weird side effect. Any snackbars will overlap this floating action button if app:layout_behavior="com.google.android.material.behavior.HideBottomViewOnScrollBehavior is added. ☹️ Taking this line off will prevent the overlap and the floating action button will behave as it is intended to inside the coordinator layout. "
To counter this, do use the following:
Snackbar.make(floating_action_button, "Some snackbar text!", BaseTransientBottomBar.LENGTH_SHORT).setAnchorView(floating_action_button).show();
A small improvement to the code from Irfan Raza:
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener(){
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy){
if (dy<0 && !fab.isShown())
fab.show();
else if(dy>0 && fab.isShown())
fab.hide();
}
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
});
The Floating Action Button hides when scrolling down and shows when scrolling up.
See this. Here it tells how to do what you are trying to achieve. You have to use it like this in a CoordinatorLayout and ListView :
<android.support.design.widget.CoordinatorLayout
android:id="#+id/main_content"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="#+id/lvToDoList"
android:layout_width="match_parent"
android:layout_height="match_parent"></ListView>
<android.support.design.widget.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:layout_margin="16dp"
android:src="#drawable/ic_done"
app:layout_anchor="#id/lvToDoList"
app:layout_anchorGravity="bottom|right|end" />
</android.support.design.widget.CoordinatorLayout>
using this class you can easily animate you FAB, here I have implemented onStopNestedScroll() method to show your Fab whenever scroll stop.
I set 1000 miliSeconds as delay using Handler();
public class FabBehaviour extends CoordinatorLayout.Behavior<FloatingActionButton> {
private static final String TAG = "ScrollingFABBehavior";
Handler mHandler;
public FabBehaviour(Context context, AttributeSet attrs) {
super();
}
public FabBehaviour() {
super();
}
#Override
public void onStopNestedScroll(#NonNull CoordinatorLayout coordinatorLayout, #NonNull final FloatingActionButton child, #NonNull View target, int type) {
super.onStopNestedScroll(coordinatorLayout, child, target, type);
if (mHandler == null)
mHandler = new Handler();
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
child.animate().translationY(0).setInterpolator(new LinearInterpolator()).start();
Log.d("FabAnim", "startHandler()");
}
}, 1000);
}
#Override
public void onNestedScroll(#NonNull CoordinatorLayout coordinatorLayout, #NonNull FloatingActionButton child, #NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) {
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type);
if (dyConsumed > 0) {
Log.d("Scrolling", "Up");
CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) child.getLayoutParams();
int fab_bottomMargin = layoutParams.bottomMargin;
child.animate().translationY(child.getHeight() + fab_bottomMargin).setInterpolator(new LinearInterpolator()).start();
} else if (dyConsumed < 0) {
Log.d("Scrolling", "down");
child.animate().translationY(0).setInterpolator(new LinearInterpolator()).start();
}
}
#Override
public boolean onStartNestedScroll(#NonNull CoordinatorLayout coordinatorLayout, #NonNull FloatingActionButton child, #NonNull View directTargetChild, #NonNull View target, int axes, int type) {
if (mHandler != null) {
mHandler.removeMessages(0);
Log.d("Scrolling", "stopHandler()");
}
return axes == ViewCompat.SCROLL_AXIS_VERTICAL;
}
}
your_layout.xml
<android.support.design.widget.FloatingActionButton
android:id="#+id/imageViewYes"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end|right"
android:layout_margin="#dimen/fab_margin"
android:src="#drawable/ic_yes"
app:backgroundTint="#color/white"
android:scaleType="center"
app:elevation="6dp"
app:fabSize="normal"
app:layout_behavior="com.your.package.FabBehaviour"
app:pressedTranslationZ="12dp"
app:rippleColor="#color/gray" />
hey there is o require to take the recyclerview for auto hiding the floating action button on scrolling down for this purpose we can use default listview with floating action button in normal way only make modifications on listview.onscroll listener then we can get feel like recycle
listview.setOnScrollListener(new AbsListView.OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
int lastItem = firstVisibleItem + visibleItemCount;
if (lastItem == totalItemCount) {
fab.setVisibility(View.INVISIBLE);
}else {
fab.setVisibility(View.VISIBLE);
}
}
});
There is my code in kotlin.
class ScrollAwareFABBehavior (val recyclerView: RecyclerView, val floatingActionButton: FloatingActionButton) {
fun start() {
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
if (dy > 0) {
if (floatingActionButton!!.isShown) {
floatingActionButton?.hide()
}
} else if (dy < 0) {
if (!floatingActionButton!!.isShown) {
floatingActionButton?.show()
}
}
}
})
}
}
Now, you just need to call the ScrollAwareFABBehavior with the recyclerView and the fab on constructor, then call method start().
ScrollAwareFABBehavior(recyclerView = recyclerViewPlaceFormContainer, floatingActionButton = floatingActionButton).start()
Another method for recycleView using kotlin extensions.
fun RecyclerView.attachFab(fab : FloatingActionButton) {
this.addOnScrollListener(object : RecyclerView.OnScrollListener(){
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
if (dy > 0)
fab.hide()
else if (dy < 0)
fab.show()
}
})
}
Now you can attach fab to any recycleView with:
rv.attachFab(requireActivity().fab)
// in my case i made fab public on activity
Here I am adding extra padding for last view item to avoid overlapping list item with floating action button
I used this in a RecyclerView.Adapter's onBindViewHolder method to set the bottom margin of the last item in the list to 72dp so that it will scroll up above the floating action button.
This does not require a dummy entry in the list.
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
// other binding code goes here.
if (position + 1 == getItemCount()) {
// set bottom margin to 72dp.
setBottomMargin(holder.itemView, (int) (72 * Resources.getSystem().getDisplayMetrics().density));
} else {
// reset bottom margin back to zero. (your value may be different)
setBottomMargin(holder.itemView, 0);
}
}
public static void setBottomMargin(View view, int bottomMargin) {
if (view.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) {
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
params.setMargins(params.leftMargin, params.topMargin, params.rightMargin, bottomMargin);
view.requestLayout();
}
}
Kotlin + DataBinding Adapter
#BindingAdapter("bindAdapter:attachFloatingButton")
fun bindRecyclerViewWithFB(recyclerView: RecyclerView, fb: FloatingActionButton) {
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
if (dy > 0 && fb.isShown) {
fb.hide()
} else if (dy < 0 && !fb.isShown) {
fb.show()
}
}
})
}
and the xml
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/main_recyclerview"
android:layout_width="match_parent"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
android:layout_height="wrap_content"
android:clipToPadding="false"
android:paddingBottom="8dp" app:layout_constraintEnd_toEndOf="parent" android:layout_marginEnd="8dp"
app:layout_constraintStart_toStartOf="parent" android:layout_marginStart="8dp"
android:layout_marginTop="8dp" app:layout_constraintTop_toBottomOf="#+id/main_chips"
android:layout_marginBottom="8dp"
**bindAdapter:attachFloatingButton="#{mainFb}"**
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintVertical_bias="0.0"/>
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="#+id/main_fb"
android:layout_width="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
style="#style/Widget.Design.FloatingActionButton"
app:layout_constraintEnd_toEndOf="parent"
android:layout_height="wrap_content"
android:layout_margin="18dp"
android:background="#color/colorPrimaryDark"
app:icon="#drawable/ic_add_black_24dp"/>
According to me the best way to implement this would be as below.
public class ScrollingFABBehavior extends FloatingActionButton.Behavior {
private static final String TAG = "ScrollingFABBehavior";
public ScrollingFABBehavior(Context context, AttributeSet attrs) {
super();
// Log.e(TAG, "ScrollAwareFABBehavior");
}
public boolean onStartNestedScroll(CoordinatorLayout parent, FloatingActionButton child, View directTargetChild, View target, int nestedScrollAxes) {
return true;
}
#Override
public boolean layoutDependsOn(CoordinatorLayout parent, FloatingActionButton child, View dependency) {
if (dependency instanceof RecyclerView)
return true;
return false;
}
#Override
public void onNestedScroll(CoordinatorLayout coordinatorLayout,
FloatingActionButton child, View target, int dxConsumed,
int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
// TODO Auto-generated method stub
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed,
dxUnconsumed, dyUnconsumed);
//Log.e(TAG, "onNestedScroll called");
if (dyConsumed > 0 && child.getVisibility() == View.VISIBLE) {
// Log.e(TAG, "child.hide()");
child.hide();
} else if (dyConsumed < 0 && child.getVisibility() != View.VISIBLE) {
// Log.e(TAG, "child.show()");
child.show();
}
}}
For detailed answer check this out. Hide FloatingActionButton on scroll of RecyclerView
for Kotlin it is very simple (API 23+)
myRecyclerView.setOnScrollChangeListener { _, _, _, _, oldScrollY ->
if (oldScrollY < 0) myFAB.hide() else myFAB.show()
}
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (dy > 0 && mFloatingActionButton.getVisibility() == View.VISIBLE) {
mFloatingActionButton.hide();
} else if (dy < 0 && mFloatingActionButton.getVisibility() != View.VISIBLE) {
mFloatingActionButton.show();
}
}});
Just to add, for NestedScrollView the approach will be something like the following:
// register the extended floating action Button
final ExtendedFloatingActionButton extendedFloatingActionButton = findViewById(R.id.extFloatingActionButton);
// register the nestedScrollView from the main layout
NestedScrollView nestedScrollView = findViewById(R.id.nestedScrollView);
// handle the nestedScrollView behaviour with OnScrollChangeListener
// to extend or shrink the Extended Floating Action Button
nestedScrollView.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() {
#Override
public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
// the delay of the extension of the FAB is set for 12 items
if (scrollY > oldScrollY + 12 && extendedFloatingActionButton.isExtended()) {
extendedFloatingActionButton.shrink();
}
// the delay of the extension of the FAB is set for 12 items
if (scrollY < oldScrollY - 12 && !extendedFloatingActionButton.isExtended()) {
extendedFloatingActionButton.extend();
}
// if the nestedScrollView is at the first item of the list then the
// extended floating action should be in extended state
if (scrollY == 0) {
extendedFloatingActionButton.extend();
}
}
});
I've taken this code from GeeksForGeeks