处理 TouchEvents concurrentModificationException android

Handling TouchEvents concurrentModificationException android

我试图在 SurfaceView canvas 中使用 canvas 在屏幕上绘制一些气球,我成功地在 canvas 上绘制了多个气球并通过交换不同的图像来制作动画。问题是当我尝试触摸 Oneballoon 时,我需要将它从屏幕上删除。这里我遇到了这个异常,我被卡住了

代码: 主要活动:

package com.pradhul.game.touchball;
import android.app.Activity;
import android.os.Bundle;

public class MainActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(new GameView(this));
    /*TODO Hide the bottom navigation bar */
}

}

GameView.java

package com.pradhul.game.touchball;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class GameView extends SurfaceView implements    SurfaceHolder.Callback, View.OnTouchListener {
private static final int NUM_OF_BALLOONS = 5; //TODO for more than one  balloons animations dont work(?)
/*use SurfaceView because we want complete control over the screen.
    * unlike extending View class the oDraw() Method will not be called automatically
    * from the method onSurfaceCreated() we have to call it Manually and pass a canvas object into it
    * */
private final SurfaceHolder holder;
private GameLoopThread gameLoopThread;
private List<Balloon> balloons =  new ArrayList<>();

public GameView(Context context) {
    super(context);
    gameLoopThread = new GameLoopThread(this);
    holder = getHolder();
    holder.addCallback(this);
    createBalloons(NUM_OF_BALLOONS);
    this.setOnTouchListener(this);
}

private void createBalloons(int count) {
    for(int i=0 ; i< count ;i++){
        balloons.add(createBalloon());
    }
}

@Override
protected void onDraw(Canvas canvas) {
    if(canvas != null) {
        canvas.drawColor(Color.WHITE);
        for(Balloon balloon : balloons){
            try {
                gameLoopThread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            balloon.onDraw(canvas);
        }
    }
}

@SuppressLint("WrongCall")
@Override
public void surfaceCreated(SurfaceHolder holder) {
    /*this is called when the view is created*/
    gameLoopThread.setRunning(true);
    gameLoopThread.start();
}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
    /*pausing game Thread*/
    gameLoopThread.setRunning(false);
    while (true){
        try {
            gameLoopThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
private  Balloon createBalloon(){
    return new Balloon(this);
}


@Override
public synchronized boolean onTouch(View v, MotionEvent event) {
    Log.d("OnTouch real -", "x: " + event.getX() + ", Y: " + event.getY());
 /*   for (int i = balloons.size()-1; i >= 0; i--) {
        Balloon balloon = balloons.get(i);
        Log.d("OnTouch collision -", !balloon.isCollision(event.getX(), event.getY())+"");
        if (!balloon.isCollision(event.getX(), event.getY())) {
            balloons.remove(0);
            break;
        }
    }*/
    Iterator<Balloon>  balloonIterator = balloons.iterator();
    while(balloonIterator.hasNext()){
        Balloon balloon = balloonIterator.next();
        balloons.remove(0);
    }
    return true;
}
}

GameLoopThread.java

package com.pradhul.game.touchball;
import android.annotation.SuppressLint;
import android.graphics.Canvas;

public class GameLoopThread extends Thread {

private GameView view;
private boolean running = false;

public GameLoopThread(GameView view){
    this.view = view;
}
public void setRunning(boolean run){
    running = run;
}

@SuppressLint("WrongCall")
public void run(){
    while (running){
        Canvas canvas = null;
        try{
            canvas = view.getHolder().lockCanvas();
            synchronized (view.getHolder()){
                view.onDraw(canvas);
            }
        }finally{
            if(canvas != null) {
                view.getHolder().unlockCanvasAndPost(canvas);
            }
        }
    }

}
}

Balloon.java

package com.pradhul.game.touchball;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.util.Log;
import java.util.Random;

public class Balloon {
private static final int BALLOON_SPEED = 10;
private int y = 0;
private int x = 0;
private int speed = 1;
private GameView gameView;
private Bitmap balloon;
public Bitmap[] normalBalloons;
private int balloonIndex = 0;

private int normalImages[] = {R.drawable.normal_01,R.drawable.normal_02,R.drawable.normal_03,
        R.drawable.normal_04,R.drawable.normal_05,R.drawable.normal_06,R.drawable.normal_07,
        R.drawable.normal_08,
};
private int crackingImages[] = {R.drawable.crack_01,R.drawable.crack_02,R.drawable.crack_03,
        R.drawable.crack_04, R.drawable.crack_05,R.drawable.crack_04,R.drawable.crack_03,
        R.drawable.crack_02
};
private boolean reverseSwap = false;


public Balloon(GameView gameView){
    this.gameView = gameView;
    normalBalloons = new Bitmap[8];
    setUpImages();
}

public  void onDraw(Canvas canvas){
    /*draws the balloon in canvas */
    animateBalloon();
    update(canvas.getWidth());
    canvas.drawBitmap(balloon, x, y, null);
}

public boolean isCollision(float x2, float y2) {
    return x2 > x && x2 < x + balloon.getWidth() && y2 > y && y2 < y + balloon.getHeight();
}

private int getRandomX(int maxVal) {
    Random rand = new Random();
    return rand.nextInt(maxVal);
}

private void animateBalloon() {
    /*Animates the balloon by swapping resource image at each call*/
    this.balloon = getBalloons();
    Log.d("Balloon",balloonIndex % normalBalloons.length + "");
}

private void update(int canvasWidth) {
    /*updates the y position for moving the balloon*/
    if (y <= 0){
        /*so that the balloon starts from bottom
        * gameView will return a height only after the View is ready
        * getting 0 in constructor of this class*/
        y = gameView.getHeight();
        /*x is assigned a random between the width od the canvas
        * so that the balloons will appear random positions from below*/
        x = getRandomX(canvasWidth - balloon.getWidth());
    }
    if (y > gameView.getHeight() - balloon.getHeight() - speed) {
        speed = -BALLOON_SPEED;
    }
    y = y + speed;
    Log.d("Balloon","Positions:"+x+","+y);
}

private Bitmap getBalloons() {
    if(balloonIndex == normalBalloons.length-1) {
        reverseSwap = true;
    }
    if(balloonIndex == 0){
        reverseSwap = false;
    }
    balloonIndex = reverseSwap?balloonIndex-1:balloonIndex+1;
    return normalBalloons[balloonIndex];
}

private void setUpImages() {
    /*setting up resources array*/
    for(int count =0; count < normalImages.length; count++){
        Bitmap balloon =   BitmapFactory.decodeResource(gameView.getResources(), normalImages[count]);
        normalBalloons[count] = balloon;
    }


}
}

我很困惑为什么它会导致这样的错误,任何人都可以看看它并建议我一个解决方案,这是正确的删除方法吗?

请分享任何建议

谢谢

此异常是由于列表气球的并发修改。

只要您触摸表面视图 onDraw() 就会调用 onTouch(),您正在其中处理列表气球。

我认为你应该将触摸侦听器添加到气球而不是 GameView。

删除方法不对。

如果你遍历一个 Collection / List,并想删除当前元素,你必须使用 Iterator.remove() 方法。

在您的情况下,只需调用 balloonIterator.remove() 而不是 balloons.remove(0)

更简单,因为您想从列表中删除所有元素 - 您应该简单地调用 balloons.clear() 并完全删除循环。

好的, 我需要将我所有的代码包装在一个同步的持有者中才能工作 (了解不多)

@Override
public boolean onTouch(View v, MotionEvent event) {
    Log.d("OnTouch","x:"+event.getX()+"Y:"+event.getY());
    synchronized (getHolder()){
        for (int i=0 ;i<balloons.size();i++){
            balloons.remove(0);
            break;
        }
    }
    return true;
}