-1

I have a "game" with two classes, one that holds and manages the layouts and views and another one that should perform all of the calculations needed. Did not get very far because when I click a TextView instead of displaying a value, the app crashes providing the following error:

Attempt to invoke virtual method 'void android.widget.TextView.setText(java.lang.CharSequence)' on a null object reference

Normally, when I click on a field it should go to a method in the second activity, increment an index, then display the value in the clicked TextView.

This is my main activity:

public class MainGame extends AppCompatActivity {

ConstraintLayout layout;
ConstraintSet mainLayout = new ConstraintSet();
ConstraintSet optionalLayout = new ConstraintSet();
ConstraintSet armbandLayout = new ConstraintSet();
ConstraintSet armbandOptionalLayout = new ConstraintSet();

private boolean isArmband = false;
private boolean isOptional = false;

TextView player1Name;
TextView fullNameP2;
TextView shortNameP1;
TextView shortNameP2;

TextView gameScoreP1;
TextView gameScoreP2;

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


    initializeLayoutViews();
    initializeLayoutValues();

    final ScoreSetter scoreSetter = new ScoreSetter(this);

    scoreSetter.initIndex();

    //scoreSetter.initAfterGameWon();

    gameScoreP1.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            scoreSetter.clickedByP1();
        }
    });

    }

private void initializeLayoutViews() {

    layout = findViewById(R.id.gameLayout);
    mainLayout.clone(layout);
    optionalLayout.clone(this, R.layout.activity_main_game_optional);
    armbandLayout.clone(this, R.layout.activity_main_armband);
    armbandOptionalLayout.clone(this, R.layout.activity_main_armband_optional);


    player1Name = findViewById(R.id.fullNameP1);
    fullNameP2 = findViewById(R.id.fullNameP2);
    shortNameP1 = findViewById(R.id.shortNameP1);
    shortNameP2 = findViewById(R.id.shortNameP2);
    gameScoreP1 = findViewById(R.id.gameScoreP1);
    gameScoreP2 = findViewById(R.id.gameScoreP2);
}

private void initializeLayoutValues() {
    player1Name.setText("Player 1");
    fullNameP2.setText("Player 2");
    shortNameP1.setText("P1");
    shortNameP2.setText("P2");

    gameScoreP1.setText("0");
    gameScoreP2.setText("0");

}

void writeGameScoreP1(int valP1) {
    Log.i("MainGame_value", String.valueOf(valP1));
    gameScoreP1.setText(String.valueOf(valP1));
}
}

Weird thing here is that the log displays the correct value, it just does not get displayed on the screen; instead it crashes the app.

And this is my second class:

class ScoreSetter {

private int[] GAME_SCORE_VALUES = new int[] {0, 15, 30, 40};

private int indexP1 = 0;
private int indexP2 = 0;

private int gameScoreP1;
private int gameScoreP2;

public String gameValueP1;
private String gameValueP2;

private MainGame mainGame = new MainGame();

ScoreSetter(Context context){
}

public void initIndex() {
    indexP2 = 0;
    indexP1 = 0;
}

public void initAfterGameWon() {
}

public void clickedByP1() {
    ++indexP1;
    mainGame.writeGameScoreP1(indexP1);

}
}

And this is the log:

2019-01-18 23:56:22.989 6684-6684/com.andygix.a40love I/MainGame_value: 1 2019-01-18 23:56:22.991 6684-6684/com.andygix.a40love E/AndroidRuntime: FATAL EXCEPTION: main Process: com.andygix.a40love, PID: 6684 java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setText(java.lang.CharSequence)' on a null object reference at com.andygix.a40love.MainGame.writeGameScoreP1(MainGame.java:116) at com.andygix.a40love.ScoreSetter.clickedByP1(ScoreSetter.java:33) at com.andygix.a40love.MainGame$1.onClick(MainGame.java:59) at android.view.View.performClick(View.java:6294) at android.view.View$PerformClick.run(View.java:24770) at android.os.Handler.handleCallback(Handler.java:790) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:164) at android.app.ActivityThread.main(ActivityThread.java:6494) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807) 2019-01-18 23:56:23.002 1693-1953/system_process W/ActivityManager:
Force finishing activity com.andygix.a40love/.MainGame 2019-01-18 23:56:23.528 1693-1712/system_process W/ActivityManager: Activity pause timeout for ActivityRecord{ea4853d u0 com.andygix.a40love/.MainGame t48 f} 2019-01-18 23:56:34.370 1693-1712/system_process W/ActivityManager: Activity destroy timeout for ActivityRecord{ea4853d u0 com.andygix.a40love/.MainGame t48 f}

Which mentions that the issues are caused by the following methods:

gameScoreP1.setText(String.valueOf(valP1));

mainGame.writeGameScoreP1(indexP1);

scoreSetter.clickedByP1();

I have searched around a lot and tried a bunch of different methods and nothing seems to work. All the ids in the xml files are correct and triple checked. Again I want to mention the thing that seems weird to me: the fact that I log the value before it is supposed to be written, it is correct yet not taken in by the TextView.

Andy
  • 87
  • 11
  • You can't instantiate Activities. You can't reference Activities directly unless the class referencing an Activity was instantiated by that Activity, and that Activity was somehow passed to the contained class. – TheWanderer Jan 18 '19 at 22:06
  • I am pretty new to this. I don't quite know how to instantiate a Class by the activity. – Andy Jan 18 '19 at 22:25
  • I just posted an answer. – TheWanderer Jan 18 '19 at 22:37

2 Answers2

1

You can't directly instantiate Activities or directly reference Activity methods unless your referencing class was instantiated by the target Activity.

In your case, you can't do this:

private MainGame mainGame = new MainGame();

However, you are instantiating ScoreSetter from your MainGame Activity, which means you have the reference you need. Instead of using Context in the constructor of ScoreSetter, use MainGame:

private MainGame mainGame; //don't instantiate it here

public ScoreSetter(MainGame mainGame) {
        this.mainGame = mainGame; //do it here instead
}

Now you can reference the actual instance of your Activity.

If you ever run into a case where your class isn't contained by the Activity that needs to be updated, you should look into broadcasts.

TheWanderer
  • 16,775
  • 6
  • 49
  • 63
  • Had no idea they were called broadcasts. I will keep an eye on them. And your method worked. Lots of things I should avoid saying in comments. :) – Andy Jan 18 '19 at 22:42
-1

Check for null

if (gameScoreP1 != null) {
    gameScoreP1.setText(String.valueOf(valP1));
} else {
    Logger.d(TAG, "gameScoreP1 is null");
}
Diego Torres Milano
  • 65,697
  • 9
  • 111
  • 134
  • It is null, yet the value increases. How can I solve in displaying the value instead of giving an error? This is what I get on clicks: `I/MainGame_value: 5 2019-01-19 00:17:01.749 7934-7934/com.andygix.a40love I/MainGame:: gameScoreP1 is null! 2019-01-19 00:17:02.485 7934-7934/com.andygix.a40love I/MainGame_value: 6 2019-01-19 00:17:02.485 7934-7934/com.andygix.a40love I/MainGame:: gameScoreP1 is null!` – Andy Jan 18 '19 at 22:17
  • This isn't right. OP is misunderstanding how Activities work. A null check won't fix anything. – TheWanderer Jan 18 '19 at 22:25