ListView в BottomSheet

Я столкнулся с проблемой, когда у меня было простое ListView в BottomSheet и ListView было достаточно элементов для заполнения экрана и прокрутки еще больше.

Когда я прокручиваю вниз, все работает, однако, когда я пытался прокручивать назад, он прокручивал сам BottomSheet и закрывал представление, а не просто прокручивал ListView.

Мне удалось найти решение через некоторое время, и поскольку я не мог найти его здесь, я решил, что разместил его здесь.

Ответ 1

Решение состоит в том, чтобы расширить ListView следующим образом:

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;
    }
}

Затем в вашем файле макета 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>

Затем, наконец, в 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();

Это обеспечит BottomSheet, который полностью работает с прокруткой ListView.

Ответ 2

Есть лучший подход, если вы не хотите расширять 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;
            }
        });

Ответ 3

Это правильный пользовательский класс представления списка с сенсорным событием

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;
    }
}

Ответ 4

public class BottomSheetListView extends ListView {

    public BottomSheetListView(Context context, AttributeSet attributeSet) {
        super(context, attributeSet);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent motionEvent) {
        View view = (View) getChildAt(getChildCount() - 1);

        int diffBottom = (view.getBottom() - (getHeight() + getScrollY()));
        if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
            if (diffBottom == 0) {
                return false;
            }
        }

         /*//Need more improvement on this logic. Do not uncomment
        int diffTop = (view.getTop() - (getHeight() + getScrollY()));
        if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
            if (diffTop < 0) {
                return true;
            }
        }*/

        return super.onInterceptTouchEvent(motionEvent);
    }

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

    public boolean canScrollVertically(AbsListView absListView) {

        boolean canScroll = false;

        if (absListView != null && absListView.getChildCount() > 0) {

            boolean isOnTop = absListView.getFirstVisiblePosition() != 0 || absListView.getChildAt(0).getTop() != 0;
            boolean isAllItemsVisible = isOnTop && getLastVisiblePosition() == absListView.getChildCount();

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

        return canScroll;
    }
}

Ответ 5

Я знаю это немного взломать, но это сработало для меня, и даже listview можно выбрать. Как мы знаем, когда когда-либо нижний лист меняет свое состояние, его можно поймать в этом слушателе, поэтому не позволяйте ему изменять состояние, если listview не касается вершины, впредь сенсорное событие будет передано в listview и будет работать так, как оно есть.

    mBottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback(){
@Override
public void onStateChanged(View bottomSheet, int newState) {
        if (newState == BottomSheetBehavior.STATE_DRAGGING && !listIsAtTop()){
            mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
        }
}
@Override
public void onSlide(View bottomSheet, float slideOffset) {}});

public boolean listIsAtTop()   {
    if(tripListView.getChildCount() == 0) return true;
    return (tripListView.getChildAt(0).getTop() == 0 && tripListView.getFirstVisiblePosition() ==0);
}

Ответ 6

Начиная с версии 22.1.0, вы можете попробовать setNestedScrollingEnabled=true

Если для этого свойства установлено значение true, представлению будет разрешено инициировать вложенные операции прокрутки с совместимым родительским представлением в текущей иерархии. Если это представление не реализует вложенную прокрутку, это не будет иметь никакого эффекта. Отключение вложенной прокрутки во время выполнения вложенной прокрутки приводит к остановке вложенной прокрутки.

Ссылка на Google API