Search code examples
androidscreen-rotation

How to store lots of data upon Screen Rotation?


Yes, i have read many other posts, but i am unable to follow along because i have lots of variables, Not just One or Two.

I am making an app(TIC TAC TOE) for first time with landscape support. I am losing data on screen rotation.

package com.netlify.krupesh.tictactoe;

import android.graphics.Typeface;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    // 0 for zero and 1 for X
    // 2 for empty
    boolean gameActive = true;
    int[] gameState = {2,2,2,2,2,2,2,2,2};
    int[][] winningPositions = {{0,1,2}, {3,4,5}, {6,7,8}, {0,3,6}, {1,4,7}, {2,5,8}, {0,4,8}, {2,4,6}};

    int activePlayer = 0;
    int[] playerScores = {0,0};

    // Show Symbol on Tap
    public void showSymbol(View view){
        // Set Player Scores
        TextView player1 = (TextView) findViewById(R.id.p1Score);
        TextView player2 = (TextView) findViewById(R.id.p2Score);


        player1.setText(playerScores[0]+"");
        player2.setText(playerScores[1]+"");

        // Get Symbol Info
        ImageView symbol = (ImageView) view;
        int tappedSymbol = Integer.parseInt(symbol.getTag().toString());

        // Update Game state Array
        if(gameState[tappedSymbol]==2 && gameActive) {
            gameState[tappedSymbol] = activePlayer;

            // Show Symbol with Animation
            symbol.setAlpha(0f);
            if (activePlayer == 0) {
                symbol.setImageResource(R.drawable.zero);
                activePlayer = 1;
                showCurrentPlayer(2);

            } else {
                symbol.setImageResource(R.drawable.cross);
                activePlayer = 0;
                showCurrentPlayer(1);
            }
            symbol.animate().alpha(1).setDuration(400);

            checkDraw(gameState);

            for (int[] winningPosition : winningPositions) {
                if (gameState[winningPosition[0]] == gameState[winningPosition[1]] && gameState[winningPosition[1]] == gameState[winningPosition[2]] && gameState[winningPosition[0]] != 2) {
                    showCurrentPlayer(0);
                    // Pause The Game
                    gameActive = false;

                    // Won the Game
                    String winningPlayer ;
                    if (gameState[winningPosition[0]] == 0) winningPlayer = "Player 1";
                    else winningPlayer = "Player 2";
                    Toast.makeText(this, winningPlayer + " won!", Toast.LENGTH_SHORT).show();

                    // Update Scores
                    playerScores[gameState[winningPosition[0]]]++;
                    player1.setText(playerScores[0] + "");
                    player2.setText(playerScores[1] + "");
                }
            }
        }
    }


    public void resetBoard(View view){
        android.support.v7.widget.GridLayout board = (android.support.v7.widget.GridLayout)findViewById(R.id.gridLayout);
        for(int i=0; i<board.getChildCount(); i++) {
            ImageView symbol = (ImageView) board.getChildAt(i);
            symbol.setImageDrawable(null);
        }
        for(int i=0; i<gameState.length; i++ ){
            gameState[i] = 2;
        }
        gameActive = true;
        activePlayer = 0;
        showCurrentPlayer(1);
    }

    public void checkDraw(int[] gamestate){
        for(int i =0; i<gamestate.length; i++){
            if(gamestate[i]==2){
                return;
            }
        }
        showCurrentPlayer(0);
        Toast.makeText(this, "Match Draw!", Toast.LENGTH_SHORT).show();
    }

    public void resetAll(View view){
        resetBoard(view);
        playerScores[0]=0; playerScores[1]=0;
        TextView player1 = (TextView) findViewById(R.id.p1Score);
        TextView player2 = (TextView) findViewById(R.id.p2Score);
        player1.setText(playerScores[0] + "");
        player2.setText(playerScores[1] + "");
        showCurrentPlayer(1);
    }

    public void showCurrentPlayer(int i){
        TextView player1Heading = (TextView) findViewById(R.id.subheading1);
        TextView player2Heading = (TextView) findViewById(R.id.subheading2);
        if(i==1){
            player1Heading.setTextColor(getResources().getColor(R.color.colorPlayer1));
            player1Heading.setTypeface(null, Typeface.BOLD);
            player2Heading.setTextColor(getResources().getColor(R.color.colorHeading));
            player2Heading.setTypeface(null, Typeface.NORMAL);
        }
        if(i==2){
            player2Heading.setTextColor(getResources().getColor(R.color.colorPlayer2));
            player2Heading.setTypeface(null, Typeface.BOLD);
            player1Heading.setTextColor(getResources().getColor(R.color.colorHeading));
            player1Heading.setTypeface(null, Typeface.NORMAL);
        }
        if(i==0){
            player1Heading.setTextColor(getResources().getColor(R.color.colorHeading));
            player1Heading.setTypeface(null, Typeface.NORMAL);
            player2Heading.setTextColor(getResources().getColor(R.color.colorHeading));
            player2Heading.setTypeface(null, Typeface.NORMAL);
        }
    }



    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // ===================== Hide Status Bar ========================== //
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);
        // ================================================================ //

        setContentView(R.layout.activity_main);
        showCurrentPlayer(1);
    }
}

See, there are so many things.. For all ImageViews i need to save who have their images set. Now how do i solve this problem of rotation??

Solution with Code Snippet to be added is what will prove helpful the most, New to Android Development


Solution

  • @krupesh Anadkat I get the frustration as a newbie but @CommonsWare is a seasoned developer who has been in the game for days.

    Follow his advice and make sure you learn the fundamentals he outlined rather than blazing through or just rushing to build something for the sake of it.

    However today is your lucky day so I'll spoil you with some code snippets (we millennial programmers like it easy-yes I said it!!!) read on youngling and learn.

    The issue you are facing here is a device configuration change.

    In your case a screen orientation change.

    Every darn time a user rotates that screen the Android OS recreates your activity a new. The Android OS means no harm its simply trying to be efficient in that its checking if there are better resources for that new orientation and if so it can use those instead.

    This is the source of your pain. Now lets crack on and help you out mate.

    You can use the Activity's class methods to work your way out of this one. Before the almighty Android OS kills your Activity the method onSaveInstanceState() will be called in your Activity's lifecycle. In your class you override onSaveInstanceState() and save the data you want to the Bundle which onSaveInstanceState() takes as an argument.

    Then in your Activity's onCreate() you check if savedInstanceState is not null if its not null you retrieve you data.

    Beware though; its best to save primitive data types to the Bundle or objects that are serializable to avoid retrieving data that is stale i.e. out of date or not valid anymore.

    See code snippet for my SaveDataAcrossScreenOrientation Activity below

    package com.demo.android.savedataacrossscreenrotationdemo;
    
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.Button;
    import android.widget.TextView;
    
    public class SaveDataAcrossScreenOrientation extends AppCompatActivity {
    
        // Key to be used for the key: value pair to be saved to the bundle
        private static final String KEY_GREETING_TEXT = "greeting_text";
    
        // The text currently displayed to the screen
        private String mCurrentDisplayedText;
    
        private TextView mGreetingTextView;
        private Button mSpanishButton;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            // Get references to the button and textview
            mGreetingTextView = (TextView) findViewById(R.id.greeting_text_view);
            mSpanishButton = (Button) findViewById(R.id.change_greeting_button);
    
            // If mCurrentDisplayedText is inside the bundle retrieve and display it on screen
            if(savedInstanceState != null) {
                mCurrentDisplayedText = savedInstanceState.getString(KEY_GREETING_TEXT, "");
                if (mCurrentDisplayedText != "") {
                    mGreetingTextView.setText(mCurrentDisplayedText);
                }
            }
    
            // Set a listener on the spanish button
            mSpanishButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    // Change the english text to spanish when the button is clicked
                    mGreetingTextView.setText(R.string.spanish_greeting);
    
                    // Get the text currently shown in the text view
                    mCurrentDisplayedText = (String) mGreetingTextView.getText(); // Calling getText() returns a CharSequence cast it to a string
                }
            });
        }
    
        // Override onSaveInstanceState(Bundle savedInstanceState) and save mCurrentDisplayedText to the bundle
        @Override
        public void onSaveInstanceState(Bundle savedInstanceState) {
            super.onSaveInstanceState(savedInstanceState);
            savedInstanceState.putString(KEY_GREETING_TEXT, mCurrentDisplayedText);
        }
    
    }
    

    See video demo here

    Have fun!