Android:在列表视图上垂直滑动(禁用滚动)

时间:2022-09-13 20:52:24

I've tried a lot of solutions but couldn't find anything that worked. Each element in my List view takes up the whole screen. I would like to navigate from one element to the next by using a vertical swipe gesture. My current setup right now has free scrolling, but I would like to disable this and allow only for vertical up and down swipes. Each swipe (depending on the direction) should bring me to the next consecutive element in the list view and snap into place.

我已经尝试了很多解决方案,但是找不到任何有效的方法。列表视图中的每个元素占据了整个屏幕。我希望通过使用垂直滑动手势从一个元素导航到下一个元素。我现在的设置是*滚动,但我想禁用它,只允许垂直向上和向下滑动。每一次滑动(取决于方向)都应该将我带到列表视图中的下一个连续元素,并将其对齐。

Here is my current code:

这是我现在的代码:

@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
    // TODO Auto-generated method stub
    switch (scrollState) {
    case OnScrollListener.SCROLL_STATE_IDLE:

            if (scrolling){
                // get first visible item
                  RelativeLayout item = (RelativeLayout) view.getChildAt(0);
                  int top = Math.abs(item.getTop()); // top is a negative value
                  int botton = Math.abs(item.getBottom());
                  if (top >= botton){

                     //((ListView)view).setSelectionFromTop(view.getFirstVisiblePosition()+1, 0);
                     ((ListView)view).smoothScrollToPositionFromTop(view.getFirstVisiblePosition()+1, 0, 1);

                  } else {
                         ((ListView)view).smoothScrollToPositionFromTop(view.getFirstVisiblePosition(), 0, 1);

                         }
                }

            scrolling = false;
   break;
    case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:


    case OnScrollListener.SCROLL_STATE_FLING:   
          scrolling = true;
          break;

    }
}

How can I go about achieving this?

我怎样才能做到这一点呢?

2 个解决方案

#1


4  

If you want to disable built-in scrolling - you need to trick AbsListView a bit and hide ACTION_MOVE motion events from it. In this way you can get all the touch system coming along with ListView, but scrolling will be disabled.

如果您想要禁用内置的滚动,您需要对AbsListView进行一些操作,并隐藏ACTION_MOVE动作事件。通过这种方式,您可以获得与ListView一起出现的所有触摸系统,但是滚动将被禁用。

What I'm doing in my example - is I send CANCEL event to the underlying AbsListView and process scrolling touch events manually.

我在我的示例中所做的是,将CANCEL事件发送到底层的AbsListView,并手动滚动触摸事件。

Here is what I came up with. Not an optimal solution probably, but it proves the concept :)

这是我想到的。这可能不是最佳解决方案,但它证明了这个概念:)

/**
 * Created by paveld on 1/13/14.
 */
public class CustomListView extends ListView {

    private float touchSlop = 0;
    private float downY = 0;
    private boolean consumeTouchEvents = false;

    public CustomListView(Context context) {
        super(context);
        init();
    }

    public CustomListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public CustomListView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    private void init() {
        touchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        boolean isHandled = true;
        switch (ev.getActionMasked()) {
            case MotionEvent.ACTION_MOVE:
                float distance = downY - ev.getY();
                if (!consumeTouchEvents && Math.abs(distance) > touchSlop) {

                    //send CANCEL event to the AbsListView so it doesn't scroll
                    ev.setAction(MotionEvent.ACTION_CANCEL);
                    isHandled = super.onTouchEvent(ev);
                    consumeTouchEvents = true;
                    handleScroll(distance);
                }
                break;
            case MotionEvent.ACTION_DOWN:
                consumeTouchEvents = false;
                downY = ev.getY();
                //fallthrough
            default:
                if (!consumeTouchEvents) {
                    isHandled = super.onTouchEvent(ev);
                }
                break;

        }
        return isHandled;
    }

    private void handleScroll(float distance) {
        if (distance > 0) {
            //scroll up
            if (getFirstVisiblePosition() < getAdapter().getCount() - 1) {
                smoothScrollToPositionFromTop(getFirstVisiblePosition() + 1, 0, 300);
            }
        } else {
            //scroll down
            if (getFirstVisiblePosition() > 0) {
                smoothScrollToPositionFromTop(getFirstVisiblePosition() - 1, 0, 300);
            }
        }
    }
}

#2


1  

You can always override the onTouchEvent() method of the listView (may require subclassing listView) so that you only perform your swipe detection.

您可以始终覆盖listView(可能需要子类化listView)的onTouchEvent()方法,以便只执行您的滑动检测。

Ex.

前女友。

@Override
public boolean onTouchEvent(MotionEvent motionEvent) {
    //TODO: perform the swipe detection functionality here

    //always return true so that the scrolling isn't performed by the list view
    return true; 
}

I haven't tested this so there may be irregularities with how the listView detects scrolls. If onTouchEvent() doesn't work you should also look at onInterceptTouchEvent()

我还没有测试过这个,所以可能会有一些不规范的情况,比如listView是如何检测scrolls的。如果onTouchEvent()不工作,您也应该查看onInterceptTouchEvent()

#1


4  

If you want to disable built-in scrolling - you need to trick AbsListView a bit and hide ACTION_MOVE motion events from it. In this way you can get all the touch system coming along with ListView, but scrolling will be disabled.

如果您想要禁用内置的滚动,您需要对AbsListView进行一些操作,并隐藏ACTION_MOVE动作事件。通过这种方式,您可以获得与ListView一起出现的所有触摸系统,但是滚动将被禁用。

What I'm doing in my example - is I send CANCEL event to the underlying AbsListView and process scrolling touch events manually.

我在我的示例中所做的是,将CANCEL事件发送到底层的AbsListView,并手动滚动触摸事件。

Here is what I came up with. Not an optimal solution probably, but it proves the concept :)

这是我想到的。这可能不是最佳解决方案,但它证明了这个概念:)

/**
 * Created by paveld on 1/13/14.
 */
public class CustomListView extends ListView {

    private float touchSlop = 0;
    private float downY = 0;
    private boolean consumeTouchEvents = false;

    public CustomListView(Context context) {
        super(context);
        init();
    }

    public CustomListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public CustomListView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    private void init() {
        touchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        boolean isHandled = true;
        switch (ev.getActionMasked()) {
            case MotionEvent.ACTION_MOVE:
                float distance = downY - ev.getY();
                if (!consumeTouchEvents && Math.abs(distance) > touchSlop) {

                    //send CANCEL event to the AbsListView so it doesn't scroll
                    ev.setAction(MotionEvent.ACTION_CANCEL);
                    isHandled = super.onTouchEvent(ev);
                    consumeTouchEvents = true;
                    handleScroll(distance);
                }
                break;
            case MotionEvent.ACTION_DOWN:
                consumeTouchEvents = false;
                downY = ev.getY();
                //fallthrough
            default:
                if (!consumeTouchEvents) {
                    isHandled = super.onTouchEvent(ev);
                }
                break;

        }
        return isHandled;
    }

    private void handleScroll(float distance) {
        if (distance > 0) {
            //scroll up
            if (getFirstVisiblePosition() < getAdapter().getCount() - 1) {
                smoothScrollToPositionFromTop(getFirstVisiblePosition() + 1, 0, 300);
            }
        } else {
            //scroll down
            if (getFirstVisiblePosition() > 0) {
                smoothScrollToPositionFromTop(getFirstVisiblePosition() - 1, 0, 300);
            }
        }
    }
}

#2


1  

You can always override the onTouchEvent() method of the listView (may require subclassing listView) so that you only perform your swipe detection.

您可以始终覆盖listView(可能需要子类化listView)的onTouchEvent()方法,以便只执行您的滑动检测。

Ex.

前女友。

@Override
public boolean onTouchEvent(MotionEvent motionEvent) {
    //TODO: perform the swipe detection functionality here

    //always return true so that the scrolling isn't performed by the list view
    return true; 
}

I haven't tested this so there may be irregularities with how the listView detects scrolls. If onTouchEvent() doesn't work you should also look at onInterceptTouchEvent()

我还没有测试过这个,所以可能会有一些不规范的情况,比如listView是如何检测scrolls的。如果onTouchEvent()不工作,您也应该查看onInterceptTouchEvent()