Reputation: 303
I have a persistent bottom sheet (which is basically a button) and a recycler view both contained in a CoordinatorLayout.
When the bottom sheet is expanded, I do not want it to obscure the recycler view. I am able to achieve this by setting app:layout_insetEdge="bottom"
in the bottom sheet and app:layout_dodgeInsetEdges="bottom"
in the recycler view respectively.
However, since the recycler view's height is set to android:layout_height="match_parent"
, its top partly moves out of the screen when the bottom sheet is expanded.
Instead, I want the recycler view to adjust its height dynamically occording to the height of the bottom sheet so it does not move out of the screen anymore. How can I achieve that?
Here is the complete layout:
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:nestedScrollingEnabled="false"
app:layout_dodgeInsetEdges="bottom" />
<Button
android:id="@+id/bottom_sheet"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/update_all"
android:foreground="?attr/selectableItemBackground"
android:background="@drawable/angled_button"
app:behavior_hideable="false"
app:behavior_peekHeight="0dp"
app:layout_insetEdge="bottom"
app:layout_behavior="android.support.design.widget.BottomSheetBehavior" />
</android.support.design.widget.CoordinatorLayout>
Edit: Screenshots added
Without the bottom sheet everything looks fine.
With the bottom sheet expanded the recycler view is not completely visible anymore.
Edit 2: GIF added
Upvotes: 11
Views: 10299
Reputation: 426
Nikita's accepted answer has the right idea, but has two problems :
The proposed padding formula is only correct when the bottom sheet is fully expanded, because the slideOffset provided as argument to the onSlide callback is proportional to the difference between the peek height and the full height of the bottom sheet, not just to its full height
Padding doesn't apply to a recyclerview's fast scrollbar, which continues to extend beneath the bottom sheet. Margin, on the other hand, behaves as intended.
Here is a Kotlin implementation that takes those two corrections into account :
val behavior = BottomSheetBehavior.from(constraintLayout)
behavior.setBottomSheetCallback(object : BottomSheetCallback() {
override fun onSlide(bottomSheet: View, slideOffset: Float) {
val margin = behavior.peekHeight + (bottomSheet.height - behavior.peekHeight) * slideOffset
recyclerview.updateLayoutParams<FrameLayout.LayoutParams> { bottomMargin = margin.toInt() }
}
override fun onStateChanged(bottomSheet: View, newState: Int) { /* Nothing to do */ }
})
The type of LayoutParams referenced should be changed according to the parent of the RecyclerView in your own layout.
Please note that the RecyclerView's bottom margin should be set to the bottom sheet's peek height in the layout xml, in order to get the right layout when the activity loads, before any sliding has occurred.
The above example is for a bottom sheet that isn't hideable, which means the slideOffset argument only goes from 0 to 1.
For a hideable bottom sheet, another formula should be used when the slideOffset is between -1 and 0 : behavior.peekHeight * (1 + slideOffset)
Upvotes: 7
Reputation: 573
Faced the same issue recently, didn't find a better way than to remove app:layout_dodgeInsetEdges="bottom"
and use padding instead. This is how it can be achieved:
Kotlin:
val rv = findViewById<RecyclerView>(R.id.recycler_view))
val behavior = BottomSheetBehavior.from(findViewById<Button>(R.id.bottom_sheet))
behavior.setBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback(){
override fun onSlide(bottomSheet: View, offset: Float) {
rv.setPadding(0, 0, 0, (bottomSheet.height * offset).toInt())
}
override fun onStateChanged(bottomSheet: View, newState: Int){}
})
Java:
RecyclerView rv = (RecyclerView) findViewById(R.id.recycler_view);
BottomSheetBehavior behavior = BottomSheetBehavior.from(findViewById(R.id.bottom_sheet));
behavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
@Override
public void onStateChanged(@NonNull View bottomSheet, int newState) {
}
@Override
public void onSlide(@NonNull View bottomSheet, float slideOffset) {
rv.setPadding(0, 0, 0, (int)(slidingView.getHeight() * offset));
}
});
Pros:
Cons:
Upvotes: 12
Reputation: 59004
RecyclerView
Because your CoordinatorLayout is not aligning RecyclerView
below Toolbar
. So set top margin actionBarSize
.
android:layout_marginTop="?actionBarSize"
RecyclerView
comes up of Toolbar
Set android:elevation
to RecyclerView
because Toolbar
has 4dp
elevation, and above 21 version layouts are layered by elevation. So set
<android.support.v7.widget.RecyclerView
...
android:elevation="@dimen/design_appbar_elevation"/>
design_appbar_elevation
value is 16dp
.
Upvotes: -1
Reputation: 1834
Try putting this attribute to the RecyclerView:
app:layout_behavior="@string/appbar_scrolling_view_behavior"
Upvotes: 0