我正在尝试创建一个纸牌游戏,在游戏中有一个功能,通过相互接触交换位置两张牌 . 为此,我使用了OnTouch事件和手势检测器类 .

但是,OnTouch事件未按预期工作 . 如果我在两个图像的触摸之间有1-2秒的延迟,我得到正确的输出 . 但是如果我在第一次触摸后立即触摸第二张图像,我就无法获得正确的输出 .

以下是我的截图:

Original Image

Output with delay touch

Output with immediate touch

此外它并不总是发生,在最后10次触摸中它只发生在6次 . 所以我无法理解为什么会发生这种情况 .

以下是我的代码:

MySurfaceView class

public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback {

private static final String TAG = MySurfaceView.class.getSimpleName();  // To get name of class in Logging
private Context context;
private DisplayMetrics metrics;
private MySurfaceViewThread thread;
private int Screen_Width;
private int Screen_Height;
private float density;
private int Card_Width;
private int Card_Height;
private int Screen_Center_X;
private int Screen_Center_Y;
private int Screen_Bottom_Middle_X;
private int Screen_Bottom_Middle_Y;
private Deck MainPlayer;
private Deck DeatlDeck;
private Deck DiscardedDeck;
private Bitmap BlueBackCard;
private int DealtDeck_CurrentX;
private int DealtDeck_CurrentY;
private int DiscardedDeck_CurrentX;
private int DiscardedDeck_CurrentY;
private boolean isLongTouched=false;
final Handler longpressedhandler= new Handler();
private Card touchedcard=null;
private int cardindex=-1;
private Card replacedcard=null;
private GestureDetector gestureDetector;
private long startclicktime;
private final int MIN_CLICK_DURATION=1000;
private ArrayList<Card> tempLongtouchList= new ArrayList<>();
private ArrayList<Integer> tempListindex= new ArrayList<>();



public MySurfaceView(Context context) {
    super(context);
    this.context = context;
    metrics = getResources().getDisplayMetrics();
    setWillNotDraw(false);
    gestureDetector = new GestureDetector(context, new GestureListener(this));
    init(context);
}

public MySurfaceView(Context context, AttributeSet attrs) {
    super(context, attrs);
    this.context = context;
    metrics = getResources().getDisplayMetrics();
    setWillNotDraw(false);
    gestureDetector = new GestureDetector(context, new GestureListener(this));
    init(context);
}

public MySurfaceView(Context context, AttributeSet attrs, int defstyles) {

    super(context, attrs, defstyles);
    this.context = context;
    metrics = getResources().getDisplayMetrics();
    setWillNotDraw(false);
    gestureDetector = new GestureDetector(context, new GestureListener(this));
    init(context);
}

private void init(Context context) {
    this.context = context;
    getHolder().addCallback(this);
    thread = new MySurfaceViewThread(getHolder(), this);
    setFocusable(true);

}

@Override
public void surfaceCreated(SurfaceHolder holder) {
    Log.d(TAG, "Inside Surface Created method");
    initializevariable();
    AllocatedCardList();
    thread.setRunning(true);
    thread.start();
}


@Override
public void surfaceDestroyed(SurfaceHolder holder) {
    boolean retry = true;
    thread.setRunning(false);
    while (retry) {
        try {
            thread.join();
            retry = false;
        } catch (InterruptedException e) {

        }
    }
}


@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

}

private void initializevariable() {
    Screen_Width = getWidth();
    Screen_Height = getHeight();
    density = metrics.density;
    Card_Width = (int) (125.0F * density);
    Card_Height = (int) (93.0F * density);
    Screen_Center_X = Screen_Width / 2;
    Screen_Center_Y = Screen_Height / 2;
    Screen_Bottom_Middle_X = Screen_Center_X - Card_Width;
    Screen_Bottom_Middle_Y = Screen_Height - Card_Height;
    BlueBackCard = DecodeSampleBitmapFromResource(getResources(), Card.GetBlueBackCardImageId(context), Card_Width, Card_Height);
    MainPlayer = new Deck();
    DeatlDeck = new Deck();
    DiscardedDeck = new Deck();
    DealtDeck_CurrentX = Screen_Center_X - Card_Width;
    DealtDeck_CurrentY = Screen_Center_Y - Card_Height / 2;
    DiscardedDeck_CurrentX= Screen_Center_X+Card_Width;
    DiscardedDeck_CurrentY= Screen_Center_Y- Card_Height/2;
}


private void AllocatedCardList() {
    Log.d(TAG, "inside AllocatedCardList method");
    for (Suit suit : Suit.values()) {
        for (Rank rank : Rank.values()) {
            DeatlDeck.add(new Card(rank, suit, false, DealtDeck_CurrentX, DealtDeck_CurrentY , BlueBackCard));
        }

    }
    DealCards();
}

private void DealCards() {
    Log.d(TAG, "Inside Deal Card method");
    MainPlayer.add(DeatlDeck.Deal(true));
    //MainPlayer.add(DeatlDeck.Deal(true));
    // MainPlayer.add(DeatlDeck.Deal(true));

}

   // @SuppressLint("ClickableViewAccessibility")
   @Override
    public boolean onTouchEvent(MotionEvent event) {
    synchronized (thread.getMySurfaceHolder()) {
        Log.d(TAG, "Inside Touch Event");
        gestureDetector.onTouchEvent(event);
    }
    return true;




}

/**
 * Method to swap Main Player card with
 * either Dealt Deck or with Discarded
 * Deck, this method will check whether
 * user has touched Main Player deck,
 * Dealt Deck or Discarded Deck, if
 * user has touched Main Deck using
 * long touch it will add those touch
 * cards to temporary deck, till
 * user does not touch Discarde/
 * Dealt Deck
 * @param e: to determine which type of event has performed.
 */
public void swapSingleTouchCard(MotionEvent e) {
    float lasttouched_X, lasttouched_Y;
    Card localcard;
    int index = -1;
    lasttouched_X = e.getX();
    lasttouched_Y = e.getY();
     if (((lasttouched_X >= DiscardedDeck_CurrentX && lasttouched_X < (DiscardedDeck_CurrentX + DiscardedDeck.getCard().getImage().getWidth())))==false &&isLongTouched) {
         addTouchedCardToLongTouched(e);
    }
    else {
        if (touchedcard == null)       // To find single touch card
        {
            index = cardTouched((int) lasttouched_X, (int) lasttouched_Y);
            if (index > -1) {
                touchedcard = MainPlayer.getCard(index);
                cardindex = index;
            }
        } else if (touchedcard != null && ((lasttouched_X >= DiscardedDeck_CurrentX && lasttouched_X < (DiscardedDeck_CurrentX + DiscardedDeck.getCard().getImage().getWidth()))))  //&& (lasttouched_Y>=DiscardedDeck_CurrentY && lasttouched_Y<(DiscardedDeck_CurrentY+ DiscardedDeck.getCard().getImage().getWidth()))))
        {
             replacedcard = DiscardedDeck.Deal(true);
            Card swapcard = MainPlayer.swapCard(replacedcard, cardindex);
            swapcard.setCurrent_X(DiscardedDeck_CurrentX);
            swapcard.setCurrent_Y(DiscardedDeck_CurrentY);
            DiscardedDeck.add(swapcard);
            touchedcard = null;
            cardindex = -1;
        }
    }
}

private int cardTouched(int lasttouched_x, int lasttouched_y) {
    int index=0;
    Card localcard=null;
    while (index<MainPlayer.Count())
    {
        localcard=MainPlayer.getCard(index);
        if(lasttouched_x>= localcard.getCurrent_X() && lasttouched_x<(localcard.getCurrent_X()+localcard.getImage().getWidth())) //&& (lasttouched_y>=localcard.getCurrent_Y() &&lasttouched_y <(localcard.getCurrent_Y()+localcard.getImage().getWidth())))
        {
            return index;
        }
        index++;
    }
    return -1;
}

public void addTouchedCardToLongTouched(MotionEvent event)
{

    float lasttouched_X, lasttouched_Y;
    int index=-1;
    lasttouched_X=event.getX();
    lasttouched_Y=event.getY();
    index=cardTouched((int)lasttouched_X,(int)lasttouched_Y);
    isLongTouched=true;
    if(index>-1)
    {
        tempLongtouchList.add(MainPlayer.getCard(index));
        tempListindex.add(index);

    }




}

public  void render(Canvas canvas)
{
    canvas.drawColor(Color.TRANSPARENT,PorterDuff.Mode.CLEAR);
    drawDealtDeck(canvas);

    if(DiscardedDeck.Count()==0) {          //To add card in discarded deck only first time
        setDiscardedDeck();
    }
    drawDiscardedDeck(canvas);
    setMainPlayer();
    DrawMainPlayerDeck(canvas);
}
private void drawDealtDeck (Canvas canvas){
    Card localcard = DeatlDeck.getCard();
    canvas.drawBitmap(localcard.getImage(), localcard.getCurrent_X(), localcard.getCurrent_Y(), null);
}

private void drawDiscardedDeck(Canvas canvas) {
    Log.d(TAG,"Inside Draw Discarded deck");
    Card localcard= DiscardedDeck.getCard();
    canvas.drawBitmap(localcard.getImage(),localcard.getCurrent_X(),localcard.getCurrent_Y(),null);

}

private void setDiscardedDeck() {
Log.d(TAG,"Inside set Discarded Deck");
Card localcard;
Bitmap localimage;
localcard=DeatlDeck.Deal(true);
localimage= DecodeSampleBitmapFromResource(getResources(),localcard.GetImageId(context),Card_Width,Card_Height);
localcard.setImage(localimage);
localcard.setCurrent_X(DiscardedDeck_CurrentX);
localcard.setCurrent_Y(DiscardedDeck_CurrentY);
DiscardedDeck.add(localcard);

}


private void setMainPlayer ()
    {
        Log.d(TAG, "Inside Set Main Player Method");
        Card localcard = null;
        Bitmap localimage = null;
        int currentiteration = 0;
        int Down_Card_Gap = 0;
        int Down_Card_Gap_positive = 0;
        int Down_Card_Gap_negative = 0;
        while (currentiteration < MainPlayer.Count()) {
            localcard = MainPlayer.getCard(currentiteration);
            localimage = DecodeSampleBitmapFromResource(getResources(), localcard.GetImageId(context), Card_Width, Card_Height);
            localcard.setImage(localimage);
            localcard.setCurrent_Y(Screen_Height - localcard.getImage().getHeight());
            MainPlayer.setCurrentCard(localcard, currentiteration);
            currentiteration++;
            if (Down_Card_Gap >= 0) {
                Down_Card_Gap_positive = Down_Card_Gap;
                localcard.setCurrent_X(Screen_Center_X + Down_Card_Gap_positive);
                Down_Card_Gap += 75;
            } else {
                Down_Card_Gap_negative = Down_Card_Gap;
                localcard.setCurrent_X(Screen_Center_X + Down_Card_Gap_negative);
            }
            Down_Card_Gap *= -1;

        }

    }

    private void DrawMainPlayerDeck (Canvas canvas)
    {
        Log.d(TAG, " Inside Draw Main Player Deck");
        Card localcard;
        int currentiteration = 0;
        while (currentiteration < MainPlayer.Count()) {
            localcard = MainPlayer.getCard(currentiteration);
            canvas.drawBitmap(localcard.getImage(), localcard.getCurrent_X(), localcard.getCurrent_X(), null);
            currentiteration++;
        }

    }


    private Bitmap DecodeSampleBitmapFromResource (Resources res,int resId,
    int reqWidth, int reqHeight){

        // First decode with inJustDecodeBounds=true to check dimensions
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(res, resId, options);

        // Calculate inSampleSize
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

        // Decode bitmap with inSampleSize set
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeResource(res, resId, options);
    }

    private int calculateInSampleSize (BitmapFactory.Options options,int reqWidth, int reqHeight)
    {
        // Raw height and width of image
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;





       if (height > reqHeight || width > reqWidth) {


            int heightratio= (int)Math.round((double)height/reqHeight);
            int widthratio= (int)Math.round((double)width/reqWidth);
            inSampleSize= heightratio < widthratio ? widthratio : heightratio;
        }

          return inSampleSize;
    }
}

GestureListener class

class GestureListener extends GestureDetector.SimpleOnGestureListener{
private static final String TAG = GestureListener.class.getSimpleName();  // To get name of class in Logging
    MySurfaceView mySurfaceView;



    public GestureListener(MySurfaceView paramMySurfaceView)
    {
        mySurfaceView=paramMySurfaceView;
    }

@Override
public void onLongPress(MotionEvent e) {

    Log.d(TAG,"Inside Long Pressed event");
        mySurfaceView.addTouchedCardToLongTouched(e);
}


@Override
public boolean onDown(MotionEvent e) {

  // don't return false here or else none of the other
  // gestures will work

    return  false;
}


@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
    mySurfaceView.swapSingleTouchCard(e);
    return false;
}

@Override
public boolean onDoubleTap(MotionEvent e) {
    Log.d(TAG,"Inside On Double Tap event");
    return false;
}
}

Thread class

public class MySurfaceViewThread extends Thread {
private MySurfaceView mySurfaceView;
private SurfaceHolder mySurfaceHolder;
boolean running;

public MySurfaceViewThread(SurfaceHolder paramSurfaceHolder, MySurfaceView paramSurfaceView)
{
    mySurfaceHolder=paramSurfaceHolder;
    mySurfaceView=paramSurfaceView;
}

public void setRunning(boolean run){
    running=run;

}

public SurfaceHolder getMySurfaceHolder()
{
    return mySurfaceHolder;
}
@SuppressLint("WrongCall")
@Override
public void run() {
    Canvas c;
    while(running)
    {
        c=null;
        try{
            c= mySurfaceHolder.lockCanvas();
            synchronized (mySurfaceHolder) {
                mySurfaceView.render(c);
            }
        }
        catch(Exception e)
        {
            Log.e("Thread Class run method","exception",e);
        }

        finally {
            if (c!=null)
            {
                mySurfaceHolder.unlockCanvasAndPost(c);
            }
        }

    }
}
}