Zoheb Satta Zoheb Satta - 11 days ago 7
Android Question

Android Studio InflateException

I'm trying to program a simple GUI Game of Life in Android Studio, but whenever I try to run it, the app closes unexpectedly. The Android Monitor keeps throwing out an InflateException. I've checked a lot of posts on this website about InflateException, but none of them have helped my situation yet.

API - 25

This is the error:

11-28 16:41:30.292 18444-18444/com.cs3340.gameoflife E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.cs3340.gameoflife, PID: 18444
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.cs3340.gameoflife/com.cs3340.gameoflife.MainActivity}: android.view.InflateException: Binary XML file line #7: Binary XML file line #7: Error inflating class com.cs3340.gameoflife.GameView
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2665)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2726)
at android.app.ActivityThread.-wrap12(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1477)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6119)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
Caused by: android.view.InflateException: Binary XML file line #7: Binary XML file line #7: Error inflating class com.cs3340.gameoflife.GameView
Caused by: android.view.InflateException: Binary XML file line #7: Error inflating class com.cs3340.gameoflife.GameView
Caused by: java.lang.NoSuchMethodException: <init> [class android.content.Context, interface android.util.AttributeSet]
at java.lang.Class.getConstructor0(Class.java:2204)
at java.lang.Class.getConstructor(Class.java:1683)
at android.view.LayoutInflater.createView(LayoutInflater.java:618)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:787)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:727)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:858)
at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:821)
at android.view.LayoutInflater.inflate(LayoutInflater.java:518)
at android.view.LayoutInflater.inflate(LayoutInflater.java:426)
at android.view.LayoutInflater.inflate(LayoutInflater.java:377)
at com.android.internal.policy.PhoneWindow.setContentView(PhoneWindow.java:412)
at android.app.Activity.setContentView(Activity.java:2414)
at com.cs3340.gameoflife.MainActivity.onCreate(MainActivity.java:47)
at android.app.Activity.performCreate(Activity.java:6679)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1118)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2618)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2726)
at android.app.ActivityThread.-wrap12(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1477)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6119)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)


And this is the xml file it is referring to:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >

<com.cs3340.gameoflife.GameView
android:id="@+id/gameView"
android:layout_width="match_parent"
android:layout_height="match_parent" />

</LinearLayout>"


And this is the View itself:

package com.cs3340.gameoflife;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

/**
* GameView is used to draw a single generation of the Game of Life
*/
public class GameView extends View {

/**
* Variables used to calculate the size of the cells of the grid
*/
private static final int CELL_SIZE = 40;
private static final int HEIGHT = GameGlobal.getViewportHeight() / CELL_SIZE;
private static final int WIDTH = GameGlobal.getViewportWidth() / CELL_SIZE;

/**
* Color of the grid
*/
private Paint background = new Paint();

/**
* Color of the cells
*/
private Paint cellColor = new Paint();

/**
* This represents the Cell Grid
*/
private int [][] grid;

/**
* Flag to check if the game is running and prevent any changes to the grid
*/
private static boolean isRunning = false;

/**
* Constructor
* @param context
*/
public GameView(Context context){
super(context);
init();
}

/**
* Constructor helper method, initializes the varibales
*/
private void init(){
cellColor.setColor(Color.WHITE);
background.setColor(Color.BLACK);
initGrid();
}

/**
* Helper method creates the grid and initializes all cells to 0, ie "Dead"
*/
private void initGrid(){
grid = new int[HEIGHT][WIDTH];
for(int i = 0; i < HEIGHT; i++){
for(int j = 0; j < WIDTH; j++){
grid[i][j] = 0;
}
}
}

/**
* Draws the grid to the screen and sets the color
* @param canvas
*/
public void gameViewDraw(Canvas canvas){
canvas.drawRect((float) 0, (float) 0, (float) getWidth(),
(float) getHeight(), background);
}

/**
* This class handles the touch screen event of pressing on a cell
* @param event
*/
@Override
public boolean dispatchTouchEvent(MotionEvent event){
int x = (int) event.getX() / CELL_SIZE;
int y = (int) event.getY() / CELL_SIZE;

//if the cell is dead, make it alive and vice-versa
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
if(grid[y][x] == 0){
grid[y][x] = 1;
}
else{
grid[y][x] = 0;
}
break;
}
//redraws the view thread
invalidate();
return true;
}

/**
* Mutator method for isRunning
* @param running Boolean to set if the game is running or not
*/
public void setIsRunning(boolean running){
isRunning = running;
}

/**
* Accessor method to return the cell grid
* @return Game of Life cell grid
*/
public int[][] getGrid(){
return grid;
}

/**
* Runs the game as an infinite loop
*/
public void gameLoop(){

new Thread(new Runnable(){

/**
* Takes a 3x3 array around the current cell we are going to test for the next gen value
* @param squared 3x3 array around current cell
* @return nextGeneration state of the cell
*/
public int nextGeneration(int[][] squared){
//value of the middle cell, ie, the cell we are testing for next gen
int currentGen = squared[1][1];

/**
* calculates how many live cells are in the 3x3 array
*/
int alive = 0;

if(currentGen == 1){
alive--; //removes 1 from the possible number of alive cells because we need only the surrounding cells
}
for(int i = 0; i < 3; i++){
for(int j = 0; j < 3; j++){
if(squared[i][j] == 1){
alive++;
}
}

}
/**
* Applies the default Conways Game of Life rules:
* Alive Cell= > less than 2 or more than 3 live neighbours = dead
* > 2 or 3 live neighbours = alive
*
* Dead Cell= > Exactly 3 live neighbours = alive
* > Else = dead
*/
if(currentGen == 1){
if(alive < 2 || alive > 3){
return 0;
} else{
return 1;
}
} else if(currentGen == 0){
if(alive == 3){
return 1;
} else{
return 0;
}
}
//safety, never actually used
return 0;
}


@Override
public void run(){
//initializing the array to be used in nextGeneration()
int[][] squared = new int [3][3];

/**
* While the game is running
*/
while(isRunning){
//temporary array to hold the new generation
int[][] nextGen = new int[HEIGHT][WIDTH];

//This inserts every possible 3x3 grid from the original cell grid to be tested for the next generation values
//This loop also takes into account wrapping
for(int i = 0; i < HEIGHT; i++){
for(int j = 0; i < WIDTH; j++){

// row 1
if (i - 1 >= 0 && j - 1 >= 0)
squared[0][0] = grid[i - 1][j - 1];
else if (i - 1 < 0 && j - 1 >= 0)
squared[0][0] = grid[HEIGHT - 1][j - 1];
else if (i - 1 >= 0 && j - 1 < 0)
squared[0][0] = grid[j - 1][WIDTH - 1];
else if (i - 1 < 0 && j - 1 < 0)
squared[0][0] = grid[HEIGHT - 1][WIDTH - 1];


if (i - 1 >= 0)
squared[0][1] = grid[i - 1][j];
else
squared[0][1] = grid[HEIGHT - 1][j];


if (i - 1 >= 0 && j < WIDTH - 1)
squared[0][2] = grid[i - 1][j + 1];
else if (i - 1 < 0 && j < WIDTH - 1)
squared[0][2] = grid[HEIGHT - 1][j + 1];
else if (i - 1 >= 0 && j >= WIDTH - 1)
squared[0][2] = grid[i - 1][0];
else if (i - 1 < 0 && j >= WIDTH - 1)
squared[0][2] = grid[HEIGHT - 1][0];

// row 2
if (j - 1 >= 0)
squared[1][0] = grid[i][j - 1];
else
squared[1][0] = grid[i][WIDTH - 1];


squared[1][1] = grid[i][j];


if (j < WIDTH - 1)
squared[1][2] = grid[i][j + 1];
else
squared[1][2] = grid[i][0];

// row 3
if (i < HEIGHT - 1 && j - 1 >= 0)
squared[2][0] = grid[i + 1][j - 1];
else if (i >= HEIGHT - 1 && j - 1 >= 0)
squared[2][0] = grid[0][j - 1];
else if (i < HEIGHT - 1 && j - 1 < 0)
squared[2][0] = grid[i + 1][WIDTH - 1];
else if (i >= HEIGHT - 1 && j - 1 < 0)
squared[2][0] = grid[0][WIDTH - 1];


if (i < HEIGHT - 1)
squared[2][1] = grid[i + 1][j];
else
squared[2][1] = grid[0][j];


if (i < HEIGHT - 1 && j < WIDTH - 1)
squared[2][2] = grid[i + 1][j + 1];
else if (i >= HEIGHT - 1 && j < WIDTH - 1)
squared[2][2] = grid[0][j + 1];
else if (i < HEIGHT - 1 && j >= WIDTH - 1)
squared[2][2] = grid[i + 1][0];
else if (i >= HEIGHT - 1 && j >= WIDTH - 1)
squared[2][2] = grid[0][0];


nextGen[i][j] = nextGeneration(squared);

}
}
}

}

}).start();


}

}


Thanks in advance for your help

Answer

Your GameView needs a two-argument constructor, as this is the constructor used by LayoutInflater to build view hierarchies from XML.

public GameView(Context context, AttributeSet attrs) {
    super(context, attrs);
    init();
}