Подтвердить что ты не робот

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

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

4b9b3361

Ответ 1

Проверьте 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 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

Ответ 2

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

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, а не ко всему экрану.

Ответ 3

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

Ответ 4

Лучшие 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 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 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;
}

Ответ 5

В то время как ответ от @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
 }

Ответ 6

Вот 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;
        }
    }
};

Ответ 7

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 и включает в себя некоторые касания. Если вы не хотите slop, просто скопируйте логику из внутреннего view.pointInView (который является общедоступным, но скрытым, поэтому он не является частью официального API и может исчезнуть в любое время).

view.setClickable(true);
view.setOnTouchListener(new OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            v.setTag(true);
        } else {
            boolean pointInView = event.getX() >= 0 && event.getY() >= 0
                    && event.getX() < (getRight() - getLeft())
                    && event.getY() < (getBottom() - getTop());
            boolean eventInView = ((boolean) v.getTag()) && pointInView;
            Log.e("onTouch", String.format("Dragging currently in view? %b", pointInView));
            Log.e("onTouch", String.format("Dragging always in view? %b", eventInView));
            v.setTag(eventInView);
        }
        return false;
    }
});

Ответ 8

У меня была та же проблема, что и OP, когда мне хотелось знать, когда (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()
        );
    }
}