Comprensión multitáctil de Android

En primer lugar, la función multitáctil necesita usar event.getActionMasked() para obtener eventos, y la llamada es la siguiente:

  • case MotionEvent.ACTION_DOWN: //El primer dedo presiona el disparador, solo dispara una vez
  • case MotionEvent.ACTION_MOVE: //Todos los eventos de movimiento de dedos activarán este evento
  • case MotionEvent.ACTION_UP: //Solo se activa una vez, se activa cuando se levanta el último dedo
  • case MotionEvent.ACTION_POINTER_DOWN: //No activado por la presión del primer dedo
  • case MotionEvent.ACTION_POINTER_UP: //Se activa cuando se levanta el último dedo
    A continuación, veamos un fragmento de código y efecto
public class MultiTouchView extends View {
    
    
    private float offsetX, offsetY;
    private float lastOffsetX, lastOffsetY;
    private float downX, downY;
    Bitmap bitmap;
    Paint paint;
    float currentScale;
    private int currentPointId;

    。。。省略

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    
    
        super.onSizeChanged(w, h, oldw, oldh);
        bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.test3);
        paint = new Paint();
        if ((float) bitmap.getWidth() / bitmap.getHeight() > (float) getWidth() / getHeight()) {
    
    //图片是横向图片
            currentScale = (float) getWidth() / bitmap.getWidth();
        } else {
    
    
            currentScale = (float) getHeight() / bitmap.getHeight();
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
    
    
        super.onDraw(canvas);
        canvas.translate(offsetX, offsetY);
        canvas.scale(currentScale, currentScale, getWidth() / 2f, getHeight() / 2f);
        canvas.drawBitmap(bitmap, (getWidth() - bitmap.getWidth()) / 2f, (getHeight() - bitmap.getHeight()) / 2f, paint);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
    
    
        switch (event.getActionMasked()) {
    
     //getAction
            //只会触发一次
            case MotionEvent.ACTION_DOWN:
                downX = event.getX();
                downY = event.getY();
                currentPointId=0;
                break;
                //所有手指移动都是触发这个事件
            case MotionEvent.ACTION_MOVE:
                //获取id对应的index值,index是会变化的,id不会变化
                //int index= event.findPointerIndex(currentPointId);

                //移动距离:上次偏移值+当前滑动距离
                offsetX = lastOffsetX + event.getX() - downX;
                //event.getY() 使用的是pointerIndex
                offsetY = lastOffsetY + event.getY() - downY;
                invalidate();
                break;
            //只会触发一次,最后一根手指抬起时触发
            case MotionEvent.ACTION_UP:
                //抬手记录上次偏移值
                lastOffsetX = offsetX;
                lastOffsetY = offsetY;
                break;
                //非第一跟手指按下触发
            case MotionEvent.ACTION_POINTER_DOWN:
                break;
                //非最后一根手指抬起触发
            case MotionEvent.ACTION_POINTER_UP:
                break;

        }
        return true;
    }
}

Personalizó una vista, onDraw dibuja una imagen y offsetX (Y) logra el efecto de deslizar la imagen con el dedo durante ACTION_MOVE:

mi operación aquí es

  1. Primero presione el dedo 1 en la esquina superior derecha, deslice y la imagen se puede deslizar con el dedo
  2. Luego presione el dedo 2 en la esquina inferior izquierda y deslice para encontrar que el dedo 2 no puede deslizar la imagen (todavía solo se desliza con el dedo 1)
  3. Luego suelte el dedo 1, luego la imagen salta a la esquina inferior izquierda y puede deslizarse con el dedo 2
  4. Finalmente, presiona de nuevo el dedo 1, la imagen salta a la esquina superior derecha y se desliza con el dedo 1

¿Por qué no puedo deslizar la imagen presionando el dedo 2 en el segundo paso? Mira el código en MotionEvent.ACTION_MOVE:

offsetX = lastOffsetX + event.getX() - downX

El problema radica en este event.getX (). Verifique el código fuente de MotionEvent:
inserte la descripción de la imagen aquí
el segundo parámetro pointerIndex usa el valor predeterminado de 0. Después de algunas investigaciones, se encuentra que
después de presionar cada dedo, el sistema guardará el índice y id del dedo Después de quitar un dedo, el índice se reordenará y la identificación original permanecerá sin cambios. Al insertar un dedo, la operación de inserción se realizará de acuerdo con la lista de id, como se muestra en la siguiente figura:
inserte la descripción de la imagen aquí
Al presionar cuatro dedos en secuencia, el id y el índice de los cuatro dedos son los mismos, que es 0123 a su vez, y cuando se quita el segundo dedo, el orden se reordenará, el índice del tercer dedo se convierte en 1, la identificación permanece sin cambios, el índice del cuarto dedo se convierte en 2 y la identificación permanece sin cambios. En este momento, si presiona otro dedo, encontrará que la identificación es solo 0, 2 y 3, por lo que se genera un dedo con identificación 1 y luego se reordena para volverse el mismo que el estado inicial.

Mirando hacia atrás en las operaciones anteriores, se generaron dos dedos al presionar los dedos 1 y 2 en secuencia: dedo 1 (id = 0, índice = 0), dedo 2 (id = 1, índice = 1). Por lo tanto, en la primera operación
arriba En el segundo paso, debido a que el índice de event.getX() en Action_move siempre es 0, se procesa el primer dedo y, naturalmente, el segundo dedo no puede extraer la imagen, en el tercer paso, debido a que se elimina el primer dedo Dedo
2 se reordena, y el índice del dedo 2 se convierte en 0, por lo que el dedo 2 puede arrastrar la imagen
Paso 4, cuando se presiona de nuevo el dedo 1, se encuentra que solo hay el dedo 2 (id = 1), y no hay id0, así que cree una identificación de dedo es 0, y luego la reordenación se convierte en dedo 1 (id = 0, índice = 0), dedo 2 (id = 1, índice = 1), el índice del dedo 1 es 0, por lo que es el dedo 1 Puede arrastrar imágenes.

A continuación, practique, cambie la Vista anterior a: el último dedo presionado extrae la imagen
Idea: registre la identificación del último dedo presionado, obtenga el índice de acuerdo con la identificación del dedo y pase event.getX/Y() en ACTION_MOVE al índice:

 case MotionEvent.ACTION_DOWN:
                downX = event.getX();
                downY = event.getY();
                currentPointId=0;
                break;
                //所有手指移动都是触发这个事件
case MotionEvent.ACTION_MOVE:
                //获取id对应的index值,index是会变化的,id不会变化
                int index= event.findPointerIndex(currentPointId);//根据id获取index

                //移动距离:上次偏移值+当前滑动距离
                offsetX = lastOffsetX + event.getX(index) - downX;
                //event.getY() 使用的是pointerIndex
                offsetY = lastOffsetY + event.getY(index) - downY;
                invalidate();
                break;
            //只会触发一次,最后一根手指抬起时触发
 case MotionEvent.ACTION_UP:
                //抬手记录上次偏移值
                lastOffsetX = offsetX;
                lastOffsetY = offsetY;
                break;
                //非第一跟手指按下触发
case MotionEvent.ACTION_POINTER_DOWN:
                int pointerIndex= event.getActionIndex();
                currentPointId=event.getPointerId(pointerIndex);
                //解决跳动
                downX=event.getX(pointerIndex);
                downY=event.getY(pointerIndex);
                lastOffsetX = offsetX;
                lastOffsetY = offsetY;
                break;

La identificación actual se registra en ACTION_DOWN: currentPointId=0 (porque es la identificación del primer dedo debe ser 0), y cuando ACTION_POINTER_DOWN no ​​es el primer dedo presionado, obtenga la identificación presionada a través de getActionIndex, event.getPointerId(pointerIndex) y finalmente encuentre el índice de la identificación actual a través de findPointerIndex(currentPointId) en el evento ACTION_MOVE, asegurando así que el índice es el índice del último dedo presionado.
En este punto, se logra el efecto del último dedo presionado tirando de la imagen, pero se introduce un nuevo problema: cuando se levanta el dedo que arrastra la imagen, habrá un flashback.
Esto se debe a que la matriz event.findPointerIndex(currentPointId) está fuera de los límites, ya que currentPointId se elimina después de levantar el dedo y se informará un error al encontrar el índice a través de esta identificación. Procesando en ACTION_POINTER_UP:

            //非最后一根手指抬起触发
            case MotionEvent.ACTION_POINTER_UP:
                int upIndex = event.getActionIndex();
                int pointerUpId = event.getPointerId(upIndex);
                if (pointerUpId == currentPointId) {
    
    
                    //如果抬起的是最后一个手指
                    if (upIndex == event.getPointerCount() - 1) {
    
    
                        upIndex = event.getPointerCount() - 2;
                    } else {
    
    
                        upIndex++;
                    }
                    currentPointId = event.getPointerId(upIndex);
                    //记录位置,解决跳动
                    downX = event.getX(upIndex);
                    downY = event.getY(upIndex);
                    lastOffsetX = offsetX;
                    lastOffsetY = offsetY;
                }
                break;

Obtenga la identificación del dedo cuando se levante el dedo. Si la identificación es la identificación del dedo que controla el arrastre de la imagen, entonces si la identificación es el último dedo, el índice retrocederá en uno (controlando el arrastre para el dedo anterior), si el id es el último dedo Si el id no es el último, entonces index++ se entregará al siguiente dedo para su procesamiento.
Al final, el problema de que la imagen salta cuando se presiona el dedo nuevo es porque el actual downX (Y) y lastOffsetX (Y) no se registran cuando se presiona el dedo.
Código completo ------------------------------------------------ ---------------------------------------->

 import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import androidx.annotation.Nullable;

public class MultiTouchView extends View {
    
    
    private float offsetX, offsetY;
    private float lastOffsetX, lastOffsetY;
    private float downX, downY;
    Bitmap bitmap;
    Paint paint;
    float currentScale;
    private int currentPointId;

    public MultiTouchView(Context context) {
    
    
        this(context, null);
    }

    public MultiTouchView(Context context, @Nullable AttributeSet attrs) {
    
    
        this(context, attrs, 0);
    }

    public MultiTouchView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    
    
        super(context, attrs, defStyleAttr);


    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    
    
        super.onSizeChanged(w, h, oldw, oldh);
        bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.test3);
        paint = new Paint();
        if ((float) bitmap.getWidth() / bitmap.getHeight() > (float) getWidth() / getHeight()) {
    
    //图片是横向图片
            currentScale = (float) getWidth() / bitmap.getWidth();
        } else {
    
    
            currentScale = (float) getHeight() / bitmap.getHeight();

        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
    
    
        super.onDraw(canvas);
        canvas.translate(offsetX, offsetY);
        canvas.scale(currentScale, currentScale, getWidth() / 2f, getHeight() / 2f);
        canvas.drawBitmap(bitmap, (getWidth() - bitmap.getWidth()) / 2f, (getHeight() - bitmap.getHeight()) / 2f, paint);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
    
    
        switch (event.getActionMasked()) {
    
     //getAction
            //只会触发一次
            case MotionEvent.ACTION_DOWN:
                downX = event.getX();
                downY = event.getY();
                currentPointId = 0;
                break;

            //所有手指移动都是触发这个事件
            case MotionEvent.ACTION_MOVE:
                //获取id对应的index值,index是会变化的,id不会变化
                int index = event.findPointerIndex(currentPointId);//根据id获取index

                //移动距离:上次偏移值+当前滑动距离
                offsetX = lastOffsetX + event.getX(index) - downX;
                //event.getY() 使用的是pointerIndex
                offsetY = lastOffsetY + event.getY(index) - downY;
                invalidate();
                break;

            //只会触发一次,最后一根手指抬起时触发
            case MotionEvent.ACTION_UP:
                //抬手记录上次偏移值
                lastOffsetX = offsetX;
                lastOffsetY = offsetY;
                break;

            //非第一跟手指按下触发
            case MotionEvent.ACTION_POINTER_DOWN:
                int pointerIndex = event.getActionIndex();
                currentPointId = event.getPointerId(pointerIndex);
                //记录位置,解决跳动
                downX = event.getX(pointerIndex);
                downY = event.getY(pointerIndex);
                lastOffsetX = offsetX;
                lastOffsetY = offsetY;
                break;
            //非最后一根手指抬起触发

            case MotionEvent.ACTION_POINTER_UP:
                int upIndex = event.getActionIndex();
                int pointerUpId = event.getPointerId(upIndex);
                if (pointerUpId == currentPointId) {
    
    
                    //如果抬起的是最后一个手指
                    if (upIndex == event.getPointerCount() - 1) {
    
    
                        upIndex = event.getPointerCount() - 2;
                    } else {
    
    
                        upIndex++;
                    }
                    currentPointId = event.getPointerId(upIndex);
                    //记录位置,解决跳动
                    downX = event.getX(upIndex);
                    downY = event.getY(upIndex);
                    lastOffsetX = offsetX;
                    lastOffsetY = offsetY;
                }

                break;

        }
        return true;
    }
}

efecto final:

Supongo que te gusta

Origin blog.csdn.net/weixin_40652755/article/details/127349899
Recomendado
Clasificación