Как найти исходное представление MotionEvent ACTION_CANCEL

Как найти представление, вызывающее MotionEvent ACTION_CANCEL? У меня есть представление "A", которое получает ACTION_CANCEL, и я не хочу, чтобы это произошло. Где-то представление "B" "потребляет" MotionEvent. Я надеюсь, что есть способ узнать, кто такой "Б", чтобы я мог исправить эту неточность.

Я пытался просмотреть свой код для различных обработчиков OnTouchEvent() и OnInterceptTouchEvent(), но еще не нашел виновника.

Я также поставил точку останова на проблемном ACTION_CANCEL, но не могу распознать в MotionEvent ничего, что могло бы представлять «B».


person Peri Hartman    schedule 21.07.2015    source источник
comment
Получаете ли вы начальное событие ACTION_DOWN?   -  person Barend    schedule 21.07.2015
comment
Да и серия ACTION_MOVE.   -  person Peri Hartman    schedule 21.07.2015
comment
Я собирался спросить, не могли бы вы просто заглянуть в представление под начальным событием, но за это время понял, что это не поможет, так что не обращайте внимания, извините.   -  person Barend    schedule 21.07.2015
comment
не могли бы вы показать нам код, с которого мы можем начать в отношении вашей проблемы.   -  person Elltz    schedule 03.09.2015
comment
Этот вопрос не зависит от моего кода. Это применимо к любому коду, использующему MotionEvent. Просто поставьте точку останова там, где вы обрабатываете ACTION_CANCEL. Тогда мой вопрос: как определить, кто вызвал ACTION_CANCEL? Это может происходить не из какого-либо кода, который вы написали!   -  person Peri Hartman    schedule 03.09.2015
comment
Для тех, кому интересно: я нашел виновника. Это был ScrollView, который находился в родительской иерархии. Он остался от более раннего подхода к проектированию и не был нужен, поэтому я его убрал. Presto - ACTION_CANCEL исчезли!   -  person Peri Hartman    schedule 04.09.2015
comment
Если кто-то из Google читает это, обратите внимание: пожалуйста, добавьте что-нибудь в MotionEvent, отслеживающее источник ACTION_CANCEL !!!!!!!!!   -  person Peri Hartman    schedule 04.09.2015


Ответы (3)


Если родитель перехватывает событие движения, единственный способ предотвратить это — запретить родителю перехватывать это событие. С этим можно хорошо справиться двумя способами.

Не видя конкретного кода и не желая получить обобщенное решение, я бы предложил следующее.

Я бы предложил управлять событиями касания для родителей и ребенка, управляя
requestDisallowInterceptTouchEvent(boolean) и

onInterceptTouchEvent(android.view.MotionEvent) обработчики событий каждого представления/группы представлений в затронутых представлениях A, B C.

Запрещая родительские перехваты в дочернем, это помогает вам перехватывать родительские перехваты, которые вы не учли, а также настраивать и изменять дочерние элементы внутри одного родителя.

Это должно управляться из вашего самого высокого родителя вашего представления/группы просмотра и управляться через все родительские и дочерние отношения.

Проверка списков, любого элемента со встроенными сенсорными событиями.

android.com/training/gestures/viewgroup

С точки зрения поиска оскорбительного представления, которое перехватывает событие, на которое нельзя ответить, кроме как по логике:

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

Просмотреть диаграмму

В этих ответах есть более подробная информация:
https://stackoverflow.com/a/30966413/3956566
https://stackoverflow.com/a/6384443/3956566

Я уверен, что вы понимаете это, но для меня это самое простое решение.

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

person Community    schedule 04.09.2015
comment
Что ж, похоже, что ответа на мой вопрос нет прямого метода. Другими словами, к моменту получения ACTION_CANCEL каким-либо произвольным представлением источник ACTION_CANCEL теряется. Очень плохо. Я действительно ценю предложенную вами методологию, и на самом деле это более или менее то, что я делал - в основном просматривал цепочку родителей, чтобы увидеть, кто может отменить. Очень мало моего кода (это макеты и виджеты Android), поэтому становится трудно пробираться через чужой исходный код, чтобы размышлять о происхождении ACTION_CANCEL. - person Peri Hartman; 04.09.2015
comment
Я действительно не чувствую, что вы ответили на вопрос, но вы подошли ближе всех и приложили большие усилия. Я тоже ненавижу видеть, как щедрость тратится впустую. Итак, ура! - person Peri Hartman; 04.09.2015

Если я правильно понял ваш вопрос, вы получаете ACTION_CANCEL, вероятно, в родительском элементе, и вам нужно найти это представление. Учитывая события X и Y, вы можете найти представление, содержащее эти координаты в первый момент, когда произошло ACTION_CANCEL. Попробуйте вызвать этот метод либо с верхним родителем (android.R.id.content), либо с ViewGroup, с которым вы имеете дело.

private View findViewAt(View contentView, int eventX, int eventY) {
            ArrayList<View> unvisited = new ArrayList<View>();
            unvisited.add(contentView);
            while (!unvisited.isEmpty()) {
                View child = unvisited.remove(0);
                if(isViewContains(child, eventX, eventY) {
                    Log.i(TAG, "view found! ");
                    unvisited.clear();
                    return child;
                }
                if (!(child instanceof ViewGroup)){
                    continue;
                }
                ViewGroup group = (ViewGroup) child;
                final int childCount = group.getChildCount();
                for (int i=0; i< childCount; i++){
                    unvisited.add(group.getChildAt(i));
                }
            }
      return null;
    }


private boolean isViewContains(View view, int eventX, int eventY) {
    int[] location = new int[2];
    view.getLocationOnScreen(location);
    int x = location[0];
    int y = location[1];
    int width = view.getWidth();
    int height = view.getHeight();
    return eventX < x || eventX > x + width || eventY < y || eventY > y + height;
}
person Nikola Despotoski    schedule 21.07.2015
comment
Это хорошая идея. Однако два комментария. (1) Разве eventX, eventY не являются локальными координатами представления? Для работы isViewContains() нам нужны экранные координаты. (2) Я думаю, что алгоритм можно выполнить напрямую с обходом дерева - нет необходимости в очереди - или я что-то упустил. - person Peri Hartman; 21.07.2015
comment
Это будет ходить по всем представлениям в иерархии. - person Nikola Despotoski; 21.07.2015
comment
Согласен, хотя пункт 2 остается в силе. В любом случае, пункт 1 является решающим. Если eventX, eventY находятся в локальных координатах, я не вижу способа определить, из какого представления они пришли. - person Peri Hartman; 22.07.2015
comment
Если они локальные, то можно попытаться найти координаты относительно экрана, заданного Rect вида, которому даны координаты касания события. - person Nikola Despotoski; 22.07.2015
comment
Это было бы неплохо, но как найти прямоугольник для представления? Я внимательно посмотрел на MotionEvent и не вижу такого метода. - person Peri Hartman; 22.07.2015
comment
X относительно экрана должно быть view.getLeft() + eventCancel.getX(), а y — view.getTop() + eventCancel.getY(), где view — это представление, в котором произошло первое действие ACTION_DOWN. Обратите внимание, что это всего лишь идея. - person Nikola Despotoski; 23.07.2015
comment
Я так не думаю. getLeft() и т. д. относятся к родительскому представлению. Кроме того, я думаю, что у вас круговая логика. Я думаю, что этот путь будет тупиковым. Я ценю ваши мысли и дал вам +1. - person Peri Hartman; 24.07.2015
comment
Я не уверен, как этот алгоритм может вернуть 100% точный ответ. Что, если в представлении A есть представление B, а в представлении B есть представление C. Либо представление B, либо представление C могли зафиксировать событие, и если представление B и представление C имеют перекрывающиеся прямоугольники, этот алгоритм может вернуть неверный ответ. . - person Gil Moshayof; 30.08.2015
comment
и добавить в @GilMoshayof, если пользователь указывает, что действие не перерисовывает себя после изменения ориентации (поворот устройства) getLocationOnScreen будет возвращать случайные числа - person Elltz; 01.09.2015

По моему опыту, случай, который заставляет представление получать событие ACTION_CANCEL, происходит после того, как пользователи касаются A, и они вытаскивают палец из области A. помощь.

person Livekus    schedule 02.09.2015