Android: определить, касается ли пользователь области перетаскивания из области кнопок? - PullRequest
49 голосов
/ 20 июня 2011

В Android, как мы можем определить, касается ли пользователь кнопки и перетаскивает ли ее область за пределы области?

Ответы [ 8 ]

87 голосов
/ 09 ноября 2011

Проверьте MotionEvent.MOVE_OUTSIDE: Проверьте MotionEvent.MOVE:

private Rect rect;    // Variable rect to hold the bounds of the view

public boolean onTouch(View v, MotionEvent event) {
    if(event.getAction() == MotionEvent.ACTION_DOWN){
        // Construct a rect of the view's bounds
        rect = new Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());

    }
    if(event.getAction() == MotionEvent.ACTION_MOVE){
        if(!rect.contains(v.getLeft() + (int) event.getX(), v.getTop() + (int) event.getY())){
            // User moved outside bounds
        }
    }
    return false;
}

ПРИМЕЧАНИЕ. Если вы хотите использовать Android 4.0, откроется целый мир новых возможностей: http://developer.android.com/reference/android/view/MotionEvent.html#ACTION_HOVER_ENTER

21 голосов
/ 04 января 2013

Ответ, опубликованный Entreco, нуждался в небольшой доработке в моем случае. Я должен был заменить:

if(!rect.contains((int)event.getX(), (int)event.getY()))

для

if(!rect.contains(v.getLeft() + (int) event.getX(), v.getTop() + (int) event.getY()))

, поскольку event.getX() и event.getY() применяются только к самому ImageView, а не ко всему экрану.

5 голосов
/ 25 мая 2015

Я добавил несколько записей в свой OnTouch и обнаружил, что MotionEvent.ACTION_CANCEL находится под ударом. Это достаточно хорошо для меня ...

4 голосов
/ 26 марта 2017

У меня была та же проблема, что и у ОП, когда я хотел узнать, когда (1) был нажат конкретный View, а также (2), когда нажатие на сенсорное нажатие на View или (3)когда прикосновение вниз вышло за пределы View.Я собрал различные ответы в этой теме, чтобы создать простое расширение View.OnTouchListener (названное SimpleTouchListener), чтобы другим не пришлось возиться с объектом MotionEvent.Источник для класса можно найти здесь или внизу этого ответа.

Чтобы использовать этот класс, просто установите его в качестве единственного параметра метода View.setOnTouchListener(View.OnTouchListener) следующим образом:

myView.setOnTouchListener(new SimpleTouchListener() {

    @Override
    public void onDownTouchAction() {
        // do something when the View is touched down
    }

    @Override
    public void onUpTouchAction() {
        // do something when the down touch is released on the View
    }

    @Override
    public void onCancelTouchAction() {
        // do something when the down touch is canceled
        // (e.g. because the down touch moved outside the bounds of the View
    }
});

Вот источник класса, который вы можете добавить в свой проект:

public abstract class SimpleTouchListener implements View.OnTouchListener {

    /**
     * Flag determining whether the down touch has stayed with the bounds of the view.
     */
    private boolean touchStayedWithinViewBounds;

    @Override
    public boolean onTouch(View view, MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                touchStayedWithinViewBounds = true;
                onDownTouchAction();
                return true;

            case MotionEvent.ACTION_UP:
                if (touchStayedWithinViewBounds) {
                    onUpTouchAction();
                }
                return true;

            case MotionEvent.ACTION_MOVE:
                if (touchStayedWithinViewBounds
                        && !isMotionEventInsideView(view, event)) {
                    onCancelTouchAction();
                    touchStayedWithinViewBounds = false;
                }
                return true;

            case MotionEvent.ACTION_CANCEL:
                onCancelTouchAction();
                return true;

            default:
                return false;
        }
    }

    /**
     * Method which is called when the {@link View} is touched down.
     */
    public abstract void onDownTouchAction();

    /**
     * Method which is called when the down touch is released on the {@link View}.
     */
    public abstract void onUpTouchAction();

    /**
     * Method which is called when the down touch is canceled,
     * e.g. because the down touch moved outside the bounds of the {@link View}.
     */
    public abstract void onCancelTouchAction();

    /**
     * Determines whether the provided {@link MotionEvent} represents a touch event
     * that occurred within the bounds of the provided {@link View}.
     *
     * @param view  the {@link View} to which the {@link MotionEvent} has been dispatched.
     * @param event the {@link MotionEvent} of interest.
     * @return true iff the provided {@link MotionEvent} represents a touch event
     * that occurred within the bounds of the provided {@link View}.
     */
    private boolean isMotionEventInsideView(View view, MotionEvent event) {
        Rect viewRect = new Rect(
                view.getLeft(),
                view.getTop(),
                view.getRight(),
                view.getBottom()
        );

        return viewRect.contains(
                view.getLeft() + (int) event.getX(),
                view.getTop() + (int) event.getY()
        );
    }
}
2 голосов
/ 14 апреля 2015

Лучшие 2 ответа хороши, за исключением случаев, когда представление находится внутри вида прокрутки: когда прокрутка происходит, потому что вы двигаете пальцем, она все равно регистрируется как событие касания, но не как событие MotionEvent.ACTION_MOVE.Итак, чтобы улучшить ответ (который необходим, только если ваш взгляд находится внутри элемента прокрутки):

private Rect rect;    // Variable rect to hold the bounds of the view

public boolean onTouch(View v, MotionEvent event) {
    if(event.getAction() == MotionEvent.ACTION_DOWN){
        // Construct a rect of the view's bounds
        rect = new Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());

    } else if(rect != null && !rect.contains(v.getLeft() + (int) event.getX(), v.getTop() + (int) event.getY())){
        // User moved outside bounds
    }
    return false;
}

Я проверял это на Android 4.3 и Android 4.4

Я не заметил ни одногоРазличия между ответом Морица и топ-2, но это касается и его ответа:

private Rect rect;    // Variable rect to hold the bounds of the view

public boolean onTouch(View v, MotionEvent event) {
    if(event.getAction() == MotionEvent.ACTION_DOWN){
        // Construct a rect of the view's bounds
        rect = new Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());

    } else if (rect != null){
        v.getHitRect(rect);
        if(rect.contains(
                Math.round(v.getX() + event.getX()),
                Math.round(v.getY() + event.getY()))) {
            // inside
        } else {
            // outside
        }
    }
    return false;
}
1 голос
/ 07 октября 2015
view.setClickable(true);
view.setOnTouchListener(new OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        if (!v.isPressed()) {
            Log.e("onTouch", "Moved outside view!");
        }
        return false;
    }
});

view.isPressed использует view.pointInView и включает некоторое касание.Если вам не нужен отстой, просто скопируйте логику из внутреннего view.pointInView (который является открытым, но скрытым, поэтому не является частью официального API и может исчезнуть в любое время).

1 голос
/ 19 января 2015

Вот View.OnTouchListener, который вы можете использовать, чтобы увидеть, было ли отправлено MotionEvent.ACTION_UP, когда пользователь держал палец вне поля зрения:

private OnTouchListener mOnTouchListener = new View.OnTouchListener() {

    private Rect rect;

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        if (v == null) return true;
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            rect = new Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
            return true;
        case MotionEvent.ACTION_UP:
            if (rect != null
                    && !rect.contains(v.getLeft() + (int) event.getX(),
                        v.getTop() + (int) event.getY())) {
                // The motion event was outside of the view, handle this as a non-click event

                return true;
            }
            // The view was clicked.
            // TODO: do stuff
            return true;
        default:
            return true;
        }
    }
};
1 голос
/ 17 октября 2014

Хотя ответ @FrostRocket правильный, вы должны использовать view.getX () и Y для учета изменений перевода:

 view.getHitRect(viewRect);
 if(viewRect.contains(
         Math.round(view.getX() + event.getX()),
         Math.round(view.getY() + event.getY()))) {
   // inside
 } else {
   // outside
 }
...