Qual é o significado do valor booleano retornado de um método de manipulação de eventos no Android

110

No Android, a maioria dos métodos de ouvinte de evento retorna um valor booleano. O que significa esse valor verdadeiro / falso? no que isso resultará nos eventos subsequentes?

class MyTouchListener implements OnTouchListener {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        logView.showEvent(event);
        return true;
    }
}

Em relação ao exemplo acima, se retornar verdadeiro no método onTouch , descobri que todos os eventos de toque (DOWN, UP, MOVE, etc) foram capturados de acordo com meu logView . Ao contrário, se retornar falso, apenas o evento DOWN foi capturado. Portanto, parece que o retorno falso impedirá a propagação do evento. Estou correcto ?

Além disso, em um OnGestureListener , muitos métodos também precisam retornar um valor booleano. Eles têm o mesmo significado?

John Wang
fonte

Respostas:

140

Se você retornar truede um ACTION_DOWNevento, estará interessado no restante dos eventos naquele gesto. Um "gesto" neste caso significa todos os eventos até o final ACTION_UPou ACTION_CANCEL. Retornar falsede um ACTION_DOWNsignifica que você não deseja o evento e outras visualizações terão a oportunidade de tratá-lo. Se você tiver visualizações sobrepostas, pode ser uma visualização irmã. Do contrário, irá borbulhar para os pais.

adamp
fonte
3
adamp, existe uma maneira de continuar recebendo eventos E permitir que eles passem?
ticofab
@ticofab não, apenas um pai da visualização que recebe eventos no momento pode interceptar eventos futuros no gesto. (Claro, você sempre pode construir seus próprios sistemas de redirecionamento em uma visualização pai, mas eu não recomendaria a menos que você realmente saiba o que está fazendo. :))
adamp
@adamp Não consigo pensar no motivo pelo qual onTouch é chamado 2 vezes ao retornar true e 1 vez apenas quando retorno false.
Bhargav Jhaveri
1
@adamp: Estou retornando falso de ACTION_DOWN, mas meu ACTION_UP está sendo acionado e executado.
Mahantesh M Ambi,
Estou recebendo esta resposta errada ou todo mundo está ... Esta resposta disse que retornar verdadeiro significa que o evento difícil não é consumido. Mas a verdade é completamente reversa.
Kai Wang
12

O valor booleano determina se o evento é consumido ou não.

Sim, você está correto. Se você retornar falso, o próximo ouvinte tratará do evento. Se retornar verdadeiro, o evento é consumido por seu ouvinte e não é enviado para o próximo método.

Falmarri
fonte
2
Isto é falso. truesignifica que você consumiu o evento e deseja o restante dos eventos no gesto - outros ouvintes / visualizações não receberão os eventos. falsesignifica deixar outra pessoa cuidar do evento. Na verdade, é um pouco mais específico do que isso; veja minha resposta.
adamp
Como não foi exatamente isso que eu disse?
Falmarri,
1
O que você disse está invertido. :)
adamp em
4

Todas as respostas acima estão corretas, mas o resultado é diferente se a visão é clickableou nãoclickable

Exemplo , eu tenho um LinearLayoutcontém 1 Buttone 1 TextViewcomo este

<LinearLayout
    android:id="@+id/linearlayout_root"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#0aa"
    android:orientation="vertical">

    <Button
        android:id="@+id/button_click"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="40dp"
        android:text="Button Click"
        android:textSize="20sp" />

    <TextView
        android:id="@+id/textview_click"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="40dp"
        android:text="TextView Click"
        android:textSize="20sp"
        android:background="#e4e4e4"
        />

</LinearLayout>

Em Activity, tenho um código como

class MainActivity : AppCompatActivity() {
    val TAG = "TAG"

    @SuppressLint("ClickableViewAccessibility")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        findViewById<LinearLayout>(R.id.linearlayout_root).setOnTouchListener { v, event ->
            Log.i(TAG, "LinearLayout onTouch event " + getDisplayAction(event.action))
            false
        }

        findViewById<Button>(R.id.button_click).setOnTouchListener { v, event ->
            Log.i(TAG, "Button onTouch event " + getDisplayAction(event.action))
            false
        }

        findViewById<TextView>(R.id.textview_click).setOnTouchListener { v, event ->
            Log.i(TAG, "TextView onTouch event " + getDisplayAction(event.action))
            false
        }
    }

    private fun getDisplayAction(action: Int): String {
        return when (action) {
            MotionEvent.ACTION_DOWN -> "DOWN"
            MotionEvent.ACTION_MOVE -> "MOVE"
            MotionEvent.ACTION_UP -> "UP"
            MotionEvent.ACTION_CANCEL -> "CANCEL"
            MotionEvent.ACTION_OUTSIDE -> "OUTSIDE"
            else -> "UNKNOWN"
        }
    }
}

Caso 1 Linear onTouch return **FALSE**, Button onTouch return **FALSE**,TextView onTouch return **FALSE**

Clique no botão

I/TAG: Button onTouch eventDOWN
I/TAG: Button onTouch eventMOVE
I/TAG: Button onTouch eventUP

Clique em TextView

TAG: TextView onTouch eventDOWN
TAG: LinearLayout onTouch eventDOWN

Clique em LinearLayout

TAG: LinearLayout onTouch eventDOWN

Caso 2 Linear onTouch return **FALSE**, Button onTouch return **TRUE**,TextView onTouch return **TRUE**

Clique no botão

Similar to case 1

Clique em TextView

TAG: TextView onTouch event DOWN
TAG: TextView onTouch event MOVE
TAG: TextView onTouch event UP

Clique em LinearLayout

Similar to case 1

Caso 3 Linear onTouch return **TRUE**, Button onTouch return **FALSE**,TextView onTouch return **FALSE**

Clique no botão

Similar to case 1

Clique em TextView

TAG: TextView onTouch event DOWN
TAG: LinearLayout onTouch event DOWN
TAG: LinearLayout onTouch event MOVE
TAG: LinearLayout onTouch event UP

Clique em LinearLayout

TAG: LinearLayout onTouch event DOWN
TAG: LinearLayout onTouch event MOVE
TAG: LinearLayout onTouch event UP

Nota

  • O padrão TextViewé not clickable, ele se tornará clicável se definirmos android:clickable="true"em xml OU quando definirmostextView.setOnClickListener(...)
  • Quando você depurar, event MOVEpode chamar mais do que meu log (baseado em como você toca)

Resumo

  • onTouchretornar trueou visualizar é clickable , Visualizar receberá todos onTouchEvent
  • onTouchreturn falseand view não é clickable, view não receberá PRÓXIMO onTouchEvent (seu pai pode recebê-lo)

Espero que ajude
DEMO

Phan Van Linh
fonte
1
Esta deve ser a resposta! Obrigado por uma explicação tão detalhada
Mysterious_android
1

Perdi quase um dia na solução de problemas, mas descobri que minha função onTouch é chamada 2 vezes quando uso true e 1 vez quando uso false.

kamor
fonte
Você poderia descobrir o motivo?
Bhargav Jhaveri
verifique event.getAction()porque se você retornar falseno evento ACTION_DOWN, o evento ACTION_UP será ignorado pelo listener
doodeec
0

Do documento Android :

Nota: o Android chamará os manipuladores de eventos primeiro e, em seguida, os manipuladores padrão apropriados da definição de classe. Como tal, retornar true a partir desses ouvintes de eventos interromperá a propagação do evento para outros ouvintes de eventos e também bloqueará o retorno de chamada para o manipulador de eventos padrão na Visualização. Portanto, certifique-se de que deseja encerrar o evento quando retornar verdadeiro.

Huy Nguyen
fonte