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.
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.