unable to add circles to canvas dynamically

2019-09-05 06:24发布

问题:

I tried to make a single ball bouncing to dynamic ball bouncing . Eg: here the number of circles is 50. But I am getting error while trying to make the circles dynamic (Model) .How do I make it work and make the model/circle dynamic.In this case 50 circles ? I really appreciate any help. Thanks in Advance.

package com.stuffthathappens.games;

import static android.hardware.SensorManager.DATA_X;
import static android.hardware.SensorManager.DATA_Y;
import static android.hardware.SensorManager.SENSOR_ACCELEROMETER;
import static android.hardware.SensorManager.SENSOR_DELAY_GAME;

import java.util.concurrent.TimeUnit;

import android.app.Activity;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.hardware.SensorListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.os.Vibrator;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.SurfaceHolder.Callback;

/**
 * This activity shows a ball that bounces around. The phone's 
 * accelerometer acts as gravity on the ball. When the ball hits
 * the edge, it bounces back and triggers the phone vibrator.
 */
@SuppressWarnings("deprecation")
public class BouncingBallActivity extends Activity implements Callback, SensorListener {
    private static final int BALL_RADIUS =20;
    private SurfaceView surface;
    private SurfaceHolder holder;

    private GameLoop gameLoop;
    private Paint backgroundPaint;
    private Paint ballPaint;
    private SensorManager sensorMgr;
    private long lastSensorUpdate = -1;


    private Paint ballPaintyellow;

     private BouncingBallModel[] model;

    int Totalcircles=50;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.bouncing_ball);

        for (int i = 0; i < Totalcircles; i++) {
            model[i] = new BouncingBallModel(BALL_RADIUS); 
        }

        surface = (SurfaceView) findViewById(R.id.bouncing_ball_surface);
        holder = surface.getHolder();
        surface.getHolder().addCallback(this);

        backgroundPaint = new Paint();
        backgroundPaint.setColor(Color.WHITE);

        ballPaint = new Paint();
        ballPaint.setColor(Color.BLUE);
        ballPaint.setAntiAlias(true);

        ballPaintyellow = new Paint();
        ballPaintyellow.setColor(Color.YELLOW);
        ballPaintyellow.setAntiAlias(true);
    }

    @Override
    protected void onPause() {
        super.onPause();

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

            model[i].setVibrator(null);
        }

        sensorMgr.unregisterListener(this, SENSOR_ACCELEROMETER);
        sensorMgr = null;

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

            model[i].setAccel(0, 0);
        }


    }

    @Override
    protected void onResume() {
        super.onResume();

        sensorMgr = (SensorManager) getSystemService(SENSOR_SERVICE);
        boolean accelSupported = sensorMgr.registerListener(this, 
                SENSOR_ACCELEROMETER,
                SENSOR_DELAY_GAME);

        if (!accelSupported) {
            // on accelerometer on this device
            sensorMgr.unregisterListener(this, SENSOR_ACCELEROMETER);
            // TODO show an error
        }

        // NOTE 1: you cannot get system services before onCreate()
        // NOTE 2: AndroidManifest.xml must contain this line:
        // <uses-permission android:name="android.permission.VIBRATE"/>
        Vibrator vibrator = (Vibrator) getSystemService(Activity.VIBRATOR_SERVICE);

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

            model[i].setVibrator(vibrator);
        }
    }

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


        for (int i = 0; i < Totalcircles; i++) {
            model[i].setSize(width, height);
        }
    }

    public void surfaceCreated(SurfaceHolder holder) {
        gameLoop = new GameLoop();
        gameLoop.start();
    }

    private void draw() {
        // thread safety - the SurfaceView could go away while we are drawing

        Canvas c = null;
        try {
            // NOTE: in the LunarLander they don't have any synchronization here,
            // so I guess this is OK. It will return null if the holder is not ready
            c = holder.lockCanvas();

            // this needs to synchronize on something
            if (c != null) {
                doDraw(c);
            }
        } finally {
            if (c != null) {
                holder.unlockCanvasAndPost(c);
            }
        }
    }

    private void doDraw(Canvas c) {
        int width = c.getWidth();
        int height = c.getHeight();
        c.drawRect(0, 0, width, height, backgroundPaint);

        ///

        float ballX[]=new float[50], ballY[]=new float[50];

        for (int i = 0; i < Totalcircles; i++) {
            synchronized (model[i].LOCK) {
                ballX[i] = model[i].ballPixelX;
                ballY[i] = model[i].ballPixelY;

            }
        }

        //

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

            c.drawCircle(ballX[i], ballY[i], BALL_RADIUS, ballPaint);
        }
        }


    public void surfaceDestroyed(SurfaceHolder holder) {
        try {

            for (int i = 0; i < Totalcircles; i++) {
                model[i].setSize(0,0);
            }
            gameLoop.safeStop();
        } finally {
            gameLoop = null;
        }
    }

    private class GameLoop extends Thread {
        private volatile boolean running = true;

        public void run() {
            while (running) {
                try {
                    //  don't like this hardcoding
                    TimeUnit.MILLISECONDS.sleep(5);

                    draw();

                    for (int i = 0; i < Totalcircles; i++) {
                        model[i].updatePhysics();
                    }

                } catch (InterruptedException ie) {
                    running = false;
                }
            }
        }

        public void safeStop() {
            running = false;
            interrupt();
        }
    }

    public void onAccuracyChanged(int sensor, int accuracy) {       
    }

    public void onSensorChanged(int sensor, float[] values) {
        if (sensor == SENSOR_ACCELEROMETER) {
            long curTime = System.currentTimeMillis();
            // only allow one update every 50ms, otherwise updates
            // come way too fast
            if (lastSensorUpdate == -1 || (curTime - lastSensorUpdate) > 50) {
                lastSensorUpdate = curTime;

                for (int i = 0; i < Totalcircles; i++) {
                    model[i].setAccel(values[DATA_X], values[DATA_Y]);
                }
            }
        }
    }
}

model.java

package com.stuffthathappens.games;

import java.util.concurrent.atomic.AtomicReference;

import android.os.Vibrator;

/**
 * This data model tracks the width and height of the playing field along 
 * with the current position of a ball. 
 */
public class BouncingBallModel {
    // the ball speed is meters / second. When we draw to the screen,
    // 1 pixel represents 1 meter. That ends up too slow, so multiply
    // by this number. Bigger numbers speeds things up.
    private final float pixelsPerMeter = 10;

    private final int ballRadius;

    // these are public, so make sure you synchronize on LOCK 
    // when reading these. I made them public since you need to
    // get both X and Y in pairs, and this is more efficient than
    // getter methods. With two getters, you'd still need to 
    // synchronize.
    public float ballPixelX, ballPixelY;

    private int pixelWidth, pixelHeight;

    // values are in meters/second
    private float velocityX, velocityY;

    // typical values range from -10...10, but could be higher or lower if
    // the user moves the phone rapidly
    private float accelX, accelY;

    /**
     * When the ball hits an edge, multiply the velocity by the rebound.
     * A value of 1.0 means the ball bounces with 100% efficiency. Lower
     * numbers simulate balls that don't bounce very much.
     */
    private static final float rebound = 0.8f;

    // if the ball bounces and the velocity is less than this constant,
    // stop bouncing.
    private static final float STOP_BOUNCING_VELOCITY = 2f;

    private volatile long lastTimeMs = -1;

    public final Object LOCK = new Object();

    private AtomicReference<Vibrator> vibratorRef =
        new AtomicReference<Vibrator>();

    public BouncingBallModel(int ballRadius) {
        this.ballRadius = ballRadius;
    }

    public void setAccel(float ax, float ay) {
        synchronized (LOCK) {
            this.accelX = ax;
            this.accelY = ay;
        }
    }

    public void setSize(int width, int height) {
        synchronized (LOCK) {
            this.pixelWidth = width;
            this.pixelHeight = height;
        }
    }

    public int getBallRadius() {
        return ballRadius;
    }

    /**
     * Call this to move the ball to a particular location on the screen. This
     * resets the velocity to zero, but the acceleration doesn't change so
     * the ball should start falling shortly.
     */
    public void moveBall(int ballX, int ballY) {
        synchronized (LOCK) {
            this.ballPixelX = ballX;
            this.ballPixelY = ballY;
            velocityX = 0;
            velocityY = 0;
        }
    }

    public void updatePhysics() {
        // copy everything to local vars (hence the 'l' prefix)
        float lWidth, lHeight, lBallX, lBallY, lAx, lAy, lVx, lVy;
        synchronized (LOCK) {
            lWidth = pixelWidth;
            lHeight = pixelHeight;
            lBallX = ballPixelX;
            lBallY = ballPixelY;
            lVx = velocityX;            
            lVy = velocityY;
            lAx = accelX;
            lAy = -accelY;
        }


        if (lWidth <= 0 || lHeight <= 0) {
            // invalid width and height, nothing to do until the GUI comes up
            return;
        }


        long curTime = System.currentTimeMillis();
        if (lastTimeMs < 0) {
            lastTimeMs = curTime;
            return;
        }

        long elapsedMs = curTime - lastTimeMs;
        lastTimeMs = curTime;

        // update the velocity
        // (divide by 1000 to convert ms to seconds)
        // end result is meters / second
        lVx += ((elapsedMs * lAx) / 1000) * pixelsPerMeter;
        lVy += ((elapsedMs * lAy) / 1000) * pixelsPerMeter;

        // update the position
        // (velocity is meters/sec, so divide by 1000 again)
        lBallX += ((lVx * elapsedMs) / 1000) * pixelsPerMeter;
        lBallY += ((lVy * elapsedMs) / 1000) * pixelsPerMeter;

        boolean bouncedX = false;
        boolean bouncedY = false;

        if (lBallY - ballRadius < 0) {
            lBallY = ballRadius;
            lVy = -lVy * rebound;
            bouncedY = true;
        } else if (lBallY + ballRadius > lHeight) {
            lBallY = lHeight - ballRadius;
            lVy = -lVy * rebound;
            bouncedY = true;
        }
        if (bouncedY && Math.abs(lVy) < STOP_BOUNCING_VELOCITY) {
            lVy = 0;  
            bouncedY = false;
        }

        if (lBallX - ballRadius < 0) {
            lBallX = ballRadius;
            lVx = -lVx * rebound;
            bouncedX = true;
        } else if (lBallX + ballRadius > lWidth) {
            lBallX = lWidth - ballRadius;
            lVx = -lVx * rebound;
            bouncedX = true;
        }
        if (bouncedX && Math.abs(lVx) < STOP_BOUNCING_VELOCITY) {
            lVx = 0;
            bouncedX = false;
        }


        // safely copy local vars back to object fields
        synchronized (LOCK) {
            ballPixelX = lBallX;
            ballPixelY = lBallY;

            velocityX = lVx;
            velocityY = lVy;
        }

        if (bouncedX || bouncedY) {
            Vibrator v = vibratorRef.get();
            if (v != null) {
                v.vibrate(20L);
            }
        }
    }

    public void setVibrator(Vibrator v) {
        vibratorRef.set(v);
    }
}

Logcat error:

 FATAL EXCEPTION: main
 java.lang.RuntimeException: Unable to start activity ComponentInfo{com.stuffthathappens.games/com.stuffthathappens.games.BouncingBallActivity}: java.lang.NullPointerException
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2194)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2229)
    at android.app.ActivityThread.access$600(ActivityThread.java:139)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1261)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:154)
    at android.app.ActivityThread.main(ActivityThread.java:4945)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:511)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
    at dalvik.system.NativeStart.main(Native Method)
 Caused by: java.lang.NullPointerException
    at com.stuffthathappens.games.BouncingBallActivity.onCreate(BouncingBallActivity.java:52)
    at android.app.Activity.performCreate(Activity.java:4531)
    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1071)
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2150)

回答1:

You can declare you array like this:

private BouncingBallModel[] model = new BouncingBallModel[50]; 

like the following

How do I declare and initialize an array in Java?