底部工作表中的列表视图

25

我遇到了一个问题,我在一个BottomSheet中放置了一个简单的ListView,而且ListView有足够的项来填满屏幕并可以向下滚动。

当我向下滚动时,一切似乎都正常,但是当我尝试向上滚动时,它会滚动BottomSheet本身并关闭视图,而不是只滚动ListView

经过一段时间的寻找,我终于找到了解决方案,由于我无法在任何地方找到它,所以我决定在这里发布。

9个回答

31

解决方案是将 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();
这将提供一个与 ListView 滚动完全兼容的 BottomSheet

嘿,伙计,不错的解决方案,但列表项无法点击,有什么技巧吗? - sUndeep
1
@sUndeep,我可以点击列表项。你的列表项布局里有ImageViewButton吗?如果有,你需要在列表项布局的根元素中添加android:descendantFocusability="blocksDescendants" - ᴛʜᴇᴘᴀᴛᴇʟ
我没有写android:descendantFocusability="blocksDescendants"就完成了它。但还需要更多的改进。我在评论中发布了代码。 - sUndeep
我的列表项也无法点击。有解决方案吗? - Zappy.Mans
2
太好了,谢谢!使用ExpandableListView进行测试并且正常工作。@PratikSaluja 如果您不想扩展一个类,您可以在列表视图中设置一个OnTouchListener,这也能很好地工作。顺便提一下,在这里:管理ViewGroup中的触摸事件 解释说,在onInterceptTouchEvent返回false时,子视图将处理事件。也许这可能是无法点击项目的问题的解决方案。 - marcRDZ
显示剩余4条评论

17

如果您不想扩展 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;
            }
        });

11

版本22.1.0开始,您可能希望尝试setNestedScrollingEnabled=true

如果将此属性设置为true,则该视图将被允许在当前层次结构中与兼容的父视图启动嵌套滚动操作。如果此视图未实现嵌套滚动,则不会产生任何效果。在进行嵌套滚动时禁用嵌套滚动会停止嵌套滚动。

参考 Google API


这应该是被接受的答案! - Patrick
这是最近库的正确答案。 - racs

8
这是一个正确的列表视图自定义类,具有触摸事件处理。
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;
    }
}

完美答案!! - Nishant Chauhan

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

@th3pat3l请查看我在代码中的评论。如果你能改进它,那就太好了,否则我会处理的。我从https://dev59.com/Po_ea4cB1Zd3GeqPLDbu上得到了灵感。 - sUndeep

4

只需在布局中将android:nestedScrollingEnabled设置为true,或者在Java中使用listView.setNestedScrollingEnabled(true)来将其应用于ListView属性。


3

我知道这有点hack,但对我有效,而且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);
}

2

我觉得我有点晚了,但请在您的listview中使用nested scroll启用true,它将帮助您解决向下滚动的问题。

<ListView
android:id="@+id/rv_jobs_offers"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nestedScrollingEnabled="true"
android:layout_marginTop="@dimen/dimens_20"/>

1
我尝试过在此处描述的嵌套ListView,但它们不允许在触摸ListView时展开/折叠底部工作表。以下是我的解决方案:当您向上滚动且ListView无法向上滚动时,底部工作表将展开;当您向下滚动且ListView无法滚动时,BottomSheet将被折叠。
public class NestedListView extends ListView {

    private boolean mIsBeingDragged = false;
    private float mLastMotionY;

    public NestedListView(final Context context, final AttributeSet attrs) {
        super(context, attrs);
    }

    public NestedListView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        switch (event.getActionMasked()) {
            case MotionEvent.ACTION_DOWN: {
                if (canScrollDown() || canScrollUp()) {
                    final ViewParent parent = getParent();
                    if (parent != null) {
                        parent.requestDisallowInterceptTouchEvent(true);
                    }
                }
                break;
            }
        }
        return super.onInterceptTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        final float y = event.getRawY();

        switch (event.getActionMasked()) {
            case MotionEvent.ACTION_MOVE: {
                if (!mIsBeingDragged) {
                    final float deltaY = mLastMotionY - y;
                    mIsBeingDragged = (deltaY > 0 && canScrollDown())
                        || (deltaY < 0 && canScrollUp());

                    if (mIsBeingDragged) {
                        final ViewParent parent = getParent();
                        if (parent != null) {
                            parent.requestDisallowInterceptTouchEvent(true);
                        }
                    } else {
                        final ViewParent parent = getParent();
                        if (parent != null) {
                            parent.requestDisallowInterceptTouchEvent(false);
                        }
                        return false;
                    }
                }
            }

            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                mIsBeingDragged = false;
                break;
        }

        mLastMotionY = y;

        return super.onTouchEvent(event);
    }

    public boolean canScrollUp() {
        final int childCount = getChildCount();
        if (childCount == 0) {
            return false;
        }

        final int firstPosition = getFirstVisiblePosition();
        final int firstTop = getChildAt(0).getTop();
        return firstPosition > 0 || firstTop < getListPaddingTop();
    }

    public boolean canScrollDown() {
        final int childCount = getChildCount();
        if (childCount == 0) {
            return false;
        }

        final int firstPosition = getFirstVisiblePosition();
        final int lastBottom = getChildAt(childCount - 1).getBottom();
        final int lastPosition = firstPosition + childCount;
        return lastPosition < getCount() || lastBottom > getHeight() - getListPaddingBottom();
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接