0

This is doing my head in, and it's probably something simple that's causing it - so I think I need a new set of eyes looking at it.

All activities are defined in the manifest. I've made sure all attributes are initialised in onCreate/constructor.

The 'Circle' object is just a generic object with gets and sets for x, y, radius and colour.

Any help greatly appreciated.

10-01 16:06:28.338: E/AndroidRuntime(18306): FATAL EXCEPTION: main
10-01 16:06:28.338: E/AndroidRuntime(18306): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.dbk.parallelreaction/com.dbk.parallelreaction.GameActivity}: java.lang.NullPointerException
10-01 16:06:28.338: E/AndroidRuntime(18306):    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2308)
10-01 16:06:28.338: E/AndroidRuntime(18306):    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2358)
10-01 16:06:28.338: E/AndroidRuntime(18306):    at android.app.ActivityThread.access$600(ActivityThread.java:153)
10-01 16:06:28.338: E/AndroidRuntime(18306):    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1247)
10-01 16:06:28.338: E/AndroidRuntime(18306):    at android.os.Handler.dispatchMessage(Handler.java:99)
10-01 16:06:28.338: E/AndroidRuntime(18306):    at android.os.Looper.loop(Looper.java:137)
10-01 16:06:28.338: E/AndroidRuntime(18306):    at android.app.ActivityThread.main(ActivityThread.java:5227)
10-01 16:06:28.338: E/AndroidRuntime(18306):    at java.lang.reflect.Method.invokeNative(Native Method)
10-01 16:06:28.338: E/AndroidRuntime(18306):    at java.lang.reflect.Method.invoke(Method.java:511)
10-01 16:06:28.338: E/AndroidRuntime(18306):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:795)
10-01 16:06:28.338: E/AndroidRuntime(18306):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:562)
10-01 16:06:28.338: E/AndroidRuntime(18306):    at dalvik.system.NativeStart.main(Native Method)
10-01 16:06:28.338: E/AndroidRuntime(18306): Caused by: java.lang.NullPointerException
10-01 16:06:28.338: E/AndroidRuntime(18306):    at com.dbk.parallelreaction.util.GameView.<init>(GameView.java:51)
10-01 16:06:28.338: E/AndroidRuntime(18306):    at com.dbk.parallelreaction.GameActivity.onCreate(GameActivity.java:64)
10-01 16:06:28.338: E/AndroidRuntime(18306):    at android.app.Activity.performCreate(Activity.java:5104)
10-01 16:06:28.338: E/AndroidRuntime(18306):    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1080)
10-01 16:06:28.338: E/AndroidRuntime(18306):    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2262)

The two files in question:

GameActivity.java

package com.dbk.parallelreaction;

import android.app.Activity;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.Typeface;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.widget.FrameLayout;
import android.widget.ProgressBar;
import android.widget.TextView;

import com.dbk.parallelreaction.util.GameView;

public class GameActivity extends Activity {

    // GameView object to hold current level
    private GameView game;

    // level/score persistance
    private int currentLevel;
    private int currentScore;

    // game countdown timer
    private CountDownTimer timer;
    private int timerTime; // in milliseconds
    private int timerPeriod; // in ms also
    private int timerIndex;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_game);

        // initialise
        timerTime = 10000;
        timerPeriod = 50;
        timerIndex = (int) timerTime / timerPeriod;

        // aesthetic stuff
        TextView ins = (TextView) findViewById(R.id.GameInstructions);
        Typeface tf = Typeface.createFromAsset(getAssets(), "Economica-Regular.ttf");
        ins.setTypeface(tf);
        ins.setTextColor(Color.WHITE);
        ins.setTextSize(18);

        // get extras
        Bundle extras = getIntent().getExtras();
        currentLevel = extras.getInt("LEVEL");
        currentScore = extras.getInt("SCORE");

        // check if starting new game, or going to next level
        switch(currentLevel) {
            case 1:
                // new level
                break;
            case 2:
                // etc
                break;
        }

        // add the game view to the layout
        FrameLayout frame = (FrameLayout) findViewById(R.id.GameContainer);
        game = new GameView(this, 2);
        frame.addView(game);

        // timer bar
        final ProgressBar pb = (ProgressBar) findViewById(R.id.GameTimerBar);
        pb.setMax(timerIndex);
        pb.setProgress(timerIndex);
        timer = new CountDownTimer(timerTime, timerPeriod) {
            @Override
            public void onFinish() {
                pb.setProgress(timerIndex);

                Bundle extras = new Bundle();
                extras.putInt("SCORE", 16); // placeholder score

                Intent i = new Intent(getApplicationContext(), GameOverActivity.class);
                i.putExtras(extras);

                // GAME OVER if timer bar empties
                finish();
                startActivity(i);
            }

            @Override
            public void onTick(long msLeft) {
                timerIndex--;
                pb.setProgress(timerIndex);
            }
        }.start();

    }

    // cancel timer if user exits game
    public void onBackPressed() {
        super.onBackPressed();
        timer.cancel();
    }

}

GameView.java updated

package com.dbk.parallelreaction.util;

import java.util.ArrayList;
import java.util.Random;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;

import com.dbk.parallelreaction.R;

public class GameView extends SurfaceView {

    // Temp ArrayList containing Circle objects
    private ArrayList<Circle> circles;

    // Paint object for colour info
    private Paint paint;

    // number of circles to draw depending on level
    private int numCircles;

    // Current score in the current level
    private int levelScore;


    public GameView(Context context, int numberCircles) {
        super(context);

        LayoutInflater inflater =(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        // replace custom_layout with the name of the .xml file that contains the `FrameLayout` with id R.id.GameContainer
        View v = inflater.inflate(R.layout.activity_game, (ViewGroup) GameView.this.getParent(), true);
        FrameLayout frame = (FrameLayout) v.findViewById(R.id.GameContainer);

        // initialise
        circles = new ArrayList<Circle>();
        paint = new Paint();
        levelScore = 0;

        numCircles = numberCircles;

        // Get the game container FrameView
        int frameW = frame.getWidth();
        int frameH = frame.getHeight();
        int frameL = frame.getLeft();
        int frameT = frame.getTop();

        // fill arraylist with random circles
        Random random = new Random();
        for(int i=0; i<numCircles; i++) {
            // generate coords within the frame
            int x = random.nextInt((frameL+frameW)-frameL) + frameL;
            int y = random.nextInt((frameT+frameH)-frameT) + frameT;

            // random radius between 50 and 10
            int r = random.nextInt(50-10) + 10;

            // add them to the ArrayList
            circles.add(new Circle(x, y, r, Color.WHITE));
        }
    }

    @Override 
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        for(int i=0; i<numCircles; i++) {
            Circle c = circles.get(i);

            // set colour
            paint.setColor(c.getColor());

            // draw circle to canvas
            canvas.drawCircle(c.getX(), c.getY(), c.getRadius(), paint);
        }
    }
}
Kieran
  • 314
  • 1
  • 4
  • 18

2 Answers2

3

If your custom SurfaceView will have a layout you need to inflate it. On your constructor you have

FrameLayout frame = (FrameLayout) findViewById(R.id.GameContainer);

You are trying to reference a FrameLayout that has not been inflated yet. To inflate the corresponding .xml you need to use a LayoutInflator. You can get a reference to the LayoutInflated via the Context passed in to your SurfaceView. For example like this:

public GameView(Context context, AttributeSet attrs, int numberCircles){

super(context,attrs);
LayoutInflater inflater =(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

 //replace custom_layout with the name of the .xml file that contains the `FrameLayout` with id R.id.GameContainer
View v = inflater.inflate(R.layout.custom_layout, (ViewGroup) GameView.this.getParent(), true);
    FrameLayout frame = (FrameLayout)v.findViewById(R.id.GameContainer);
 //The rest of your code...
}

Also, If you are going to add your GameView to an .xml file you need to use the SurfaceView(Context context, AttributeSet attrs) constructor.

Emmanuel
  • 13,083
  • 4
  • 39
  • 53
  • Q: Why is this likely the correct answer? A: because line 51 on GameView: `int frameW = frame.getWidth();` – paulsm4 Oct 01 '13 at 15:41
  • Sorry, I'm pretty new to Android dev so I'm not fully understanding this. The FrameLayout `frame` is defined in the XML. I am then adding a custom SurfaceView (`GameView`) as a child to `frame`. Is it `frame` I need to inflate? How do I go about that exactly? Thanks. – Kieran Oct 01 '13 at 15:45
  • 2
    You need to inflate the .xml file that contains the `FrameLayout` – Emmanuel Oct 01 '13 at 15:48
  • Ok, thanks. I had to use `ViewGroup vg = (ViewGroup) GameView.this.getParent(); View v = inflater.inflate(R.layout.activity_game, vg, true);`. One final question: on line 66 of GameActivity (`game = new GameView(this, 2);`), what is the AttributeSet object I pass in supposed to be comprised of? – Kieran Oct 01 '13 at 16:05
  • I updated my answer. You are right. Here is a link of what `AttributeSet` is. http://developer.android.com/reference/android/util/AttributeSet.html – Emmanuel Oct 01 '13 at 16:11
  • Thanks, but what I meant was what attributes am I meant to specify? I also just realised I am also referencing the FrameLayout in GameActivity.java on line 63. Does it need to be inflated here as well? – Kieran Oct 01 '13 at 16:18
  • Like the link says, the values in `AttributeSet` are gathered from the .xml. Android does this automatically. In your case it seems that you are adding the `SurfaceView` by code, so you can just use the constructor the only requires a Context. You only need the AttributeSet constructor if you are going to add your custom `View` to an .xml like you might do with a Button for example. You already inflated it; no need to do it again. – Emmanuel Oct 01 '13 at 16:26
0

Please check your XML that whether you have supplied correct ID for Frame Layout in GameView.java

Karan_Rana
  • 2,813
  • 2
  • 26
  • 35