How to detect single touch as well as multi-touch

2019-02-03 11:08发布

问题:

I used the following code to detect the single finger touch and double finger touch. The code detects the double finger touch (when count==2).

I need to do some action on single touch also. If I touch the screen with one finger it doesn't go for the else part. What have I done wrong in this code?

@Override
public boolean onTouchEvent(MotionEvent event) {
    int action = event.getAction() & MotionEvent.ACTION_MASK;
    switch (action) {
        case MotionEvent.ACTION_POINTER_UP: {
            int count = event.getPointerCount();
            Log.v("count >>", count + "");
            if (count == 2) {
                // some action
            } else {
                Log.v("count not equal to 2", "not 2");
            }
            break;
        }
    }
    return true;
}

Update:

I used this code inside an Activity. I need to detect single touch, multitouch.

In my activity, I have two images, one on the left and one on the right. If I click the image some process has to do. If I use two fingers, I need to re-size the images, according to the scale-factor.

This is the code that I've used:

Updated:

package com.pinch.detect;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

import android.app.Activity;
import android.content.Context;
import android.content.ContextWrapper;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.util.FloatMath;
import android.util.Log;
import android.view.Display;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.view.ScaleGestureDetector.SimpleOnScaleGestureListener;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

public class PinchDetectorActivity extends Activity {
    TextView textGestureAction;
    ImageView img1, img2;
    static Bitmap bm, bm1;

    boolean multiTouch = false;
    Context context;
    float scaleFactor = 0.0f;
    float lastscale;
    int scr_width, scr_height;
    Display display;
    private ScaleGestureDetector scaleGestureDetector;

    /** Called when the activity is first created. */

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        textGestureAction = (TextView) findViewById(R.id.GestureAction);
        img1 = (ImageView) findViewById(R.id.img_left);
        img2 = (ImageView) findViewById(R.id.img_right);
        display = getWindowManager().getDefaultDisplay();

        scr_width = display.getWidth();
        scr_height = display.getHeight();
        Log.v("width >>", Integer.toString(scr_width));
        Log.v("height >>", Integer.toString(scr_height));

        bm = BitmapFactory.decodeResource(this.getResources(),
                R.drawable.fiction1);
        img1.setImageBitmap(bm);
        bm1 = BitmapFactory.decodeResource(this.getResources(),
                R.drawable.fiction2);
        img2.setImageBitmap(bm1);
        img1.setScaleType(ImageView.ScaleType.FIT_START);
        img2.setScaleType(ImageView.ScaleType.FIT_END);

        scaleGestureDetector = new ScaleGestureDetector(this,
                new MySimpleOnScaleGestureListener());



        img1.setOnTouchListener(new OnTouchListener(){

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                // TODO Auto-generated method stub
                return false;
            }

        });
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        scaleGestureDetector.onTouchEvent(event);
        int index = event.getActionIndex();
        Log.v("Index value ",index+"");
        int action = event.getAction();

        if (index == 1) {
            multiTouch = true;
            System.out.println("mutli1");
        } else if (multiTouch == false) {
            System.out.println("single1");
            img1.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    Log.v("clicked image1","img1>>>");
                }
            });
            img2.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) { // TODO Auto-generated method
                    Log.v("clicked image2","img2>>>");
                }
            });
        }

         else if (action == MotionEvent.ACTION_MOVE && multiTouch) {
            System.out.println("mutli2");


            if (scaleFactor > 1) {

                Bitmap resizedbitmap = Bitmap.createScaledBitmap(bm,
                        scr_width / 2, scr_height, true);
                img1.setImageBitmap(resizedbitmap);
                Bitmap resizedbitmap1 = Bitmap.createScaledBitmap(bm1,
                        scr_width / 2, scr_height, true);
                img2.setImageBitmap(resizedbitmap1);
                Log.v("width >>", Integer.toString(scr_width));
                Log.v("height >>", Integer.toString(scr_height));

            } else if(scaleFactor<1){

                Log.v("width >>", Integer.toString(scr_width));
                Log.v("height >>", Integer.toString(scr_height));
                if (scr_width >= 640) {
                    Bitmap resizedbitmap = Bitmap.createScaledBitmap(bm,
                            scr_height + 90, scr_height, true);

                    img1.setImageBitmap(resizedbitmap);

                    Bitmap resizedbitmap1 = Bitmap.createScaledBitmap(bm1,
                            scr_height + 90, scr_height, true);

                    img2.setImageBitmap(resizedbitmap1);
                } else {
                    Bitmap resizedbitmap = Bitmap.createScaledBitmap(bm,
                            scr_height, scr_height + 30, true);

                    img1.setImageBitmap(resizedbitmap);

                    Bitmap resizedbitmap1 = Bitmap.createScaledBitmap(bm1,
                            scr_height, scr_height + 30, true);

                    img2.setImageBitmap(resizedbitmap1);
                }

            }



        } else if (action == MotionEvent.ACTION_MOVE && !multiTouch)
        {
            System.out.println("single2");
        }

        return super.onTouchEvent(event);

    }

    public class MySimpleOnScaleGestureListener extends
            SimpleOnScaleGestureListener {

        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            // TODO Auto-generated method stub

            scaleFactor = detector.getScaleFactor();

            Log.v("scaleFactor >>>", scaleFactor + "");

            return true;
        }
    }

}

and my main.xml is

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:id="@+id/pinchlay" >


    <ImageView
        android:id="@+id/img_left"
        android:layout_width="320dp"
        android:layout_height="fill_parent"
        />

    <ImageView
        android:id="@+id/img_right"
        android:layout_width="320dp"
        android:layout_height="fill_parent"
        android:layout_alignParentRight="true"
        />

</RelativeLayout>

回答1:

If I understand correctly, single touch will not cause ACTION_POINTER_UP, but ACTION_UP. That is why it doesn't detect the single finger. See this post for reference: https://stackoverflow.com/a/4269592/1084813



回答2:

As the previous poster suggested, there are separate events for a single touch pointer up as opposed to action up. The sequence of motionEvents is as follows:

  1. ACTION_DOWN
  2. (ACTION_MOVE)
  3. ACTION_POINTER_DOWN
  4. (ACTION_MOVE)
  5. (ACTION_POINTER_ID_SHIFT)
  6. (repeat for as many fingers as come down)
  7. ACTION_POINTER_UP
  8. (repeat until only one finger remains)
  9. ACTION_UP

Here's a good resource for understanding multitouch events, but the idea and easiest way to catch them all is to use a switch statement. I use this TouchImageView I found here. what it does is allows for scaling, and panning, and if you just click it, it performs the click, so you can add that processing code into an OnClickListener() or just insert it at line 125.



回答3:

Here is the code for detecting points and drawing each points in different color get compete example here

public class custom_view extends View {



private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);

public int cu = 0;



final int MAX_NUMBER_OF_POINT = 10;

float[] x = new float[MAX_NUMBER_OF_POINT];

float[] y = new float[MAX_NUMBER_OF_POINT];

boolean[] touching = new boolean[MAX_NUMBER_OF_POINT];



public custom_view(Context context, AttributeSet attrs, int defStyle) {

    super(context, attrs, defStyle);

    init();

}

public custom_view(Context context, AttributeSet attrs) {

    super(context, attrs);

    init();

}



public custom_view(Context context) {

    super(context);

    init();

}

void init() {

    paint.setStyle(Paint.Style.STROKE);

    paint.setStrokeWidth(40);

}

@Override

protected void onDraw(Canvas canvas) {

    for (int i = 0; i < MAX_NUMBER_OF_POINT; i++) {

        if (touching[i]) {

            switch (i) {

            case 1:

                paint.setColor(Color.BLUE);

                break;

            case 2:

                paint.setColor(Color.RED);

                break;

            case 3:

                paint.setColor(Color.CYAN);

                break;

            case 4:

                paint.setColor(Color.GREEN);

                break;

            case 5:

                paint.setColor(Color.YELLOW);

                break;

            case 6:

                paint.setColor(Color.MAGENTA);

                break;

            case 7:

                paint.setColor(Color.DKGRAY);

                break;

            case 8:

                paint.setColor(Color.LTGRAY);

                break;

            case 9:

                paint.setColor(Color.GREEN);

                break;

            case 10:

                paint.setColor(Color.BLACK);



                break;

                }

            canvas.drawCircle(x[i], y[i], 70f, paint);

        }

    }

}

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

    setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),

            MeasureSpec.getSize(heightMeasureSpec));

}

@Override

public boolean onTouchEvent(MotionEvent event) {

    int action = (event.getAction() & MotionEvent.ACTION_MASK);

    int pointCount = event.getPointerCount();

    for (int i = 0; i < pointCount; i++) {

        int id = event.getPointerId(i);



        if (id < MAX_NUMBER_OF_POINT) {

            x[id] = (int) event.getX(i);

            y[id] = (int) event.getY(i);



            if ((action == MotionEvent.ACTION_DOWN)

                    || (action == MotionEvent.ACTION_POINTER_DOWN)

                    || (action == MotionEvent.ACTION_MOVE)) {

                touching[id] = true;

            } else {

                touching[id] = false;

                }

        }

    }

    invalidate();

    return true;

}

}



回答4:

Have you tried using a OnDoubleTapListener ?

Found this tutorial that can be helpful too showing how to detect a single tap and a double tap.



回答5:

I cannot reproduce count==2 for single touch. It is 1 for me for single touch. Try debug your code on real device and see what you actually have.

Use event.getPointerId(i) along with event.getPointerCount() and see what these actual points of touch are.



回答6:

You must need to user PointerID for MultiTouch. See this Complete Example

private View.OnTouchListener OnTouchListener = new View.OnTouchListener() {
        public boolean onTouch(View view, MotionEvent event) { 
            int pointerIndex = ((event.getAction() & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT);
            int action = event.getAction() & MotionEvent.ACTION_MASK;
            int pointerId = event.getPointerId(pointerIndex);
            Log.i("", "Pointer ID = " + pointerId);
            switch (action) {
            case MotionEvent.ACTION_POINTER_UP: {

                break;
            }
            }
            return true;
        }
    };


回答7:

You checked that ACTION_DOWN but at the same time you also need to check ACTION_POINTER_DOWN, which makes sense more clear. try it

 @Override
    public boolean onTouchEvent(MotionEvent event) {    
         scaleGestureDetector.onTouchEvent(event);

         int action = event.getAction();

         if( (action == MotionEvent.ACTION_DOWN) && (action != MotionEvent.ACTION_POINTER_DOWN))
             // Single touch
        else if ( action == MotionEvent.ACTION_POINTER_DOWN)
            //multi touch

        return true;
}   

Edited

boolean multiTouch=false;
 @Override
public boolean onTouchEvent(MotionEvent event) {
    // TODO Auto-generated method stub

    int index= event.getActionIndex();
    int action = event.getAction();

    if( index ==1)
    {
        multiTouch=true;
        System.out.println("mutli");
    }
    else if (multiTouch==false)
        System.out.println("single");
    //else if (action== MotionEvent.ACTION_POINTER_1_UP)
    //  multiTouch=false;
    else if (action== MotionEvent.ACTION_MOVE && multiTouch)
        System.out.println("mutli");
    else if (action== MotionEvent.ACTION_MOVE && !multiTouch)
        System.out.println("single");

        return super.onTouchEvent(event);
}

EDITED:

 package com.pinch.detect;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

import android.app.Activity;
import android.content.Context;
import android.content.ContextWrapper;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.util.FloatMath;
import android.util.Log;
import android.view.Display;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.view.ScaleGestureDetector.SimpleOnScaleGestureListener;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

public class PinchDetectorActivity extends Activity {
    TextView textGestureAction;
    ImageView img1, img2;
    static Bitmap bm, bm1;

boolean multiTouch = false;
Context context;
float scaleFactor = 0.0f;
float lastscale;
int scr_width, scr_height;
Display display;
private ScaleGestureDetector scaleGestureDetector;

/** Called when the activity is first created. */

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    textGestureAction = (TextView) findViewById(R.id.GestureAction);
    img1 = (ImageView) findViewById(R.id.img_left);
    img2 = (ImageView) findViewById(R.id.img_right);
    display = getWindowManager().getDefaultDisplay();

    scr_width = display.getWidth();
    scr_height = display.getHeight();
    Log.v("width >>", Integer.toString(scr_width));
    Log.v("height >>", Integer.toString(scr_height));

    bm = BitmapFactory.decodeResource(this.getResources(),
            R.drawable.fiction1);
    img1.setImageBitmap(bm);
    bm1 = BitmapFactory.decodeResource(this.getResources(),
            R.drawable.fiction2);
    img2.setImageBitmap(bm1);
    img1.setScaleType(ImageView.ScaleType.FIT_START);
    img2.setScaleType(ImageView.ScaleType.FIT_END);

    scaleGestureDetector = new ScaleGestureDetector(this,
            new MySimpleOnScaleGestureListener());



    img1.setOnTouchListener(new OnTouchListener(){

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            // TODO Auto-generated method stub
            system.out.println("Image 1 touched");
            return false;
        }

    });



img2.setOnTouchListener(new OnTouchListener(){

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            // TODO Auto-generated method stub
            system.out.println("Image 2 touched");
            return false;
        }

    });
}

@Override
public boolean onTouchEvent(MotionEvent event) {

    scaleGestureDetector.onTouchEvent(event);
    int index = event.getActionIndex();
    Log.v("Index value ",index+"");
    int action = event.getAction();

    if (index == 1) {
        multiTouch = true;
        System.out.println("mutli1");
    } else if (multiTouch == false) {
        System.out.println("single1");
        img1.onTouch(view, event);

    }

     else if (action == MotionEvent.ACTION_MOVE && multiTouch) {
        System.out.println("mutli2");
         img2.onTouch(view, event);

        if (scaleFactor > 1) {

            Bitmap resizedbitmap = Bitmap.createScaledBitmap(bm,
                    scr_width / 2, scr_height, true);
            img1.setImageBitmap(resizedbitmap);
            Bitmap resizedbitmap1 = Bitmap.createScaledBitmap(bm1,
                    scr_width / 2, scr_height, true);
            img2.setImageBitmap(resizedbitmap1);
            Log.v("width >>", Integer.toString(scr_width));
            Log.v("height >>", Integer.toString(scr_height));

        } else if(scaleFactor<1){

            Log.v("width >>", Integer.toString(scr_width));
            Log.v("height >>", Integer.toString(scr_height));
            if (scr_width >= 640) {
                Bitmap resizedbitmap = Bitmap.createScaledBitmap(bm,
                        scr_height + 90, scr_height, true);

                img1.setImageBitmap(resizedbitmap);

                Bitmap resizedbitmap1 = Bitmap.createScaledBitmap(bm1,
                        scr_height + 90, scr_height, true);

                img2.setImageBitmap(resizedbitmap1);
            } else {
                Bitmap resizedbitmap = Bitmap.createScaledBitmap(bm,
                        scr_height, scr_height + 30, true);

                img1.setImageBitmap(resizedbitmap);

                Bitmap resizedbitmap1 = Bitmap.createScaledBitmap(bm1,
                        scr_height, scr_height + 30, true);

                img2.setImageBitmap(resizedbitmap1);
            }

        }



    } else if (action == MotionEvent.ACTION_MOVE && !multiTouch)
    {
        System.out.println("single2");
    }

    return super.onTouchEvent(event);

}

public class MySimpleOnScaleGestureListener extends
        SimpleOnScaleGestureListener {

    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        // TODO Auto-generated method stub

        scaleFactor = detector.getScaleFactor();

        Log.v("scaleFactor >>>", scaleFactor + "");

        return true;
    }
}

}

Latest:

if (index == 1) {
        multiTouch = true;
        img2.onTouch(view, event);
    System.out.println("mutli1");
} else if (multiTouch == false) {
    System.out.println("single1");
    img1.onTouch(view, event);

}

 else if (action == MotionEvent.ACTION_MOVE && multiTouch) {
    System.out.println("mutli2");