Reputation: 17788
I have a BottomSheetDialogFragment
with a RecyclerView
. The problem is, I want to disable the drag close function of the BottomSheetDialogFragment
as long as the RecyclerView
is not scrolled up (currently I can't scroll my RecyclerView
as the attempt will always close the BottomSheetDialogFragment
). Any ideas how to achieve this?
Upvotes: 38
Views: 31179
Reputation: 191
Just wrap your layout into NestedScrollView and add this attribute in to the RecyclerView
android:nestedScrollingEnabled="true"
Upvotes: 0
Reputation: 31
To scroll RecyclerView inside BottomSheet can use this way:
Add android:focusableInTouchMode="true" to RecyclerView in file XML.
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="5dp"
android:clipToPadding="false"
android:focusableInTouchMode="true"
android:paddingBottom="50dp" />
- In func onCreateDialog for BottomSheet: + where: maxDesiredHeight is set fixed size of BottomSheet. + this.isDraggable = false -> disabling dragging on BottomSheet.
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val dialog = super.onCreateDialog(savedInstanceState) as BottomSheetDialog
dialog.setOnShowListener {
dialog.findViewById<FrameLayout>(com.google.android.material.R.id.design_bottom_sheet)!!
.apply {
val maxDesiredHeight = (resources.displayMetrics.heightPixels * 0.95).toInt()
val bottomSheetLayoutParams = this.layoutParams
bottomSheetLayoutParams.height = maxDesiredHeight
this.layoutParams = bottomSheetLayoutParams
BottomSheetBehavior.from(this).apply {
this.state = BottomSheetBehavior.STATE_EXPANDED
this.isDraggable = false
}
}
}
return dialog
}
- Call the setOnTouchListener function for RecyclerView:
Recycler.setOnTouchListener { v, event ->
v.parent.requestDisallowInterceptTouchEvent(true)
v.onTouchEvent(event)
true
}
Upvotes: 0
Reputation: 749
I had the same problem recently on my project and I have tried all the answer suggested in this and in other threads on SO but without luck so I decided to solve it my way.
Just a note, my situation it's that I have a BottomSheetDialog with a complex layout that result in something like this
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="_mainLayout"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<View
android:id=" />
<LinearLayout
android:id="">
<ImageView
android:id=""/>
<LinearLayout
android:id="">
<TextView
android:id="" />
<TextView
android:id=""/>
</LinearLayout>
<LinearLayout
android:id="">
<TextView
android:id=""/>
</LinearLayout>
</LinearLayout>
<android.support.v7.widget.RecyclerView
android:layout_marginTop="10dp"
android:id=""
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
<Button
android:id="">
<Button
android:id="">
</LinearLayout>
Basically what it's needed it's to add a BottomSheetBehavior to the main layout of the dialog
_bottomSheetBehavior = BottomSheetBehavior.From((View)_mainLayout.Parent);
once we have this we create a custom BottomSheetCallback implementation and we add it to the behavior
_bottomSheetCallback = new BottomSheetFullScreenCallback(_bottomSheetBehavior);
_bottomSheetBehavior.AddBottomSheetCallback(_bottomSheetCallback);
last step it's to create a custo OntouchListener that we will add to the RecyclerView
_recyclerView.SetOnTouchListener(new MyTouchListener(_bottomSheetBehavior));
Now we have everything in place and we just need to manage the touch on the recyclerView. So in our custom OnTouchListener we implement the OnTouch method in this way
public bool OnTouch(View view, MotionEvent e)
{
if (e.Action == MotionEventActions.Down || e.Action == MotionEventActions.Move)
{
_bottomSheetBehavior.Draggable = false;
}
if (e.Action == MotionEventActions.Up)
{
_bottomSheetBehavior.Draggable = true;
}
return true;
}
Once we have done this we need to be sure that the OnSlide event of the custom BottomSheetCallback will never be called once the bottomSheetBehavior it's not draggable, and we can do this in the following way:
public override void OnSlide(View bottomSheet, float slideOffset)
{
if (_bottomSheetBehavior.Draggable)
{
OnSlideEvent?.Invoke(this, slideOffset);
}
}
And that's it ;)
Upvotes: 0
Reputation: 601
I had the same situation and what worked for me is just a single line of code inside onCreate
function.
What you have to do is just enable recyclerViews nested scrolling.
recyclerView.setNestedScrollingEnabled(true);
Upvotes: 0
Reputation: 1
With Constraint layout
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_team_mates"
android:layout_width="@dimen/dp_0"
android:layout_height="@dimen/dp_0"
android:layout_marginVertical="@dimen/dp_20"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/txt_title_custom"
app:layout_constraintBottom_toBottomOf="parent"/>
Upvotes: -1
Reputation: 846
try this solution :
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/layout_form"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<!-- elements-->
<!-- elements-->
</LinearLayout>
</androidx.core.widget.NestedScrollView>
Upvotes: 3
Reputation: 998
Just add one (!) attribute to your layout:
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/receiversList"
android:nestedScrollingEnabled="false"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
android:nestedScrollingEnabled="false"
doing all the job.
Also, if you wrap around your RecyclerView
with SwipeRefreshLayout
- refresh function will continue to work. And even you put your layout with more complex Views, your BottomSheetDialog
will continue to support drag-down-to-dismiss behaviour (If touch with swipe down gesture occurs outside of RecyclerView
& SwipeRefreshLayout
).
Upvotes: 7
Reputation: 176
Try this in your Fragment
that extends RecyclerView
in onCreateView
recyclerView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
v.getParent().requestDisallowInterceptTouchEvent(true);
v.onTouchEvent(event);
return true;
}
});
Upvotes: 8
Reputation: 415
When recycle view has scope to scroll, bottom sheet will remain expanded. But if recycle view has no scroll to up direction, than drag from up, will revert normal behaviour.
behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
behavior.addBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
@Override
public void onStateChanged(@NonNull View bottomSheet, int newState) {
if (newState == BottomSheetBehavior.STATE_HIDDEN) {
dismiss();
}
if (newState == BottomSheetBehavior.STATE_DRAGGING) {
if(!recyclerView.canScrollVertically(1)) {
behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
}
}
}
@Override
public void onSlide(@NonNull View bottomSheet, float slideOffset) {
}
});
Upvotes: 2
Reputation: 703
Put your RecyclerView
under NestedScrollView
where NestedScroll is parent of recycler
Upvotes: -4
Reputation: 4701
RecyclerView scrolling problem in BottomSheetDialog can be solved by this way.
from: https://android.jlelse.eu/recyclerview-within-nestedscrollview-scrolling-issue-3180b5ad2542
<?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="match_parent"
android:orientation="vertical">
<android.support.v4.widget.NestedScrollView
android:id="@+id/nestedScrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:overScrollMode="never">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
</LinearLayout>
Upvotes: 12
Reputation: 106
Just treat it as a BottomSheetDialog , and simply disable its dragging or sliding when touch .
When create a BottomSheetDialog , it will automatically wrap your layout in a CoordinatorLayout , so if you want to get a behaviour from your view , call
final BottomSheetBehavior behavior = BottomSheetBehavior.from((View) view.getParent());
Then through this behaviour, you can do what you need.
final BottomSheetBehavior behavior = BottomSheetBehavior.from((View) view.getParent());
behavior.setHideable(false);
behavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
@Override
public void onStateChanged(@NonNull View bottomSheet, int newState) {
}
@Override
public void onSlide(@NonNull View bottomSheet, float slideOffset) {
behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
}
});
This worked for me.Hope it helps.
Upvotes: 3
Reputation: 483
Change the Behaviour in the BottomSheetDialogFragment in setupDialog method:
CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) ((View) contentView.getParent()).getLayoutParams();
final CoordinatorLayout.Behavior behavior = layoutParams.getBehavior();
if (behavior != null && behavior instanceof BottomSheetBehavior) {
((BottomSheetBehavior) behavior).setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
@Override
public void onStateChanged(@NonNull View bottomSheet, int newState) {
if (newState == BottomSheetBehavior.STATE_HIDDEN) {
dismiss();
}
if (newState == BottomSheetBehavior.STATE_DRAGGING) {
((BottomSheetBehavior) behavior).setState(BottomSheetBehavior.STATE_EXPANDED);
}
}
@Override
public void onSlide(@NonNull View bottomSheet, float slideOffset) {
}
});
}
Upvotes: 1