-1

I'm taking a course on Android development and one of the exercise is to make a simple Connect 3 game. I'm not sure where the error is as it's not a compilation error, so I can't figure out where to change the code.

Given below is the MainActivity.java code :

package com.anurag.connectthree;
import android.media.Image;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.GridLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

// 0 = yellow, 1 = red  => means yellow goes first
int activePlayer = 0;

//save a memory state to know when a tile has been clicked
int[] gameState = {2,2,2,2,2,2,2,2,2};

//check if game is won after min 5 moves
static int noOfMoves = 0;

//layout over the grid layout for playing again
private LinearLayout playAgainLayout = (LinearLayout)findViewById(R.id.playAgainLayout);

//the winner message on the linear  layout
private TextView winnerMessage = (TextView)findViewById(R.id.winnerMessage);

//initially true, set to false after game has been won
private boolean gameIsActive = true;

public void dropIn(View view){


    //tapped on view, so imageview is the view itself, then set resource to image as done on line 31 and 35
    ImageView counter =(ImageView) view;

    //Added tags to tiles in XML .getTag() returns tag and can act as index of gameState array
    int tappedCounter = Integer.parseInt(counter.getTag().toString());

    //2 means tile hasn't been been clicked
    if(gameState[tappedCounter] == 2 && gameIsActive) {

        gameState[tappedCounter]=activePlayer; //this will tell us which player is playing this move

        counter.setTranslationY(-1000f);

        if (activePlayer == 0) {
            counter.setImageResource(R.drawable.yellow);
            activePlayer = 1;
        } else {
            counter.setImageResource(R.drawable.red);
            activePlayer = 0;
        }
        counter.animate().translationYBy(1000f).rotation(540f).setDuration(300);

    }

    noOfMoves++;

    Log.i("Move number ",Integer.toString(noOfMoves));

    if(noOfMoves>=5) checkForWin();

}

private void checkForWin() {
    int i,j;

    //winning combinations are {{0,1,2},{3,4,5},{6,7,8}} horizontally, {{0,3,6},{1,4,7},{2,5,8}} vertically and {{0,4,8}, {2,4,6}} diagonally
    for(i=1,j=3;i<=8&&j<=8;i+=3,j+=1){
        if((gameState[i-1]==gameState[i]&&gameState[i+1]==gameState[i])    //takes care of horizontal
                ||(gameState[j-3]==gameState[j]&&gameState[j]==gameState[j+3])   //takes care of vertical
            ||(gameState[0]==gameState[4]&&gameState[4]==gameState[8])       //hardcoding diagonal
                ||(gameState[2]==gameState[4]&&gameState[4]==gameState[6])){



            if(gameState[i]==0||gameState[j]==0||gameState[4]==0) {
                gameIsActive = false;
                winnerMessage.setText("Yellow has won!");
                if(playAgainLayout.getVisibility()==View.INVISIBLE)
                    playAgainLayout.setVisibility(View.VISIBLE);

            }
            else if(gameState[i]==1||gameState[j]==1||gameState[4]==1) {
                gameIsActive = false;
                winnerMessage.setText("Yellow has won!");
                if(playAgainLayout.getVisibility()==View.INVISIBLE)
                    playAgainLayout.setVisibility(View.VISIBLE);

            }
        }
    }

}
public void playAgain(View view){

    //for playing again set the game to active again
    gameIsActive=true;

    //this is again set to invisible
    playAgainLayout.setVisibility(View.INVISIBLE);

    //game state reset to {2,2,2,2,2,2,2,2,2}
    for(int i =0; i<gameState.length;i++)
        gameState[i]=2;

    //again yellow will start, so set to 0
    activePlayer = 0;

    GridLayout gridLayout = (GridLayout)findViewById(R.id.gridLayout);

    //for each of the 9 children of the grid, set their image src to null
    for(int i=0;i<gridLayout.getChildCount();i++){

        ((ImageView) gridLayout.getChildAt(i)).setImageResource(0);
    }

}


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Log.i("Check if game starts :","Yes");

    }
}

Also given the log output:

04-26 20:55:03.639 20649-20649/? E/Zygote: v2
04-26 20:55:03.639 20649-20649/? I/libpersona: KNOX_SDCARD checking this for 
10231
KNOX_SDCARD not a persona
04-26 20:55:03.641 20649-20649/? E/Zygote: accessInfo : 0
04-26 20:55:03.642 20649-20649/? W/SELinux: SELinux 
selinux_android_compute_policy_index : Policy Index[2],  Con:u:r:zygote:s0 
RAM:SEPF_SECMOBILE_7.0_0010, [-1 -1 -1 -1 0 1]
04-26 20:55:03.643 20649-20649/? I/SELinux: SELinux: seapp_context_lookup: 
seinfo=untrusted, level=s0:c512,c768, pkgname=com.anurag.connectthree 
04-26 20:55:03.648 20649-20649/? I/art: Late-enabling -Xcheck:jni
04-26 20:55:03.967 20649-20649/com.anurag.connectthree I/InstantRun: 
starting instant run server: is main process
04-26 20:55:04.067 20649-20649/com.anurag.connectthree E/AndroidRuntime: 
FATAL EXCEPTION: main
Process: com.anurag.connectthree, PID: 20649
java.lang.RuntimeException: Unable to instantiate activity 
ComponentInfo{com.anurag.connectthree/com.anurag.connectthree.MainActivity}: 
java.lang.NullPointerException: Attempt to invoke virtual method 
'android.view.Window$Callback android.view.Window.getCallback()' on a null 
object reference
at 
android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2849)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3045)
at android.app.ActivityThread.-wrap14(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1642)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6776)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1496)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1386)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 
'android.view.Window$Callback android.view.Window.getCallback()' on a null 
object reference
at android.support.v7.app.AppCompatDelegateImplBase.<init> 
(AppCompatDelegateImplBase.java:117)
at android.support.v7.app.AppCompatDelegateImplV9.<init> 
(AppCompatDelegateImplV9.java:149)
at android.support.v7.app.AppCompatDelegateImplV14.<init> 
(AppCompatDelegateImplV14.java:56)
at android.support.v7.app.AppCompatDelegateImplV23.<init> 
(AppCompatDelegateImplV23.java:31)
at android.support.v7.app.AppCompatDelegateImplN.<init> 
(AppCompatDelegateImplN.java:31)
at 
android.support.v7.app.AppCompatDelegate.create(AppCompatDelegate.java:198)
at 
android.support.v7.app.AppCompatDelegate.create(AppCompatDelegate.java:183)
at android.support.v7.app.AppCompatActivity.getDelegate(AppCompatActivity.java:519)
    at android.support.v7.app.AppCompatActivity.findViewById(AppCompatActivity.java:190)
    at com.anurag.connectthree.MainActivity.<init>(MainActivity.java:26)
    at java.lang.Class.newInstance(Native Method)
    at android.app.Instrumentation.newActivity(Instrumentation.java:1086)
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2839)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3045) 
    at android.app.ActivityThread.-wrap14(ActivityThread.java) 
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1642) 
    at android.os.Handler.dispatchMessage(Handler.java:102) 
    at android.os.Looper.loop(Looper.java:154) 
    at android.app.ActivityThread.main(ActivityThread.java:6776) 
    at java.lang.reflect.Method.invoke(Native Method) 
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1496) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1386) 
04-26 20:55:04.093 20649-20649/? I/Process: Sending signal. PID: 20649 SIG: 9

Also attaching the activity_main XML file:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<android.support.v7.widget.GridLayout
    android:id="@+id/gridLayout"
    android:layout_width="369dp"
    android:layout_height="424dp"
    android:layout_marginBottom="8dp"
    android:layout_marginEnd="8dp"
    android:layout_marginLeft="8dp"
    android:layout_marginRight="8dp"
    android:layout_marginStart="8dp"
    android:layout_marginTop="8dp"
    android:background="@drawable/board"
    app:columnCount="3"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintHorizontal_bias="0.0"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintVertical_bias="0.492"
    app:rowCount="3">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="119dp"
        android:layout_height="134dp"

        android:onClick="dropIn"
        android:tag="0" />

    <ImageView
        android:id="@+id/imageView3"
        android:layout_width="119dp"
        android:layout_height="134dp"
        android:layout_marginLeft="10dp"
        android:onClick="dropIn"
        android:tag="1"
        app:layout_column="1"
        app:layout_row="0" />

    <ImageView
        android:id="@+id/imageView4"
        android:layout_width="119dp"
        android:layout_height="134dp"
        android:layout_marginLeft="5dp"
        android:onClick="dropIn"
        android:tag="2" />

    <ImageView
        android:id="@+id/imageView10"
        android:layout_width="119dp"
        android:layout_height="134dp"
        android:layout_marginTop="5dp"
        android:onClick="dropIn"
        android:tag="3" />

    <ImageView
        android:id="@+id/imageView9"
        android:layout_width="119dp"
        android:layout_height="134dp"
        android:layout_marginLeft="10dp"
        android:layout_marginTop="5dp"
        android:onClick="dropIn"
        android:tag="4" />

    <ImageView
        android:id="@+id/imageView8"
        android:layout_width="119dp"
        android:layout_height="134dp"
        android:layout_marginLeft="5dp"
        android:layout_marginTop="5dp"
        android:onClick="dropIn"
        android:tag="5" />

    <ImageView
        android:id="@+id/imageView7"
        android:layout_width="119dp"
        android:layout_height="134dp"
        android:layout_marginTop="13dp"
        android:onClick="dropIn"
        android:tag="6" />

    <ImageView
        android:id="@+id/imageView6"
        android:layout_width="119dp"
        android:layout_height="134dp"
        android:layout_marginLeft="9dp"
        android:layout_marginTop="13dp"
        android:onClick="dropIn"
        android:tag="7" />

    <ImageView
        android:id="@+id/imageView5"
        android:layout_width="119dp"
        android:layout_height="134dp"
        android:layout_marginLeft="5dp"
        android:layout_marginTop="13dp"
        android:onClick="dropIn"
        android:tag="8" />

</android.support.v7.widget.GridLayout>

<LinearLayout
    android:id="@+id/playAgainLayout"
    android:layout_width="309dp"
    android:layout_height="179dp"
    android:layout_marginBottom="8dp"
    android:layout_marginEnd="8dp"
    android:layout_marginLeft="8dp"
    android:layout_marginRight="8dp"
    android:layout_marginStart="8dp"
    android:layout_marginTop="8dp"
    android:background="@android:color/holo_blue_dark"
    android:orientation="vertical"
    android:padding="10dp"
    android:visibility="invisible"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent">

    <TextView
        android:id="@+id/winnerMessage"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@android:color/transparent"
        android:padding="10dp"
        android:text="The winner is "
        android:textColor="@android:color/black"
        android:textSize="30sp" />

    <Button
        android:id="@+id/playAgainButton"
        android:layout_width="141dp"
        android:layout_height="76dp"
        android:layout_gravity="center_vertical|center"
        android:onClick="playAgain"
        android:text="Play Again"
        android:textSize="17sp" />
</LinearLayout>

And the Android Mainfest XML as well:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.anurag.connectthree">

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>

Phantômaxx
  • 37,901
  • 21
  • 84
  • 115
Anurag
  • 3
  • 1
  • 5

2 Answers2

0

View should be find inside method onCreate(), after setContentView()

   private LinearLayout playAgainLayou
   private TextView winnerMessage

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        winnerMessage = (TextView)findViewById(R.id.winnerMessage);
        playAgainLayout =(LinearLayout)findViewById(R.id.playAgainLayout);
    }
artemiygreg
  • 101
  • 1
  • 4
0

You have two views (playAgainLayout and winnerMessage) being initialised as part of field declaration using findViewById API call.

When this class' constructor is called, first these fields are initialised. This can be verified from your stack trace:

at android.support.v7.app.AppCompatActivity.findViewById(AppCompatActivity.java:190)
at com.anurag.connectthree.MainActivity.<init>(MainActivity.java:26)

Here <init> refers to the constructor call.

The findViewById can only work when the activity is attached to the window.

The first callback method in the activity lifecycle is the onCreate method. Usually we call setContentView method from here to inflate our view. Since you want the views inflated from your layout file activity_main.xml, you should find them after calling setContentView(R.layout.activity_main);.

//layout over the grid layout for playing again
private LinearLayout playAgainLayout;

//the winner message on the linear  layout
private TextView winnerMessage;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // layout is inflated, now we can find the views using findViewById
    playAgainLayout = (LinearLayout)findViewById(R.id.playAgainLayout);
    winnerMessage = (TextView)findViewById(R.id.winnerMessage);

    Log.i("Check if game starts :","Yes");
    }
}

Hope this helps.

Ammar Al Hashmi
  • 101
  • 1
  • 4