Trouble with detecting gestures over ListView
Asked Answered
H

6

10

I have an Activity that contains a ViewFlipper. The ViewFlipper includes 2 layouts, both of which are essentially just ListViews.

So the idea here is that I have two lists and to navigate from one to the other I would use a horizontal swipe.

I have that working. However, what ever list item your finger is on when the swipe begins executing, that item will also be long-clicked.

Here is the relevant code I have:

public class MyActivity extends Activity implements OnItemClickListener, OnClickListener {

    private static final int SWIPE_MIN_DISTANCE = 120;
    private static final int SWIPE_MAX_OFF_PATH = 250;
    private static final int SWIPE_THRESHOLD_VELOCITY = 200;

    private GestureDetector mGestureDetector;
    View.OnTouchListener mGestureListener;

    class MyGestureDetector extends SimpleOnGestureListener {
        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            try {
                if (Math.abs(e1.getY() - e2.getY()) > SWIPE_MAX_OFF_PATH)
                    return false;
                // right to left swipe
                if(e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
                    if (mCurrentScreen != SCREEN_SECONDLIST) {
                        mCurrentScreen = SCREEN_SECONDLIST;
                        mFlipper.setInAnimation(inFromRightAnimation());
                        mFlipper.setOutAnimation(outToLeftAnimation());
                        mFlipper.showNext();
                        updateNavigationBar();
                    }
                }
                else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
                    if (mCurrentScreen != SCREEN_FIRSTLIST) {
                        mCurrentScreen = SCREEN_FIRSTLIST;
                        mFlipper.setInAnimation(inFromLeftAnimation());
                        mFlipper.setOutAnimation(outToRightAnimation());
                        mFlipper.showPrevious();
                        updateNavigationBar();
                    }
                }
            } catch (Exception e) {
                // nothing
            }
            return true;
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (mGestureDetector.onTouchEvent(event))
            return true;
        else
            return false;
    }





    ViewFlipper mFlipper;

    private int mCurrentScreen = SCREEN_FIRSTLIST;

    private ListView mList1;
    private ListView mList2;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.layout_flipper);

        mFlipper = (ViewFlipper) findViewById(R.id.flipper);

        mGestureDetector = new GestureDetector(new MyGestureDetector());
        mGestureListener = new View.OnTouchListener() {
            public boolean onTouch(View v, MotionEvent event) {
                if (mGestureDetector.onTouchEvent(event)) {
                    return true;
                }
                return false;
            }
        };

        // set up List1 screen

        mList1List = (ListView)findViewById(R.id.list1);
        mList1List.setOnItemClickListener(this);
        mList1List.setOnTouchListener(mGestureListener);

        // set up List2 screen
        mList2List = (ListView)findViewById(R.id.list2);
        mList2List.setOnItemClickListener(this);
        mList2List.setOnTouchListener(mGestureListener);

    }

    …
}

If I change the "return true;" statement from the GestureDetector to "return false;", I do not get long-clicks. Unfortunately, I get regular clicks.

Does anyone know how I can get around this?

Halimeda answered 13/1, 2011 at 20:58 Comment(0)
A
7

Just to throw in a completely different answer with a completely different approach...

I've made a custom view called SwipeView, it's open source etc etc blah blah blah. It should let you do what you want to do as simply as going:

mSwipeView.addChild(myList1);
mSwipeView.addChild(myList2);

You won't have to mess about with gesture detectors or anything, and the the swipe should follow your finger as you do it...

(This reads like a terrible terrible advert, please excuse me. It's been a long day ;)

Have some wonderful links:

http://jasonfry.co.uk/?id=23

http://jasonfry.co.uk/?id=24

https://jasonfry.co.uk/blog/android-swipeview-update/

Adore answered 18/1, 2011 at 21:28 Comment(3)
This is similar to a component I recently developed, except that I added view recycling to mine and its backed by a listAdapter. Basically, I kanged a bunch of ListView code and attached it to a custom gesture driven HorizontalScrollView as you did.Weigh
Yeah I'm working on adding that to SwipeView too! Also, I just (literally just, about 2 minutes ago) noticed that there is a bug with ListViews on a SwipeView. I have now fixed the bug and updated the git repo. github.com/fry15/uk.co.jasonfry.android.toolsAdore
jfelectron is yours on GIThub though?Crammer
S
1

Since you're using a GestureDetector, the thing to do is implement SimpleGestureDetector.onLongPress(MotionEvent e) for handling long clicks. However, you can't know the listitem that's been long-pressed from there, so you need to remember it from the normal onItemLongClick().

class MyGestureDetector extends SimpleOnGestureListener {
    ...
    @Override public void onLongPress(MotionEvent e) {
        if (longClickedItem != -1) {
           Toast.makeText(MainActivity.this, "Long click!", Toast.LENGTH_LONG).show();
        }           
    }
}

int longClickedItem = -1;

@Override
public boolean onItemLongClick(AdapterView<?> list, View view, int position, long id) {
    longClickedItem = position;
    return true;
}

@Override
public boolean onTouchEvent(MotionEvent event) {

    // Reset longclick item if new touch is starting
    if (event.getAction()==MotionEvent.ACTION_DOWN) {
        longClickedItem = -1;
    }

    if (mGestureDetector.onTouchEvent(event))
        return true;
    else
        return false;
}

I have tested this and it seems to work fine.

Somatotype answered 17/1, 2011 at 21:7 Comment(2)
Thank you for your response. I will give it a try. I should note that while a long press is what is being activated and is what I've noted, what I'd really like to happen is have no gesture leak if someone is swiping. There are a few other issues, especially with Android phones that can do the iPhone end-of-list animations and such. The real problem here is that swiping is sending other gestures to the list; and I don't know how to stop that.Halimeda
I've just tried this and it does prevent the long click from executing when I swipe, though the item your finger originates on does highlight yellow; so some piece of the gesture is obviously leaking to the listHalimeda
F
1

Go you your gestureDetector and set

 mGestureDetector.setIsLongpressEnabled(false);

This will prevent long press events from being detected.

Finial answered 19/1, 2011 at 11:30 Comment(0)
I
0

[EDIT]Scratch that, totally wrong.

I think you would have to update this part, and detect if you were scrolling left or right, and return true if you were in a swiping state. Or at least that is where i would look first.

 mGestureListener = new View.OnTouchListener() {
            public boolean onTouch(View v, MotionEvent event) {
                if (mGestureDetector.onTouchEvent(event)) {
                    return true;
                }
                return false;
            }
        };

Continuing with my poor example, sorry.

Within the onTouch i believe you can test the event.getAction() and determine if a long click has occured. If it has, and your in a flinging motion, then return true as to capture the long click.

Afraid this is more of a suggestion than a definitive answer.

Also have you check the other on[methods] that you can override from the SimpleOnGestureListener. Just checked and

@Override
public void onLongPress(MotionEvent e) {
    // TODO Auto-generated method stub
    super.onLongPress(e);
}

might be something you could experiment with.

Icy answered 13/1, 2011 at 21:43 Comment(2)
That's what is happening. Or is your example what your EDIT is intending to invalidate?Halimeda
Sorry, its a terrible example, been fixing another issue. Will try and explain better later.Icy
W
0

I developed a custom component derived from horizontalScrollView that does this. I have listViews as the child views and swipe without long-presses. It backed by a listAdapter, which could be anything you want (ArrayAdapter, CursorAdapter...).It loads up to three child views and then on flipping, just swaps them on either side. Most of this is kanged from ListView and needs to be refactored. Its way too long to post here directly.

http://pastie.org/1480091

Weigh answered 20/1, 2011 at 9:15 Comment(0)
V
0

Remove your getListView().setOnItemLongClickListener

Add in your class MyGestureDetector :

public void onLongPress(MotionEvent e) {
    final int position = getListView().pointToPosition((int) e.getX(),
            (int) e.getY());
    // what you want do
    super.onLongPress(e);
}
Vasoinhibitor answered 12/11, 2014 at 10:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.