Почему Android думает, что я создаю мультисенсорное событие?

Дано: точка зрения A, которая поддерживает точку зрения B.

мой макет

Действие: я касаюсь и удерживаю вид A пальцем 1, а затем пальцем 2 — вид B. Затем отпускаю и та же операция, но сначала вид B, а затем вид A. И я ожидаю, что просмотры получат похожие события в обоих случаях.

Что происходит: когда я касаюсь и удерживаю A пальцем 1, а затем касаюсь и удерживаю B пальцем 2, A и B получают два отдельных MotionEvents, каждый из которых содержит координаты ACTION_MOVE для одного указателя:

Логкат:

-----first finger down on view A-------
    ACTION_DOWN[#0 (pid 0)=897,392]
    
    ----action move for first finger----
    ACTION_MOVE[#0 (pid 0)=897,392]
    ACTION_MOVE[#0 (pid 0)=897,392]
    ACTION_MOVE[#0 (pid 0)=897,392]
    ACTION_MOVE[#0 (pid 0)=895,392]
    ACTION_MOVE[#0 (pid 0)=896,393]
    ACTION_MOVE[#0 (pid 0)=896,394]
    ACTION_MOVE[#0 (pid 0)=896,395]
    ACTION_MOVE[#0 (pid 0)=896,396]
    ACTION_MOVE[#0 (pid 0)=895,396]
    ACTION_MOVE[#0 (pid 0)=895,396]
    
    ---second finger down on view B-------
    ACTION_DOWN[#0 (pid 1)=224,87]
    
    ---action move for 1st finger----
    ACTION_MOVE[#0 (pid 0)=895,396]
    
    ---action move for 2nd finger----
    ACTION_MOVE[#0 (pid 1)=224,87]
    
    --first--
    ACTION_MOVE[#0 (pid 0)=895,396]
    
    --second--
    ACTION_MOVE[#0 (pid 1)=224,87]
    
    etc . . .
    ACTION_MOVE[#0 (pid 0)=895,397]
    ACTION_MOVE[#0 (pid 1)=224,87]
    ACTION_MOVE[#0 (pid 0)=895,397]
    ACTION_MOVE[#0 (pid 1)=224,87]
    ACTION_MOVE[#0 (pid 0)=895,397]
    ACTION_MOVE[#0 (pid 1)=223,87]
    ACTION_MOVE[#0 (pid 0)=895,397]
    
        ...

НО, когда я сначала касаюсь B, а затем A - Android думает, что это событие multi-touch, и начинает отправлять объект MotionEvent, который содержит координаты ACTION_MOVE для 2 указателей для просмотра только Б.

Логкат:

   ----------holding finger at view B------------------
    event ACTION_MOVE[#0 (pid 0)=65,33]
    event ACTION_MOVE[#0 (pid 0)=62,33]
    event ACTION_MOVE[#0 (pid 0)=60,33]
    event ACTION_MOVE[#0 (pid 0)=58,33]
    event ACTION_MOVE[#0 (pid 0)=56,32]
    event ACTION_MOVE[#0 (pid 0)=55,32]
    event ACTION_MOVE[#0 (pid 0)=54,32]
    event ACTION_MOVE[#0 (pid 0)=54,32]
    event ACTION_MOVE[#0 (pid 0)=53,32]
    event ACTION_MOVE[#0 (pid 0)=52,32]
    event ACTION_MOVE[#0 (pid 0)=52,32]
    event ACTION_MOVE[#0 (pid 0)=51,32]
    event ACTION_MOVE[#0 (pid 0)=51,33]
    
    ----------press on view A with 2nd finger, while holding B with first finger-------------
    event ACTION_POINTER_DOWN(pid 1); [#0 (pid 0)=51,33; #1 (pid 1)=1050,-226]
    
    ---------action move obj that holds coords for 2 pointers...---------
    event ACTION_MOVE[#0 (pid 0)=50,33;#1 (pid 1)=1055,-225]
    event ACTION_MOVE[#0 (pid 0)=48,34;#1 (pid 1)=1068,-223]
    event ACTION_MOVE[#0 (pid 0)=47,34;#1 (pid 1)=1082,-224]
    event ACTION_MOVE[#0 (pid 0)=45,35;#1 (pid 1)=1106,-221]
    event ACTION_MOVE[#0 (pid 0)=42,35;#1 (pid 1)=1131,-219]
    event ACTION_MOVE[#0 (pid 0)=38,35;#1 (pid 1)=1157,-217]
    event ACTION_MOVE[#0 (pid 0)=34,35;#1 (pid 1)=1178,-215]
    event ACTION_MOVE[#0 (pid 0)=27,36;#1 (pid 1)=1195,-213]
    event ACTION_MOVE[#0 (pid 0)=20,37;#1 (pid 1)=1211,-213]
    event ACTION_MOVE[#0 (pid 0)=11,39;#1 (pid 1)=1228,-212]

Что я хочу: я хочу отдельные MotionEvents (первый случай) в обоих сценариях (сначала A, затем B; и сначала B, затем A)

Как я пытался это решить:

  1. Я попытался обработать только 1 набор координат в представлении B onTouch(), а затем вернуть false, а затем использовать событие в представлении A, НО в этом сценарии я не получаю ничего, кроме ACTION_DOWN в представлении B.

  2. Я попытался отправить событие в действии в методе dispatchTouchEvent(), а затем вручную вызвать методы представления A и представления B dispatchTouchEvent(), но это не удалось. Я пытался перейти на ViewGroup (родительский макет), но тоже не получилось =( onTouch не вызывается.

Но эти 2 мои попытки я считаю HACK, я хотел бы, чтобы ОС переключилась с мультитач на отдельные сенсорные события. Вы знаете, как это сделать?


person StayCool    schedule 04.03.2021    source источник
comment
Предоставьте исходный код метода onTouch() для обоих представлений.   -  person emandt    schedule 11.03.2021
comment
Вы пытались сделать тот же тест, разгруппировав представление B из представления A, добавив B выше A (изменив Z-порядок вместо B, чтобы он был дочерним элементом A), и проверьте, получили ли вы те же результаты?   -  person MariosP    schedule 12.03.2021
comment
Просто некоторые разъяснения по A и B: я предполагаю, что B является дочерним элементом A. Является ли B представлением или ViewGroup? Где каждое представление в настоящее время фиксирует поток событий (onTouch(), onTouchEvent(), onInterceptTouchEvent() или displatchTouchEvent())? Я нашел это полезным.   -  person Cheticamp    schedule 12.03.2021
comment
@MariosP на самом деле, A и B - это отдельные представления. И они находятся на одном уровне иерархии, но да, B выше A, а A ниже B. Я проведу предложенные вами тесты и вернусь   -  person StayCool    schedule 13.03.2021
comment
@Cheticamp нет, A и B - это отдельные виды, и они не связаны друг с другом. Одна деталь: B находится выше A и ниже B. Оба представления имеют OnTouchListener, поэтому я обрабатываю касания точно в методе прослушивателя onTouch. Спасибо за ссылку. Я уже читал, очень полезно в целом, но не для этого конкретного случая.   -  person StayCool    schedule 13.03.2021


Ответы (2)


Я только что создал новый проект, установил макет как ваш, а затем установил другое событие onTouch() для обоих представлений. Я не могу воспроизвести вашу ситуацию. Если оба события onTouch() возвращают TRUE, результаты следующие:

//DOWN for A
onTouchA(MotionEvent { action=ACTION_DOWN, actionButton=0, id[0]=0, x[0]=382.0, y[0]=175.0, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=10987450, downTime=10987450, deviceId=5, source=0x1002 })
onTouchA(MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=365.0, y[0]=186.0, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=10993036, downTime=10987450, deviceId=5, source=0x1002 })
//DOWN for B
onTouchB(MotionEvent { action=ACTION_DOWN, actionButton=0, id[0]=1, x[0]=409.0, y[0]=260.0, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=10995795, downTime=10987450, deviceId=5, source=0x1002 })
onTouchA(MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=363.0, y[0]=184.0, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=10995795, downTime=10987450, deviceId=5, source=0x1002 })
onTouchB(MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=1, x[0]=409.0, y[0]=256.0, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=10995888, downTime=10987450, deviceId=5, source=0x1002 })
onTouchA(MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=363.0, y[0]=184.0, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=10995888, downTime=10987450, deviceId=5, source=0x1002 })
onTouchB(MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=1, x[0]=409.8291, y[0]=254.1709, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=1, eventTime=10995909, downTime=10987450, deviceId=5, source=0x1002 })
//UP for A
onTouchA(MotionEvent { action=ACTION_UP, actionButton=0, id[0]=0, x[0]=361.0, y[0]=191.0, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=10998121, downTime=10987450, deviceId=5, source=0x1002 })
onTouchB(MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=1, x[0]=410.0, y[0]=234.0, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=10998156, downTime=10987450, deviceId=5, source=0x1002 })
onTouchB(MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=1, x[0]=410.0, y[0]=235.0, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=10998285, downTime=10987450, deviceId=5, source=0x1002 })
onTouchB(MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=1, x[0]=409.0, y[0]=235.0, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=10998460, downTime=10987450, deviceId=5, source=0x1002 })
onTouchB(MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=1, x[0]=408.0, y[0]=235.0, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=10998530, downTime=10987450, deviceId=5, source=0x1002 })
//UP for B
onTouchB(MotionEvent { action=ACTION_UP, actionButton=0, id[0]=1, x[0]=405.0, y[0]=235.0, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=10998846, downTime=10987450, deviceId=5, source=0x1002 })

=================================

//DOWN for B
onTouchB(MotionEvent { action=ACTION_DOWN, actionButton=0, id[0]=0, x[0]=241.0, y[0]=195.0, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=11125227, downTime=11125227, deviceId=5, source=0x1002 })
onTouchB(MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=221.0, y[0]=196.0, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=11126010, downTime=11125227, deviceId=5, source=0x1002 })
onTouchB(MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=220.0, y[0]=196.0, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=11126044, downTime=11125227, deviceId=5, source=0x1002 })
//DOWN for A
onTouchA(MotionEvent { action=ACTION_DOWN, actionButton=0, id[0]=1, x[0]=347.0, y[0]=156.0, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=11128089, downTime=11125227, deviceId=5, source=0x1002 })
onTouchB(MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=216.0, y[0]=196.0, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=11128089, downTime=11125227, deviceId=5, source=0x1002 })
onTouchA(MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=1, x[0]=347.0, y[0]=156.0, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=11129071, downTime=11125227, deviceId=5, source=0x1002 })
onTouchB(MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=216.0, y[0]=195.0, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=11129071, downTime=11125227, deviceId=5, source=0x1002 })
onTouchA(MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=1, x[0]=343.0, y[0]=156.0, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=11130531, downTime=11125227, deviceId=5, source=0x1002 })
//UP for B
onTouchB(MotionEvent { action=ACTION_UP, actionButton=0, id[0]=0, x[0]=219.91565, y[0]=194.0, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=11132305, downTime=11125227, deviceId=5, source=0x1002 })
onTouchA(MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=1, x[0]=337.0, y[0]=156.0, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=11133099, downTime=11125227, deviceId=5, source=0x1002 })
onTouchA(MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=1, x[0]=337.0, y[0]=157.0, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=11133228, downTime=11125227, deviceId=5, source=0x1002 })
//UP for A
onTouchA(MotionEvent { action=ACTION_UP, actionButton=0, id[0]=1, x[0]=337.0, y[0]=157.0, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=11133240, downTime=11125227, deviceId=5, source=0x1002 })

XML:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<TextView
    android:id="@+id/textView"
    android:layout_width="300dp"
    android:layout_height="400dp"
    android:background="@color/teal_200"
    android:text="Hello World!"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintHorizontal_bias="0.495"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintVertical_bias="0.498" />
<TextView
    android:id="@+id/textView2"
    android:layout_width="200dp"
    android:layout_height="200dp"
    android:text="Hello World!"
    android:background="@color/teal_700"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

Код основной активности:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    TextView cTextView = findViewById(R.id.textView);
    cTextView.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            Log.e(TAG, "onTouchA("+event.toString()+")");
            return true;
        }
    });

    TextView cTextView2 = findViewById(R.id.textView2);
    cTextView2.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            Log.e(TAG, "onTouchB("+event.toString()+")");
            return false;
        }
    });
}
person emandt    schedule 15.03.2021
comment
Не могли бы вы создать репозиторий git, и я его протестирую? - person StayCool; 16.03.2021
comment
Скачать: emandt.net/Android/MyApplication.zip - person emandt; 16.03.2021

Чтобы предотвратить проблемы с мультитач, вы можете просто отключить атрибут splitMotionEvents или windowEnableSplitTouch внутри ViewGroup.

person Jessica Neo    schedule 04.03.2021
comment
Мне не нужно предотвращать событие мультитач, мне нужно, чтобы андроид разделял события движения, чтобы каждый ребенок получал только свой указатель. И когда я делаю splitMotionEvent = false -- я больше не могу использовать 2 вида одновременно. Когда я устанавливаю для флага значение false, я вижу, что события каким-то образом дублируются между представлениями, теперь тестируем brb) - person StayCool; 05.03.2021
comment
Хорошо, я отключил splitMotionEvents. Что дальше?) Я получаю все события в одном месте, не разбивая их. И вид А никогда не получит свои события =) - person StayCool; 05.03.2021