Pergunta

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?

Foi útil?

Solução

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.

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

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

Outras dicas

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

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()

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top