Проблема с обнаружением жестов над ListView

У меня есть Activity, который содержит ViewFlipper. ViewFlipper включает в себя 2 макета, оба из которых по существу являются только ListViews.

Итак, идея здесь состоит в том, что у меня есть два списка и для перехода от одного к другому я использовал бы горизонтальный сабпорт.

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

Вот соответствующий код, который у меня есть:

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

    }

    …
}

Если я изменю "return true"; оператора GestureDetector, чтобы "вернуть false", я не получаю длинные клики. К сожалению, я получаю регулярные клики.

Кто-нибудь знает, как я могу обойти это?

Ответ 1

Просто для того, чтобы полностью ответить на совершенно другой подход...

Я сделал собственное представление под названием SwipeView, его открытый источник и т.д. и т.д. бла-бла-бла. Это должно позволить вам делать то, что вы хотите сделать так же просто, как и вы:

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

Вам не нужно будет возиться с детекторами жестов или чем-то еще, и салфетка должна следовать за вашим пальцем, как вы это делаете...

(Это читается как ужасное ужасное объявление, пожалуйста, извините меня. Это был долгий день;)

Есть несколько замечательных ссылок:

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

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

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

Ответ 2

Поскольку вы используете GestureDetector, нужно выполнить SimpleGestureDetector.onLongPress(MotionEvent e) для обработки длинных кликов. Тем не менее, вы не можете знать список, который был долго нажат оттуда, поэтому вам нужно запомнить его из обычного 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;
}

Я тестировал это и, похоже, работает нормально.

Ответ 3

Перейдите к вашему жестовДетектор и установите

 mGestureDetector.setIsLongpressEnabled(false);

Это предотвратит обнаружение событий длительного события.

Ответ 4

[EDIT] Поцарапайте это, совершенно неправильно.

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

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

Продолжая мой плохой пример, извините.

Внутри onTouch я считаю, что вы можете проверить event.getAction() и определить, произошел ли длинный клик. Если он есть и ваш в движении, то верните true, чтобы зафиксировать длинный клик.

Боюсь, что это скорее предложение, чем окончательный ответ.

Также вы проверяете другой метод [методы], который вы можете переопределить из SimpleOnGestureListener. Просто проверено и

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

может быть чем-то, с чем вы могли бы экспериментировать.

Ответ 5

Я разработал пользовательский компонент, созданный из horizontalScrollView, который делает это. У меня есть listViews как вид ребенка и проведите пальцем без длинного нажатия. Он поддерживает списокAdapter, который может быть любым, что вы хотите (ArrayAdapter, CursorAdapter...). Он загружает до трех дочерних представлений, а затем переворачивает, просто свопит их по обе стороны. Большая часть из них отличается от ListView и нуждается в рефакторинге. Его путь слишком длинный, чтобы публиковать здесь напрямую.

http://pastie.org/1480091

Ответ 6

Удалите getListView().setOnItemLongClickListener

Добавьте в свой класс MyGestureDetector:

public void onLongPress(MotionEvent e) {
    final int position = getListView().pointToPosition((int) e.getX(),
            (int) e.getY());
    // what you want do
    super.onLongPress(e);
}