Simultaneous display of two animations in SurfaceView

Viewed 78

I'm trying to get two animations to show at the same time. The application builds successfully but crashes in the emulator.

I have created an Anim class in a thread that handles the Animations in a while loop. I think there is a problem with my draw method but I cant seem to figure it out.

I appreciate any help on this, thank you

Error Log

java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.myproject/com.example.myproject.MainActivity}: java.lang.IllegalArgumentException: Cannot add a null child view to a ViewGroup

and

java.lang.ArrayIndexOutOfBoundsException: length=2; index=2
        at com.example.my_project.PlayActivity$surfaceView$Anim.run(PlayActivity.java:109)

CODE

public class PlayActivity extends AppCompatActivity {
surfaceView view;


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    view = new surfaceView(this);

    setContentView(view);



public class surfaceView extends SurfaceView {


    public surfaceView(Context context) {
        super(context);
        new Anim().start();
    }


    private class Anim extends Thread {
        int counter = 0;
        int counter2 = 0;



        @Override
        public void run() {
            long last_updated_time = 0;
            long delay = 250;
            int[] purple_bird = {
                    R.drawable.bird1,
                    R.drawable.bird2
            };
            int[] red_bird = {
                    R.drawable.red1,
                    R.drawable.red2,
                    R.drawable.red3,
                    R.drawable.red4
            };


            while (true) {
                boolean playing = true;
                if (playing) {

                    long current_time = System.currentTimeMillis();
                    if (current_time > last_updated_time + delay) {
                        if ((counter >= 2) && (counter2 >= 4)) {
                            counter = 0;
                            counter2 = 0;


                        }
                        draw(purple_bird[counter], red_bird[counter2]);

                        last_updated_time = current_time;
                        counter++;
                        counter2++;
                    }
                }
            }




        }

        private void draw(int red_bird, int purple_bird) {

            SurfaceHolder holder = getHolder();
            Canvas canvas = holder.lockCanvas();

            if (canvas != null) {
                canvas.drawColor(Color.WHITE);
                Paint paint = new Paint();

                Bitmap purpleBird = BitmapFactory.decodeResource(getContext().getResources(), purple_bird);
                Bitmap redBird = BitmapFactory.decodeResource(getContext().getResources(), red_bird);


                Bitmap resizedRedBird = Bitmap.createScaledBitmap(redBird, (int) (redBird.getWidth() * 0.4), (int) (redBird.getHeight() * 0.4), true);
                Bitmap resizedPurpleBird = Bitmap.createScaledBitmap(purpleBird, (int) (purpleBird.getWidth() * 0.2), (int) (purpleBird.getHeight() * 0.2), true);

                canvas.drawBitmap(resizedRedBird, 500, 500, paint);
                canvas.drawBitmap(resizedPurpleBird, 100, 100, paint);

                holder.unlockCanvasAndPost(canvas);




            }

        }
    }





}

}

2 Answers

Because accessing a SurfaceView (with Canvas) is exclusive, you cannot draw simultaneously to it.

holder.lockCanvas();

(...) // the canvas is locked during this

holder.unlockCanvasAndPost(canvas);

So, your possible solution is to manipulate and draw both of red bird and blue bird in the same draw method (in between lock and unlock) of the same thread.


Edit

For another error 2:

if ((counter >= 2) && (counter2 >= 4)) {
    counter = 0;
    counter2 = 0;
}

This condition is wrong. Because only when both of counter and counter2 excessed, they are reset. They should be separated:

if (counter >= 2) {
    counter = 0;
}
if (counter2 >= 4) {
    counter2 = 0;
}

Drawing on a Surface has to be done USUALLY only from one Thread at a time.

If you want to draw from different Threads then you have to consider that "lockCanvas()" requires to upload ALL Surface pixels at each call, but you're just drawing each animation pixels without considering the other one. In this behaviour the "second" Thread will "override" the whole Surface pixels with only its own pixels and that's why you see a flickering effect.

I suggest you to use a common Bitmap where to draw on (and it will be the only Bitmap on which call "unlockCanvasAndPost()"), or just merge those two Threads in just one and handle both animations there.

Related