Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ListView in BottomSheet

I ran into a problem where I had a simple ListView in a BottomSheet and ListView had enough items to fill the screen and scroll even more.

When I scroll down, everything seems to work however when I tried to scroll back up, it was scrolling the BottomSheet itself and closing the view instead of just scrolling the ListView.

I was able to find a solution after a while and since I couldn't find it anywhere here, I figured I would post it here.

like image 528
ᴛʜᴇᴘᴀᴛᴇʟ Avatar asked Nov 13 '16 05:11

ᴛʜᴇᴘᴀᴛᴇʟ


4 Answers

The solution is to extend the ListView like this:

public class BottomSheetListView extends ListView {
    public BottomSheetListView (Context context, AttributeSet p_attrs) {
        super (context, p_attrs);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return true;
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (canScrollVertically(this)) {
            getParent().requestDisallowInterceptTouchEvent(true);
        }
        return super.onTouchEvent(ev);
    }

    public boolean canScrollVertically (AbsListView view) {
        boolean canScroll = false;

        if (view !=null && view.getChildCount ()> 0) {
            boolean isOnTop = view.getFirstVisiblePosition() != 0 || view.getChildAt(0).getTop() != 0;
            boolean isAllItemsVisible = isOnTop && view.getLastVisiblePosition() == view.getChildCount();

            if (isOnTop || isAllItemsVisible) {
                canScroll = true;
            }
        }

        return  canScroll;
    }
}

Then in your layout file bottom_sheet_view.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.mypackage.name.BottomSheetListView
        android:id="@+id/listViewBtmSheet"
        android:divider="@color/colorPrimary"
        android:dividerHeight="1dp"
        android:layout_weight="1"
        android:layout_width="match_parent"
        android:layout_height="0dp" />

</LinearLayout>

Then finally, in your Activity/Fragment:

BottomSheetDialog dialog = new BottomSheetDialog(context);
dialog.setContentView(R.layout.bottom_sheet_view);

BottomSheetListView listView = (BottomSheetListView) dialog.findViewById(R.id.listViewBtmSheet);
// apply some adapter - add some data to listview

dialog.show();

This will provide a BottomSheet that is fully working with ListView scroll.

like image 55
ᴛʜᴇᴘᴀᴛᴇʟ Avatar answered Nov 15 '22 05:11

ᴛʜᴇᴘᴀᴛᴇʟ


There is a better approach if you don't want to extend the ListView:

//in onCreate

_listView.setOnTouchListener(new ListView.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                int action = event.getAction();
                switch (action) {
                    case MotionEvent.ACTION_DOWN:
                        // Disallow NestedScrollView to intercept touch events.
                        v.getParent().requestDisallowInterceptTouchEvent(true);
                        break;

                    case MotionEvent.ACTION_UP:
                        // Allow NestedScrollView to intercept touch events.
                        v.getParent().requestDisallowInterceptTouchEvent(false);
                        break;
                }

                // Handle ListView touch events.
                v.onTouchEvent(event);
                return true;
            }
        });
like image 28
okkko Avatar answered Nov 15 '22 07:11

okkko


This is the correct list view custom class with touch event hadnling

public class BottomSheetListView extends ListView
{
    public BottomSheetListView(Context context, AttributeSet p_attrs)
    {
        super(context, p_attrs);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev)
    {
        if (canScrollVertically(this))
        {
            getParent().requestDisallowInterceptTouchEvent(true);
        }
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev)
    {
        if (canScrollVertically(this))
        {
            getParent().requestDisallowInterceptTouchEvent(true);
        }
        return super.onTouchEvent(ev);
    }

    public boolean canScrollVertically(AbsListView view)
    {
        boolean canScroll = false;

        if (view != null && view.getChildCount() > 0)
        {
            boolean isOnTop = view.getFirstVisiblePosition() != 0 || view.getChildAt(0).getTop() != 0;
            boolean isAllItemsVisible = isOnTop && view.getLastVisiblePosition() == view.getChildCount();

            if (isOnTop || isAllItemsVisible)
            {
                canScroll = true;
            }
        }

        return canScroll;
    }
}
like image 43
Nauman Afzaal Avatar answered Nov 15 '22 07:11

Nauman Afzaal


Starting from version 22.1.0, you might want to try setNestedScrollingEnabled=true

If this property is set to true the view will be permitted to initiate nested scrolling operations with a compatible parent view in the current hierarchy. If this view does not implement nested scrolling this will have no effect. Disabling nested scrolling while a nested scroll is in progress has the effect of stopping the nested scroll.

Reference to Google API

like image 45
longi Avatar answered Nov 15 '22 06:11

longi