Aquellos que no entienden el proceso de envío de eventos de Android View lo han visto ~

Desde que Joe lanzó el iPhone, las operaciones táctiles se han convertido en el método de entrada estándar para dispositivos inteligentes en el siglo XXI. Para Android, que también es un sistema operativo inteligente, no es una excepción. Los eventos, especialmente los eventos táctiles, son muy importantes para el desarrollo de aplicaciones móviles y los desarrolladores deben dominarlos. Aquí discutiremos el sistema de eventos en Android View, enfocándonos en el proceso de envío de eventos.

Resumen de eventos de entrada

Clasificación de eventos

Para el sistema Android, los eventos de entrada del usuario se dividen en dos categorías, una es KeyEvent, que es un evento generado por hardware, o más exactamente, un evento generado por un gesto no táctil, que generalmente incluye botones de hardware como teclas de volumen, encendido las teclas, el sistema de navegación (INICIO, ATRÁS y MENÚ) y los periféricos (como dispositivos externos, teclados, selfie sticks, etc.) las capas del sistema también se asignarán de manera uniforme y se convertirán en KeyEvents y se enviarán a la ventana de la ventana actual (proceso actual) .

Hay otra categoría que se refiere específicamente a los eventos de gestos táctiles generados al no controlar la pantalla. Es MotionEvent. ¿Por qué no se llama TouchEvent? Porque la versión original de Android admite bolas deslizantes. No existe tal dispositivo ahora, pero el nombre es solo así. Este evento es manejado especialmente por el árbol de vistas del sistema de vistas, y este artículo también se centrará en tales eventos.

de donde viene el evento

En pocas palabras, los eventos se originan en el hardware, como pantallas o botones. Esto no tiene sentido. Saber esto no tiene sentido. Después de que el hardware genera una señal electrónica, se transmitirá al kernel a través del controlador y el kernel lo informará. al sistema de entrada y luego a wms (Windows Manager Server), eventualmente llegará a Window aquí. Para la capa de aplicación, se puede entender que todos los eventos provienen del objeto Ventana.

quien recibe el evento primero

Para la capa de la aplicación GUI, wms es el origen del evento, por lo que el objeto ViewRootImpl es el primero en recibir el evento. ViewRootImpl no envía directamente el evento al árbol de vistas, sino a DecorView primero. El componente host tiene un objeto Recibir el evento dedicado. devolución de llamada del evento, y luego el evento llega al componente host actual, como Actividad o Diálogo, para ver si está dispuesto a procesarlo. Si no lo procesa, el evento se enviará al sistema de visualización de la GUI, es decir, Esta vez, en lugar de pasar por el objeto ViewRootImpl, el objeto Window llama directamente al dispatchTouchEvent o dispatchKeyEvent del nodo raíz.

   15:57:02.254   W/System.err: java.lang.Exception: Stack trace
   15:57:02.255   W/System.err:     at java.lang.Thread.dumpStack(Thread.java:1348)
   15:57:02.256   W/System.err:     at net.toughcoder.view.ViewWindowExampleActivity.dispatchKeyEvent(ViewWindowExampleActivity.java:107)
   15:57:02.256   W/System.err:     at com.android.internal.policy.DecorView.dispatchKeyEvent(DecorView.java:342)
   15:57:02.256   W/System.err:     at android.view.ViewRootImpl$ViewPostImeInputStage.processKeyEvent(ViewRootImpl.java:5053)
   15:57:02.257   W/System.err:     at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4921)
   15:57:02.257   W/System.err:     at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4442)
   15:57:02.258   W/System.err:     at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4495)
   15:57:02.258   W/System.err:     at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4461)
   15:57:02.259   W/System.err:     at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:4601)
   15:57:02.259   W/System.err:     at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4469)
   15:57:02.259   W/System.err:     at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:4658)
   15:57:02.260   W/System.err:     at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4442)
   15:57:02.260   W/System.err:     at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4495)
   15:57:02.260   W/System.err:     at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4461)
   15:57:02.261   W/System.err:     at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4469)
   15:57:02.261   W/System.err:     at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4442)
   15:57:02.261   W/System.err:     at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4495)
   15:57:02.262   W/System.err:     at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4461)
   15:57:02.262   W/System.err:     at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:4634)
   15:57:02.263   W/System.err:     at android.view.ViewRootImpl$ImeInputStage.onFinishedInputEvent(ViewRootImpl.java:4795)
   15:57:02.263   W/System.err:     at android.view.inputmethod.InputMethodManager$PendingEvent.run(InputMethodManager.java:2571)
   15:57:02.263   W/System.err:     at android.view.inputmethod.InputMethodManager.invokeFinishedInputEventCallback(InputMethodManager.java:2081)
   15:57:02.264   W/System.err:     at android.view.inputmethod.InputMethodManager.finishedInputEvent(InputMethodManager.java:2072)
   15:57:02.264   W/System.err:     at android.view.inputmethod.InputMethodManager$ImeInputEventSender.onInputEventFinished(InputMethodManager.java:2548)
   15:57:02.265   W/System.err:     at android.view.InputEventSender.dispatchInputEventFinished(InputEventSender.java:141)
   15:57:02.265   W/System.err:     at android.os.MessageQueue.nativePollOnce(Native Method)
   15:57:02.265   W/System.err:     at android.os.MessageQueue.next(MessageQueue.java:326)
   15:57:02.265   W/System.err:     at android.os.Looper.loop(Looper.java:160)
   15:57:02.266   W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:6692)
   15:57:02.266   W/System.err:     at java.lang.reflect.Method.invoke(Native Method)
   15:57:02.266   W/System.err:     at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
   15:57:02.266   W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

   15:57:14.582   W/System.err: java.lang.Exception: Stack trace
   15:57:14.586   W/System.err:     at java.lang.Thread.dumpStack(Thread.java:1348)
   15:57:14.586   W/System.err:     at net.toughcoder.view.DemoEventView.dispatchTouchEvent(DemoEventView.java:24)
   15:57:14.586   W/System.err:     at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3030)
   15:57:14.586   W/System.err:     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2662)
   15:57:14.586   W/System.err:     at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3030)
   15:57:14.587   W/System.err:     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2662)
   15:57:14.587   W/System.err:     at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3030)
   15:57:14.587   W/System.err:     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2662)
   15:57:14.587   W/System.err:     at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3030)
   15:57:14.587   W/System.err:     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2662)
   15:57:14.587   W/System.err:     at com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:440)
   15:57:14.588   W/System.err:     at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1830)
   15:57:14.588   W/System.err:     at android.app.Activity.dispatchTouchEvent(Activity.java:3400)
   15:57:14.588   W/System.err:     at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:398)
   15:57:14.588   W/System.err:     at android.view.View.dispatchPointerEvent(View.java:12753)
   15:57:14.588   W/System.err:     at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:5122)
   15:57:14.588   W/System.err:     at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4925)
   15:57:14.588   W/System.err:     at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4442)
   15:57:14.588   W/System.err:     at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4495)
   15:57:14.589   W/System.err:     at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4461)
   15:57:14.589   W/System.err:     at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:4601)
   15:57:14.589   W/System.err:     at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4469)
   15:57:14.589   W/System.err:     at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:4658)
   15:57:14.589   W/System.err:     at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4442)
   15:57:14.589   W/System.err:     at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4495)
   15:57:14.589   W/System.err:     at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4461)
   15:57:14.589   W/System.err:     at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4469)
   15:57:14.590   W/System.err:     at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4442)
   15:57:14.590   W/System.err:     at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:7117)
   15:57:14.590   W/System.err:     at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:7086)
   15:57:14.590   W/System.err:     at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:7047)
   15:57:14.590   W/System.err:     at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:7220)
   15:57:14.590   W/System.err:     at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:187)
   15:57:14.590   W/System.err:     at android.os.MessageQueue.nativePollOnce(Native Method)
   15:57:14.590   W/System.err:     at android.os.MessageQueue.next(MessageQueue.java:326)
   15:57:14.591   W/System.err:     at android.os.Looper.loop(Looper.java:160)
   15:57:14.591   W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:6692)
   15:57:14.591   W/System.err:     at java.lang.reflect.Method.invoke(Native Method)
   15:57:14.591   W/System.err:     at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
   15:57:14.591   W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

Por lo tanto, desde el punto de vista de la aplicación, el primero en recibir eventos es Activity o Dialog, el componente de nivel superior que contiene Window, por lo que si desea interceptar todos los eventos desde el nivel de ventana, entonces Activity será la mejor opción, código ejemplo:

   @Override
   public boolean dispatchKeyEvent(KeyEvent event) {
        if (want to intercept all key events) {
              return true;
        }
        return super.dispatchKeyEvent(event);
    }

     @Override
   public boolean dispatchTouchEvent(MotionEvent event) {
        if (want to intercept all touch events) {
              return true;
        }
        return super.dispatchTouchEvent(event);
    }

Los dos métodos anteriores son los primeros métodos para recibir eventos antes del árbol de vista, y son el mejor lugar para interceptar en el componente, que es el pionero desde el frente. Si desea manejar eventos no controlados en el árbol de vista, debe procesarlos en onKeyUp(int keyCode, KeyEvent event) y onKeyDown(int keyCode, KeyEvent event) y onTouchEvent(MotionEvent event), que es equivalente a una pausa.

Propagación de eventos

Después de que el evento llega al final de la aplicación, el proceso de distribución comienza desde la Actividad. Su mecanismo y proceso son como una pequeña bola que fluye por todas partes. Cada nodo recibe un objeto de evento y devuelve un booleano. Si devuelve verdadero, significa que el evento está aquí se consume, la difusión del evento actual finaliza, y si devuelve falso, significa que el nodo actual no está interesado en este evento, y el evento continúa difundiéndose.

El proceso específico es ir primero a la Actividad (los componentes de primer nivel como Diálogo) y luego al árbol de vistas. Lo mismo ocurre en el árbol de vistas, pasando de la vista principal a la vista secundaria uno por uno. Este proceso secuencial está determinado por el conjunto determinado por la arquitectura del sistema.

Un evento es un flujo de datos.

El proceso de distribución de los eventos mencionados anteriormente puede verse como una bola que fluye, lo cual es cierto desde la perspectiva del procesamiento de un solo evento. Pero lo es aún más desde la perspectiva del evento completo, porque los eventos suelen ser como señales electrónicas, comenzando desde la fuente (como una pantalla táctil, hardware, etc.), y todo el proceso de enviar una serie de objetos de eventos con un cierto intervalo de tiempo Se envía una pelota cada 1 segundo, tal flujo de datos.

Habiendo dicho tanto, en realidad es relativamente simple hacerlo Aunque es un flujo de datos, cada flujo tiene una marca de inicio y final, por lo que no es difícil de manejar. Por ejemplo, KeyEvent comienza con onKeyDown, luego onKeyUp y procesa en estos dos para completar el procesamiento de la transmisión KeyEvent.

MotionEvent es un poco más complicado. Para un flujo de MotionEvent, el sistema volverá a llamar continuamente a TouchEvent hasta el final, juzgado por MotionEvent#getAction(), de ACTION_DOWN a ACTION_MOVE a ACTION_UP o ACTION_CANCEL.

Nota : Debido a que el manejo de KeyEvent es relativamente simple, el resto de la sección se centrará en MotionEvent.

El proceso de distribución de Touch Event

Después de generar el evento, se pasará a Activity#dispatchTouchEvent. Si no se intercepta, se pasará a Window, y Window se pasará a ViewRootImpl para su procesamiento. Después de que se procese el árbol de vista, se pasará a Actividad#onTouchEvent:

 public boolean dispatchTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        onUserInteraction();
    }
    if (getWindow().superDispatchTouchEvent(ev)) {
        return true;
    }
    return onTouchEvent(ev);
}

Este método puede ver claramente el orden de la vanguardia y el post-rotura y el árbol de vista en el flujo de despacho de eventos.

Centrémonos en el proceso de envío de eventos en la ventana (árbol de vista). De hecho, es suficiente centrarse en View#dispatchTouchEvent y ViewGroup#dispatchTouchEvent. Cabe señalar que el proceso de envío de eventos es diferente del proceso de procesamiento. El envío viene primero y el procesamiento viene después. Entonces, si observa el envío de eventos, debe mirar el método de envío al principio, y el procesamiento depende del principio de.

El envío de View es más simple, porque proporciona una implementación predeterminada, y View es una hoja en el árbol de vistas, por lo que su envío es en realidad un punto final, por lo que lo que hace es ver si hay un OnTouchListener, si lo hay, llame a su onTouch, y luego llama a onTouchEvent para procesar el evento, y se acabó. También se puede ver desde aquí que OnTouchListener está por delante del método onTouchEvent.

En cuanto a ViewGroup, es relativamente complicado porque tiene que administrar subvistas, enviar eventos a subvistas y también manejar la intercepción. Su lógica es más o menos: primero verifique si desea interceptar onInterceptTouchEvent, devuelva true para indicar que desea interceptar, false no interceptará, si desea interceptar, llame a su propio onTouchEvent para manejar el evento y luego finalice el envío ( la lógica real es un poco más complicada, diferente La lógica de procesamiento de ACCIÓN es diferente).

Centrémonos en cómo ViewGroup envía eventos a las Vistas secundarias. Cuando no se interceptan, esto es más convencional. Los eventos se enviarán a las Vistas secundarias. Echemos un vistazo al proceso: Primero, buildTouchDispatchChildList se usará para crear TouchDispatchChildList Seleccione el orden de la sub -Vistas. Este método es para ordenar las sub-Vistas de acuerdo al proceso de despacho de eventos. Este orden es el orden que ve el usuario. Se ordenará por el eje Z (desde el interior hacia el exterior de la pantalla), y el Orden de representación (dibujar), después de todo, desde el punto de vista del usuario, el primero en hacer clic debe ser el que tiene el eje Z más grande (más cercano al usuario), y el primero en dibujarse (no bloqueado ). Luego, en este orden, llame al dispatchTouchEvent según cada subvista y pase el evento a la subvista. Por supuesto, este también es un evento que está fluyendo. Una vez que se consuma el evento, dejará de enviarse.

A partir de este proceso, el envío de eventos del árbol de vistas es un proceso que prioriza la profundidad, por lo que la profundidad del árbol de vistas no solo afecta el rendimiento de la representación, sino que incluso el procesamiento de eventos es más lento que los planos.

Método de procesamiento de eventos Touch Event

El procesamiento de eventos es varios métodos que comienzan con on como onTouchEvent, o varios oyentes (OnClickListener, OnTouchListener). En términos generales, es suficiente configurar varios oyentes, pero si desea personalizar algo, puede anular directamente el método onTouchEvent. No entraré en detalles aquí, y hay demasiados tutoriales.

La diferencia entre el oyente y el método directo de la clase padre Override

Cabe señalar que si desea anular, debe personalizar la Vista, por lo que este es un método más "hack". Solo es necesario personalizar la Vista y los diversos oyentes convencionales no pueden cumplir con los requisitos. Por ejemplo, Implementar varios gestos personalizados, etc.

La mayor ventaja del oyente es que es muy simple y conveniente, y tiene un buen aislamiento. El desencadenante y el resultado del evento están aislados. Si desea tratar el evento, solo necesita implementar una interfaz. En cuanto al disparador de la condición del evento, no necesita preocuparse por eso. Cualquier objeto puede implementarlo. interfaz para manejar eventos sin tener que no desubclasificar (heredar) el objeto View.

También se debe tener en cuenta que OnTouchListener ocurre antes que onTouchEvent, mientras que las interfaces de devolución de llamada de gestos convencionales (como OnClickListener y onLongClickListner) se activan en onTouchEvent. Por lo tanto, OnTouchListener es en realidad una interfaz de "piratería" de nivel más bajo. Generalmente, esta interfaz debe implementarse cuando se requieren gestos de reconocimiento personalizados.

evitar hacer clic

A veces habrá algunos problemas con los clics, como escribir un diseño con varios botones y vistas de texto, pero cuando hace clic en un área en blanco fuera del contenido principal, el botón en la siguiente capa de la página recibe un evento, como como desencadenar su evento onClick. Este problema es más común cuando se usan Fragmentos apilados. De hecho, puede ver la solución en View#onTouchEvent. Si se puede hacer clic en una Vista, consumirá el evento, y si se puede hacer clic es falso, seguirá pasando.

El motivo de la penetración es el área en blanco. Solo hay un diseño raíz de esta página de capa, que suele ser un grupo de vistas, y la mayoría de los clics predeterminados de los grupos de vistas son falsos, por lo que el evento continuará pasándose al árbol de vistas hasta que se consume

La solución más fácil para este tipo de problema es establecer View en clickable="true", que se puede establecer en el archivo de diseño.

Reconocimiento básico de gestos

El reconocimiento de gestos básico se refiere a la clasificación de algunas operaciones simples para operaciones sensibles al tacto, como tocar ligeramente la pantalla y quitarla de inmediato, lo que se considera un clic (clic, presionar o tocar) y mantener presionada la pantalla. como clic largo O llamada pulsación larga, así como deslizamiento, doble clic, etc. El reconocimiento de gestos es un conjunto de algoritmos lógicos que se utilizan para determinar qué tipo de operación está realizando actualmente el usuario y luego activar la lógica de procesamiento correspondiente para dar retroalimentación sobre la operación del usuario. Tantas tonterías, vamos a ver cómo hacerlo.

Los gestos básicos en el sistema GUI de Android son hacer clic y hacer clic largo. Hay dos formas de reconocer estos gestos básicos. Una es configurar la interfaz para que vuelva a llamar a View, es decir, implementar un OnClickListener, y luego configurar este objeto en View#setOnClickListener (la presión prolongada es OnLongClickListener y View#setOnLongClickListener; la otra El método es para Dentro del árbol de vista, como subclasificar (heredar) un objeto Vista y luego anular el método correspondiente.

Nota : también es conveniente usar onclick para especificar el método de devolución de llamada del gesto en el archivo xml de diseño, pero su esencia es la misma que configurar un OnClickListener.

Si hacer clic y mantener presionado no puede cumplir con los requisitos de la operación, se necesita un objeto básico de reconocimiento de gestos un poco más complicado para ayudar, es decir, GestureDetector, que se conecta a la Vista separando la interfaz.La fuente del evento MotionEvent es suficiente. El método utilizado no es complicado, simplemente configure un OnTouchListener o una Vista de subclase y anule el método onTouch, obtenga el objeto MotionEvent de él y luego inserte el MotionEvent en un objeto GestureDetecotor, y se acabó, GestureDetector devolverá la llamada al procesamiento del gesto correspondiente. están interesados ​​en el método Callback, a través del objeto OnGestureListener. Debido a que OnGestureListener es una interfaz, pero si solo está interesado en algunos métodos de devolución de llamada de gestos y no desea escribir todos los métodos (incluso si es una implementación vacía), puede subclasificar SimpleOnGestureListener, que es una clase que implementa OnGestureListener Para todos los métodos, solo necesitamos anular los métodos que nos interesan.

Una cosa que necesita atención especial es que cuando usa GestureDetector, su secuencia con onClick normal o onLongClick, o manejo de conflictos. Basado en el principio de consistencia, si se usa GestureDetector, significa que desea controlar el procesamiento de eventos por sí mismo, entonces no debe configurar onClick o onLongClick nunca más. Pero, ¿qué pasa si lo haces por accidente? Esto requiere encontrar la respuesta del proceso de manejo de eventos de View. OnTouchListener se llama en View#dispatchTouchEvent, esto es antes de View#onTouchEvent, y OnClickListener y OnLongClickListener se llaman en View#onTouchEvent. Entonces, la secuencia es esta:

  1. Si usa OnTouchListener para obtener MotionEvent, entonces se llama primero al método de devolución de llamada de OnGestureListener, antes de todas las demás devoluciones de llamada.
  2. Si es el evento obtenido por el método de invalidación View#onTouchEvent, depende del orden en que llame a super#onTouchEvent. Si llama a super antes, su detector de gestos se ejecutará primero. De hecho, la forma normal de escribir override primero debe escribir su propia lógica y luego llamar a super, o no llamar a super en absoluto.Esta es la postura de clase padre de override de subclase más ortodoxa.

A partir de esto, se puede concluir que si se usa GestureDetector, primero se debe ejecutar su detector de gestos.

Tiempo de activación de onClick y onLongClick

Veamos otras dos preguntas interesantes: ¿Cuándo se activa onClick? Se puede ver en el método View#onTouchEvent que se activa cuando se activa ACTION_UP. Si no ha activado un clic largo, el clic largo comenzará a contar el tiempo después de ACTION_DOWN después de que comience el evento, y se activará después de un cierto intervalo de tiempo. No se cuenta como tipo de evento de seguimiento.

El proceso general es así. En View#onTouchEvent, los tipos de eventos se procesan. Comience a cronometrar en ACTION_DOWN y continúe cronometrando en ACTION_MOVE más tarde. Si se alcanza el estándar de pulsación prolongada, se activará un clic prolongado. En ACTION_UP que termina normalmente, marque si hay alguno. Si se cumple el estándar de pulsación larga, se activará un clic largo si hay uno, y se activará un clic si no lo hay.

Definición del umbral del sistema

Los umbrales clave, como la duración de la pulsación larga, la distancia mínima de deslizamiento, la distancia mínima de estiramiento, etc., están definidos por la sugerencia del sistema. Estos valores están todos en ViewConfiguration. En general, se recomienda usar la definición del sistema directamente , a menos que exista uno real Necesidades especiales.

Notas de estudio de Android

Optimización del rendimiento de Android: https://qr18.cn/FVlo89
Vehículo de Android: https://qr18.cn/F05ZCM
Notas de estudio de seguridad inversa de https://qr18.cn/CQ5TcL
Android: Principios del marco de trabajo de Android: https://qr18.cn/AQpN4J
Audio y video de Android: https://qr18.cn/Ei3VPD
Jetpack (incluido Compose): https://qr18.cn/A0gajp
Kotlin: https://qr18.cn/CdjtAF
Gradle: https://qr18.cn/DzrmMB
OkHttp Notas de análisis de código fuente: https://qr18.cn/Cw0pBD
Flutter: https://qr18.cn/DIvKma
Android Eight Knowledge Body: https://qr18.cn/CyxarU
Notas principales de Android: https://qr21.cn/CaZQLo
Android Preguntas de entrevistas anteriores: https://qr18.cn/CKV8OZ
2023 Última colección de preguntas de entrevistas de Android: https://qr18.cn/CgxrRy
Ejercicios de entrevistas de trabajo de desarrollo de vehículos de Android: https://qr18.cn/FTlyCJ
Preguntas de entrevistas de audio y video:https://qr18.cn/AcV6Ap

Supongo que te gusta

Origin blog.csdn.net/weixin_61845324/article/details/131534630
Recomendado
Clasificación