quitar para pantalla hacer como aplicacion android testing zoom gesture

pantalla - Cómo generar un gesto de zoom/pellizco para probar para Android



como quitar el zoom de la pantalla de mi android (3)

Estoy tratando de generar un gesto de zoom / pellizco para la prueba, he intentado usar la API de Android MotionEvent.obtain (), pero he encontrado que es difícil implementar los eventos de zoom / pellizco.

Hice referencia a la API de MotionEvent.obtain . ¿Puedes decirme el método correcto?

Aquí está mi implementación:

//for zoom, we need four points coordinations: start0, start1, end0, end1 Instrumentation inst; // action down event MotionEvent event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_DOWN, 1, prop_start0, pointerCoords_start0, 0, 0, 0, 0, 0, 0, 0, 0 ); inst.sendPointerSync(event); // action pointer 2 down event event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_POINTER_2_DOWN, 2, properties_start0_1, pointerCoords_start0_1, 0, 0, 0, 0, 0, 0, 0, 0); inst.sendPointerSync(event); // action move events duration = 1000; //1000 ms event_interval = 10;//time interval between consecutive events 10ms moveEventNum = duration / event_interval; stepx0 = (end0.x - start0.x)/moveEventNum; stepy0 = (end0.y - start0.y)/moveEventNum; stepx1 = (end1.x - start1.x)/moveEventNum; stepy1 = (end1.y - start1.y)/moveEventNum; move_event0= start0; move_event1 = start1; for ( int i = 0; i < moveEventNum; i++) { // [generate middle points here ] mov_event0.x += stepx0; mov_event0.y += stepy0; mov_event1.x += stepx1; mov_event1.y += stepy1; eventTime += event_interval; event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE, 2, properties_move_event, pointerCoords_move_event0_1, 0, 0, 0, 0, 0, 0, 0, 0); inst.sendPointerSync(event); } event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_POINTER_2_UP, 2, properties_end0_1, pointerCoords_end0_1, 0, 0, 0, 0, 0, 0, 0, 0); inst.sendPointerSync(event); event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, 1, end1, pointerCoords_end1, 0, 0, 0, 0, 0, 0, 0, 0 ); inst.sendPointerSync(event);


Bueno, he encontrado el problema.

PROBLEMA:

Al utilizar la API de obtención (), debemos establecer la presión y el tamaño de los puntos en cada evento.

por

obtain(long, long, int, int, android.view.MotionEvent.PointerProperties[], android.view.MotionEvent.PointerCoords[], int, int, float, float, int, int, int, int)

PointerCoords [], tenemos que establecer la presión y el tamaño en 1, los valores predeterminados son 0.

por

public static MotionEvent obtain (long downTime, long eventTime, int action, float x, float y, int metaState) Create a new MotionEvent, filling in a subset of the basic motion values. Those not specified here are: device id (always 0), pressure and size (always 1), x and y precision (always 1), and edgeFlags (always 0). since the default pressure and size are 1, so we don''t need to set them.

Mis consejos para crear gestos:

1. Siguiendo la secuencia de gestos reales, ya que queremos simular los gestos reales.

  • anule el onTouchEvent () para verificar los eventos reales recibidos por la aplicación. Estos eventos también se pueden usar para comparar eventos táctiles de usuarios reales y eventos táctiles generados. Tome el navegador, por ejemplo:

    a) @Override public boolean onTouchEvent (evento MotionEvent) {Log.i ("WebView", event.toString () + event.getAction ()); boolean rt = super.onTouchEvent (evento); retorno rt }

  • toque manualmente la pantalla para obtener la secuencia de gestos reales de onTouchEvent () en a). Podemos seguir la secuencia de eventos al generar eventos. - Si no seguimos la secuencia de eventos gestuales, los eventos instrumentados pueden ser rechazados.

  • Aquí hay una secuencia de eventos válida del gesto de zoom, (el tiempo de inactividad es el mismo para todos los eventos)

    yo. ACTION_DOWN de un punto de inicio

    ii. ACTION_POINTER_2_DOWN de dos puntos de inicio

    iii.ACTION_MOVE de dos puntos medios

    iv. ACTION_POINTER_2_UP de dos puntos finales

    v. ACTION_UP de un punto final

2. Usa la API MotionEvent.obtain correctamente

  • Hay dos API de obtención () más utilizadas.

Obtención estática pública de MotionEvent (long downTime, long eventTime, int action, float x, float y, int metaState)

Y

MotionEvent estático público obtenido (long, long, int, int, android.view.MotionEvent.PointerProperties [], android.view.MotionEvent.PointerCoords [], int, int, float, float, int, int, int, int)

El primero generalmente se usa para gestos de un solo punto, como lanzar, desplazar, hacer clic, etc. Los parámetros (presión, tamaño, xPresion, yPresion) para esta función están todos configurados en 1.

Y el segundo es más general y puede usarse para la generación de eventos multitáctiles. Mientras que para el segundo, tenemos que establecer la presión, el tamaño en los puntos de referencia de cada punto de contacto a 1.

Aquí está el ejemplo para generar el gesto de zoom:

public static void generateZoomGesture(Instrumentation inst, long startTime, boolean ifMove, GestureInfo.Point startPoint1, GestureInfo.Point startPoint2, GestureInfo.Point endPoint1, GestureInfo.Point endPoint2, int duration) { if (inst == null || startPoint1 == null || (ifMove && endPoint1 == null)) { return; } long eventTime = startTime; long downTime = startTime; MotionEvent event; float eventX1, eventY1, eventX2, eventY2; eventX1 = startPoint1.x; eventY1 = startPoint1.y; eventX2 = startPoint2.x; eventY2 = startPoint2.y; // specify the property for the two touch points PointerProperties[] properties = new PointerProperties[2]; PointerProperties pp1 = new PointerProperties(); pp1.id = 0; pp1.toolType = MotionEvent.TOOL_TYPE_FINGER; PointerProperties pp2 = new PointerProperties(); pp2.id = 1; pp2.toolType = MotionEvent.TOOL_TYPE_FINGER; properties[0] = pp1; properties[1] = pp2; //specify the coordinations of the two touch points //NOTE: you MUST set the pressure and size value, or it doesn''t work PointerCoords[] pointerCoords = new PointerCoords[2]; PointerCoords pc1 = new PointerCoords(); pc1.x = eventX1; pc1.y = eventY1; pc1.pressure = 1; pc1.size = 1; PointerCoords pc2 = new PointerCoords(); pc2.x = eventX2; pc2.y = eventY2; pc2.pressure = 1; pc2.size = 1; pointerCoords[0] = pc1; pointerCoords[1] = pc2; ////////////////////////////////////////////////////////////// // events sequence of zoom gesture // 1. send ACTION_DOWN event of one start point // 2. send ACTION_POINTER_2_DOWN of two start points // 3. send ACTION_MOVE of two middle points // 4. repeat step 3 with updated middle points (x,y), // until reach the end points // 5. send ACTION_POINTER_2_UP of two end points // 6. send ACTION_UP of one end point ////////////////////////////////////////////////////////////// // step 1 event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_DOWN, 1, properties, pointerCoords, 0, 0, 1, 1, 0, 0, 0, 0 ); inst.sendPointerSync(event); //step 2 event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_POINTER_2_DOWN, 2, properties, pointerCoords, 0, 0, 1, 1, 0, 0, 0, 0); inst.sendPointerSync(event); //step 3, 4 if (ifMove) { int moveEventNumber = 1; moveEventNumber = duration / EVENT_MIN_INTERVAL; float stepX1, stepY1, stepX2, stepY2; stepX1 = (endPoint1.x - startPoint1.x) / moveEventNumber; stepY1 = (endPoint1.y - startPoint1.y) / moveEventNumber; stepX2 = (endPoint2.x - startPoint2.x) / moveEventNumber; stepY2 = (endPoint2.y - startPoint2.y) / moveEventNumber; for (int i = 0; i < moveEventNumber; i++) { // update the move events eventTime += EVENT_MIN_INTERVAL; eventX1 += stepX1; eventY1 += stepY1; eventX2 += stepX2; eventY2 += stepY2; pc1.x = eventX1; pc1.y = eventY1; pc2.x = eventX2; pc2.y = eventY2; pointerCoords[0] = pc1; pointerCoords[1] = pc2; event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE, 2, properties, pointerCoords, 0, 0, 1, 1, 0, 0, 0, 0); inst.sendPointerSync(event); } } //step 5 pc1.x = endPoint1.x; pc1.y = endPoint1.y; pc2.x = endPoint2.x; pc2.y = endPoint2.y; pointerCoords[0] = pc1; pointerCoords[1] = pc2; eventTime += EVENT_MIN_INTERVAL; event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_POINTER_2_UP, 2, properties, pointerCoords, 0, 0, 1, 1, 0, 0, 0, 0); inst.sendPointerSync(event); // step 6 eventTime += EVENT_MIN_INTERVAL; event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, 1, properties, pointerCoords, 0, 0, 1, 1, 0, 0, 0, 0 ); inst.sendPointerSync(event); }


Como se mencionó en un comentario de Täg, esto se puede usar con Espresso. Aquí está mi código para hacer esto en Espresso, basado en la muy buena respuesta de longchuan y el formulario de actualización peetasan.

En la prueba de espresso:

// Pinch out (to zoom in): onView(withId(R.id.MyViewId)).perform(pinchOut());

Los métodos de ayuda, que incluyen una buena manera de obtener las coordenadas correctas de la vista, y también incluyen el manejo correcto de errores para uiController.injectMotionEvent(event) :

public static ViewAction pinchOut() { return new ViewAction() { @Override public Matcher<View> getConstraints() { return ViewMatchers.isEnabled(); } @Override public String getDescription() { return "Pinch out"; } @Override public void perform(UiController uiController, View view) { Point middlePosition = getCenterPoint(view); final int startDelta = 0; // How far from the center point each finger should start final int endDelta = 500; // How far from the center point each finger should end (note: Be sure to have this large enough so that the gesture is recognized!) Point startPoint1 = new Point(middlePosition.x - startDelta, middlePosition.y); Point startPoint2 = new Point(middlePosition.x + startDelta, middlePosition.y); Point endPoint1 = new Point(middlePosition.x - endDelta, middlePosition.y); Point endPoint2 = new Point(middlePosition.x + endDelta, middlePosition.y); performPinch(uiController, startPoint1, startPoint2, endPoint1, endPoint2); } }; } public static ViewAction pinchIn() { return new ViewAction() { @Override public Matcher<View> getConstraints() { return ViewMatchers.isEnabled(); } @Override public String getDescription() { return "Pinch in"; } @Override public void perform(UiController uiController, View view) { Point middlePosition = getCenterPoint(view); final int startDelta = 500; // How far from the center point each finger should start (note: Be sure to have this large enough so that the gesture is recognized!) final int endDelta = 0; // How far from the center point each finger should end Point startPoint1 = new Point(middlePosition.x - startDelta, middlePosition.y); Point startPoint2 = new Point(middlePosition.x + startDelta, middlePosition.y); Point endPoint1 = new Point(middlePosition.x - endDelta, middlePosition.y); Point endPoint2 = new Point(middlePosition.x + endDelta, middlePosition.y); performPinch(uiController, startPoint1, startPoint2, endPoint1, endPoint2); } }; } @NonNull private static Point getCenterPoint(View view) { int[] locationOnScreen = new int[2]; view.getLocationOnScreen(locationOnScreen); float viewHeight = view.getHeight() * view.getScaleY(); float viewWidth = view.getWidth() * view.getScaleX(); return new Point( (int) (locationOnScreen[0] + viewWidth / 2), (int) (locationOnScreen[1] + viewHeight / 2)); } private static void performPinch(UiController uiController, Point startPoint1, Point startPoint2, Point endPoint1, Point endPoint2) { final int duration = 500; final long eventMinInterval = 10; final long startTime = SystemClock.uptimeMillis(); long eventTime = startTime; MotionEvent event; float eventX1, eventY1, eventX2, eventY2; eventX1 = startPoint1.x; eventY1 = startPoint1.y; eventX2 = startPoint2.x; eventY2 = startPoint2.y; // Specify the property for the two touch points MotionEvent.PointerProperties[] properties = new MotionEvent.PointerProperties[2]; MotionEvent.PointerProperties pp1 = new MotionEvent.PointerProperties(); pp1.id = 0; pp1.toolType = MotionEvent.TOOL_TYPE_FINGER; MotionEvent.PointerProperties pp2 = new MotionEvent.PointerProperties(); pp2.id = 1; pp2.toolType = MotionEvent.TOOL_TYPE_FINGER; properties[0] = pp1; properties[1] = pp2; // Specify the coordinations of the two touch points // NOTE: you MUST set the pressure and size value, or it doesn''t work MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2]; MotionEvent.PointerCoords pc1 = new MotionEvent.PointerCoords(); pc1.x = eventX1; pc1.y = eventY1; pc1.pressure = 1; pc1.size = 1; MotionEvent.PointerCoords pc2 = new MotionEvent.PointerCoords(); pc2.x = eventX2; pc2.y = eventY2; pc2.pressure = 1; pc2.size = 1; pointerCoords[0] = pc1; pointerCoords[1] = pc2; /* * Events sequence of zoom gesture: * * 1. Send ACTION_DOWN event of one start point * 2. Send ACTION_POINTER_DOWN of two start points * 3. Send ACTION_MOVE of two middle points * 4. Repeat step 3 with updated middle points (x,y), until reach the end points * 5. Send ACTION_POINTER_UP of two end points * 6. Send ACTION_UP of one end point */ try { // Step 1 event = MotionEvent.obtain(startTime, eventTime, MotionEvent.ACTION_DOWN, 1, properties, pointerCoords, 0, 0, 1, 1, 0, 0, 0, 0); injectMotionEventToUiController(uiController, event); // Step 2 event = MotionEvent.obtain(startTime, eventTime, MotionEvent.ACTION_POINTER_DOWN + (pp2.id << MotionEvent.ACTION_POINTER_INDEX_SHIFT), 2, properties, pointerCoords, 0, 0, 1, 1, 0, 0, 0, 0); injectMotionEventToUiController(uiController, event); // Step 3, 4 long moveEventNumber = duration / eventMinInterval; float stepX1, stepY1, stepX2, stepY2; stepX1 = (endPoint1.x - startPoint1.x) / moveEventNumber; stepY1 = (endPoint1.y - startPoint1.y) / moveEventNumber; stepX2 = (endPoint2.x - startPoint2.x) / moveEventNumber; stepY2 = (endPoint2.y - startPoint2.y) / moveEventNumber; for (int i = 0; i < moveEventNumber; i++) { // Update the move events eventTime += eventMinInterval; eventX1 += stepX1; eventY1 += stepY1; eventX2 += stepX2; eventY2 += stepY2; pc1.x = eventX1; pc1.y = eventY1; pc2.x = eventX2; pc2.y = eventY2; pointerCoords[0] = pc1; pointerCoords[1] = pc2; event = MotionEvent.obtain(startTime, eventTime, MotionEvent.ACTION_MOVE, 2, properties, pointerCoords, 0, 0, 1, 1, 0, 0, 0, 0); injectMotionEventToUiController(uiController, event); } // Step 5 pc1.x = endPoint1.x; pc1.y = endPoint1.y; pc2.x = endPoint2.x; pc2.y = endPoint2.y; pointerCoords[0] = pc1; pointerCoords[1] = pc2; eventTime += eventMinInterval; event = MotionEvent.obtain(startTime, eventTime, MotionEvent.ACTION_POINTER_UP + (pp2.id << MotionEvent.ACTION_POINTER_INDEX_SHIFT), 2, properties, pointerCoords, 0, 0, 1, 1, 0, 0, 0, 0); injectMotionEventToUiController(uiController, event); // Step 6 eventTime += eventMinInterval; event = MotionEvent.obtain(startTime, eventTime, MotionEvent.ACTION_UP, 1, properties, pointerCoords, 0, 0, 1, 1, 0, 0, 0, 0); injectMotionEventToUiController(uiController, event); } catch (InjectEventSecurityException e) { throw new RuntimeException("Could not perform pinch", e); } } /** * Safely call uiController.injectMotionEvent(event): Detect any error and "convert" it to an * IllegalStateException */ private static void injectMotionEventToUiController(UiController uiController, MotionEvent event) throws InjectEventSecurityException { boolean injectEventSucceeded = uiController.injectMotionEvent(event); if (!injectEventSucceeded) { throw new IllegalStateException("Error performing event " + event); } }


Para hacer que el código anterior funcione con constantes no en desuso, utilicé MotionEvent.ACTION_POINTER_DOWN + (pp2.id << MotionEvent.ACTION_POINTER_INDEX_SHIFT) lugar de MotionEvent.ACTION_POINTER_2_DOWN

Así que tuve que reemplazar esta línea:

event = MotionEvent.obtain (downTime, eventTime, MotionEvent.ACTION_POINTER_2_DOWN, 2, properties, pointerCoords, 0, 0, 1, 1, 0, 0, 0, 0, 0);

con

event = MotionEvent.obtain (downTime, eventTime, MotionEvent.ACTION_POINTER_DOWN + (pp2.id << MotionEvent.ACTION_POINTER_INDEX_SHIFT), 2, properties, pointerCoords, 0, 0, 1, 0, 0, 0, 0, 0);