Обнаружение долгого нажатия с Android - PullRequest
67 голосов
/ 27 октября 2011

В настоящее время я использую

onTouchEvent(MotionEvent event){
}

, чтобы определить, когда пользователь нажимает мой glSurfaceView, и есть ли способ определить, когда сделан длинный щелчок.Я предполагаю, что если я не смогу найти что-нибудь в документации для разработчиков, то это будет своего рода обходной метод.Что-то вроде регистрации ACTION_DOWN и определения продолжительности ACTION_UP.

Как вы обнаруживаете длительные нажатия на Android с помощью opengl-es?

Ответы [ 10 ]

148 голосов
/ 27 июля 2012

GestureDetector - лучшее решение.

Вот интересная альтернатива. В onTouchEvent на каждом ACTION_DOWN запланируйте запуск Runnable в течение 1 секунды. На каждом ACTION_UP или ACTION_MOVE отмените запланированный Runnable. Если отмена произошла менее чем 1 с из события ACTION_DOWN , Runnable не запустится.

final Handler handler = new Handler(); 
Runnable mLongPressed = new Runnable() { 
    public void run() { 
        Log.i("", "Long press!");
    }   
};

@Override
public boolean onTouchEvent(MotionEvent event, MapView mapView){
    if(event.getAction() == MotionEvent.ACTION_DOWN)
        handler.postDelayed(mLongPressed, ViewConfiguration.getLongPressTimeout());
    if((event.getAction() == MotionEvent.ACTION_MOVE)||(event.getAction() == MotionEvent.ACTION_UP))
        handler.removeCallbacks(mLongPressed);
    return super.onTouchEvent(event, mapView);
}
101 голосов
/ 27 октября 2011

Попробуйте это:

final GestureDetector gestureDetector = new GestureDetector(new GestureDetector.SimpleOnGestureListener() {
    public void onLongPress(MotionEvent e) {
        Log.e("", "Longpress detected");
    }
});

public boolean onTouchEvent(MotionEvent event) {
    return gestureDetector.onTouchEvent(event);
};
5 голосов
/ 22 марта 2015

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

//Declare this flag globally
boolean goneFlag = false;

//Put this into the class
final Handler handler = new Handler(); 
    Runnable mLongPressed = new Runnable() { 
        public void run() { 
            goneFlag = true;
            //Code for long click
        }   
    };

//onTouch code
@Override
    public boolean onTouch(View v, MotionEvent event) {
        switch (event.getAction()) {    
        case MotionEvent.ACTION_DOWN:
            handler.postDelayed(mLongPressed, 1000);
            //This is where my code for movement is initialized to get original location.
            break;
        case MotionEvent.ACTION_UP:
            handler.removeCallbacks(mLongPressed);
            if(Math.abs(event.getRawX() - initialTouchX) <= 2 && !goneFlag) {
                //Code for single click
                return false;
            }
            break;
        case MotionEvent.ACTION_MOVE:
            handler.removeCallbacks(mLongPressed);
            //Code for movement here. This may include using a window manager to update the view
            break;
        }
        return true;
    }

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

3 голосов
/ 27 октября 2011

Когда вы имеете в виду нажатия пользователя, вы имеете в виду щелчок?Щелчок - это когда пользователь нажимает, а затем сразу же поднимает палец вверх.Поэтому он охватывает два события onTouch.Вы должны сохранить использование onTouchEvent для вещей, которые происходят при первом касании или после выпуска.

Таким образом, вы должны использовать onClickListener, если это щелчок.Используйте onLongClickListener.

2 голосов
/ 05 февраля 2017

Я создал фрагмент - вдохновленный фактическим источником просмотра - который надежно обнаруживает длинные щелчки / нажатия с пользовательской задержкой.Но это в Котлине:

val LONG_PRESS_DELAY = 500

val handler = Handler()
var boundaries: Rect? = null

var onTap = Runnable {
    handler.postDelayed(onLongPress, LONG_PRESS_DELAY - ViewConfiguration.getTapTimeout().toLong())
}

var onLongPress = Runnable {

    // Long Press
}

override fun onTouch(view: View, event: MotionEvent): Boolean {
    when (event.action) {
        MotionEvent.ACTION_DOWN -> {
            boundaries = Rect(view.left, view.top, view.right, view.bottom)
            handler.postDelayed(onTap, ViewConfiguration.getTapTimeout().toLong())
        }
        MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
            handler.removeCallbacks(onLongPress)
            handler.removeCallbacks(onTap)
        }
        MotionEvent.ACTION_MOVE -> {
            if (!boundaries!!.contains(view.left + event.x.toInt(), view.top + event.y.toInt())) {
                handler.removeCallbacks(onLongPress)
                handler.removeCallbacks(onTap)
            }
        }
    }
    return true
}
1 голос
/ 16 мая 2018

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

Вам также необходимо знать, когда использовался длинный щелчок и когда он отменен из-за слишком большого перемещения пальца. Мы используем initialTouchX & initialTouchY для проверки, если пользователь выходит из квадратной области 10 пикселей, 5 с каждой стороны.

Вот мой полный код для делегирования Click & LongClick с Cell в ListView до Activity с OnTouchListener:

    ClickDelegate delegate; 
    boolean goneFlag = false;
    float initialTouchX;
    float initialTouchY;
    final Handler handler = new Handler();
    Runnable mLongPressed = new Runnable() {
        public void run() {
            Log.i("TOUCH_EVENT", "Long press!");
            if (delegate != null) {
                goneFlag = delegate.onItemLongClick(index);
            } else {
                goneFlag = true;
            }
        }
    };

    @OnTouch({R.id.layout})
    public boolean onTouch (View view, MotionEvent motionEvent) {
        switch (motionEvent.getAction()) {
            case MotionEvent.ACTION_DOWN:
                handler.postDelayed(mLongPressed, ViewConfiguration.getLongPressTimeout());
                initialTouchX = motionEvent.getRawX();
                initialTouchY = motionEvent.getRawY();
                return true;
            case MotionEvent.ACTION_MOVE:
            case MotionEvent.ACTION_CANCEL:
                if (Math.abs(motionEvent.getRawX() - initialTouchX) > 5 || Math.abs(motionEvent.getRawY() - initialTouchY) > 5) {
                    handler.removeCallbacks(mLongPressed);
                    return true;
                }
                return false;
            case MotionEvent.ACTION_UP:
                handler.removeCallbacks(mLongPressed);
                if (goneFlag || Math.abs(motionEvent.getRawX() - initialTouchX) > 5 || Math.abs(motionEvent.getRawY() - initialTouchY) > 5) {
                    goneFlag = false;
                    return true;
                }
                break;
        }
        Log.i("TOUCH_EVENT", "Short press!");
        if (delegate != null) {
            if (delegate.onItemClick(index)) {
                return false;
            }
        }
        return false;
    }

ClickDelegate - это interface для отправки событий щелчка в класс обработчика, например Activity

    public interface ClickDelegate {
        boolean onItemClick(int position);
        boolean onItemLongClick(int position);
    }

И все, что вам нужно, это реализовать его в вашем Activity или родительском View, если вам нужно делегировать поведение:

public class MyActivity extends Activity implements ClickDelegate {

    //code...
    //in some place of you code like onCreate, 
    //you need to set the delegate like this:
    SomeArrayAdapter.delegate = this;
    //or:
    SomeViewHolder.delegate = this;
    //or:
    SomeCustomView.delegate = this;

    @Override
    public boolean onItemClick(int position) {
        Object obj = list.get(position);
        if (obj) {
            return true; //if you handle click
        } else {
            return false; //if not, it could be another event
        }
    }

    @Override
    public boolean onItemLongClick(int position) {
        Object obj = list.get(position);
        if (obj) {
            return true; //if you handle long click
        } else {
            return false; //if not, it's a click
        }
    }
}
1 голос
/ 17 августа 2015

Решение от MSquare работает только в том случае, если вы держите определенный пиксель, но это неоправданное ожидание для конечного пользователя, если только он не использует мышь (а они не используют пальцы).

Поэтому я добавил немного порога для расстояния между ВНИЗ и действием ВВЕРХ, если между ними было действие ПЕРЕМЕЩЕНИЕ.

final Handler longPressHandler = new Handler();
Runnable longPressedRunnable = new Runnable() {
    public void run() {
        Log.e(TAG, "Long press detected in long press Handler!");
        isLongPressHandlerActivated = true;
    }
};

private boolean isLongPressHandlerActivated = false;

private boolean isActionMoveEventStored = false;
private float lastActionMoveEventBeforeUpX;
private float lastActionMoveEventBeforeUpY;

@Override
public boolean dispatchTouchEvent(MotionEvent event) {
    if(event.getAction() == MotionEvent.ACTION_DOWN) {
        longPressHandler.postDelayed(longPressedRunnable, 1000);
    }
    if(event.getAction() == MotionEvent.ACTION_MOVE || event.getAction() == MotionEvent.ACTION_HOVER_MOVE) {
        if(!isActionMoveEventStored) {
            isActionMoveEventStored = true;
            lastActionMoveEventBeforeUpX = event.getX();
            lastActionMoveEventBeforeUpY = event.getY();
        } else {
            float currentX = event.getX();
            float currentY = event.getY();
            float firstX = lastActionMoveEventBeforeUpX;
            float firstY = lastActionMoveEventBeforeUpY;
            double distance = Math.sqrt(
                    (currentY - firstY) * (currentY - firstY) + ((currentX - firstX) * (currentX - firstX)));
            if(distance > 20) {
                longPressHandler.removeCallbacks(longPressedRunnable);
            }
        }
    }
    if(event.getAction() == MotionEvent.ACTION_UP) {
        isActionMoveEventStored = false;
        longPressHandler.removeCallbacks(longPressedRunnable);
        if(isLongPressHandlerActivated) {
            Log.d(TAG, "Long Press detected; halting propagation of motion event");
            isLongPressHandlerActivated = false;
            return false;
        }
    }
    return super.dispatchTouchEvent(event);
}
0 голосов
/ 17 июля 2019

Я нашел одно решение, и оно не требует определения работоспособности или других вещей, и оно работает нормально.

    var lastTouchTime: Long = 0

    // ( ViewConfiguration.#.DEFAULT_LONG_PRESS_TIMEOUT =500)
    val longPressTime = 500

    var lastTouchX = 0f
    var lastTouchY = 0f

    view.setOnTouchListener { v, event ->

        when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                lastTouchTime = SystemClock.elapsedRealtime()
                lastTouchX = event.x
                lastTouchY = event.y
                return@setOnTouchListener true
            }
            MotionEvent.ACTION_UP -> {
                if (SystemClock.elapsedRealtime() - lastTouchTime > longPressTime
                        && Math.abs(event.x - lastTouchX) < 3
                        && Math.abs(event.y - lastTouchY) < 3) {
                    Log.d(TAG, "Long press")
                }
                return@setOnTouchListener true
            }
            else -> {
                return@setOnTouchListener false
            }
        }

    }
0 голосов
/ 30 января 2019

Вот подход, основанный на прекрасной идее MSquare для обнаружения длительного нажатия кнопки, который имеет дополнительную функцию: операция не только выполняется в ответ на долгое нажатие, но операция повторяется до MotionEvent.ACTION_UP сообщение получено.В этом случае действия длинного и короткого нажатия одинаковы, но они могут быть разными.

Обратите внимание, что, как сообщали другие, удаление обратного вызова в ответ на сообщение MotionEvent.ACTION_MOVE препятствовало выполнению обратного вызова, поскольку я не мог держать палец достаточно неподвижно.Я обошел эту проблему, проигнорировав это сообщение.

private void setIncrementButton() {
    final Button btn = (Button) findViewById(R.id.btn);
    final Runnable repeater = new Runnable() {
        @Override
        public void run() {
            increment();
            final int milliseconds = 100;
            btn.postDelayed(this, milliseconds);
        }
    };
    btn.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent e) {
            if (e.getAction() == MotionEvent.ACTION_DOWN) {
                increment();
                v.postDelayed(repeater, ViewConfiguration.getLongPressTimeout());
            } else if (e.getAction() == MotionEvent.ACTION_UP) {
                v.removeCallbacks(repeater);
            }
            return true;
        }
    });
}

private void increment() {
    Log.v("Long Press Example", "TODO: implement increment operation");   
}
0 голосов
/ 27 марта 2016
setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {

                int action = MotionEventCompat.getActionMasked(event);


                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        longClick = false;
                        x1 = event.getX();
                        break;

                    case MotionEvent.ACTION_MOVE:
                        if (event.getEventTime() - event.getDownTime() > 500 && Math.abs(event.getX() - x1) < MIN_DISTANCE) {
                            longClick = true;
                        }
                        break;

                    case MotionEvent.ACTION_UP:
                                if (longClick) {
                                    Toast.makeText(activity, "Long preess", Toast.LENGTH_SHORT).show();
                                } 
                }
                return true;
            }
        });
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...