0

I am doing a multiscreen quiz app. For each question I have a separate activity / screen. At the bottom of each screen there are next/previous "buttons" which navigate to the next/previous screen. Please see the UI example of a screen with a question:

enter image description here

I have a problem though. Let's assume a user selects answers to a question 2 and then clicks "Previous", selects an answer in question 1 and hits "Next".

I would like to save the UI state of the Question 2, so the selected answer stays if a user comes back to a question either by clicking previous or next.

One thing I managed to accomplish is when a user clicks "previous" the UI stays, I used the following code in the manifest file:

android:launchMode="singleTask"

However I cannot make it saved when a user comes bak to a question via "next". Here is my code for the activity with the question 2:

package com.example.justynagolawska.quizappiteration2;

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.CheckBox;
import android.widget.TextView;

public class Question2Activity extends AppCompatActivity {

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

        ActionBar actionbar = getSupportActionBar();

        // Applies the custom action bar style
        getSupportActionBar().setDisplayOptions(actionbar.DISPLAY_SHOW_CUSTOM);
        getSupportActionBar().setCustomView(R.layout.action_bar);

        // Changes the action bar title
        TextView title = (TextView) getSupportActionBar().getCustomView().findViewById(R.id.action_bar_title);
        title.setText(R.string.q2_name);

        //Getting the intent with score for question 1
        Intent question2Intent = getIntent();
        final int resultQ1 = question2Intent.getIntExtra("q1result", 0);

        // Find the View that shows the next TextView
        TextView nextQuestion = (TextView) findViewById(R.id.next);

        // Set a click listener on that View
        nextQuestion.setOnClickListener(new View.OnClickListener() {
            // The code in this method will be executed when next View is clicked on.
            @Override
            public void onClick(View view) {
                //Getting the answer to question 2 checkbox 1
                CheckBox checkBox1Q2 = (CheckBox) findViewById(R.id.checkbox1Q2);
                boolean isCheckBox1Q2 = checkBox1Q2.isChecked();

                //Getting the answer to question 2 checkbox 2
                CheckBox checkBox2Q2 = (CheckBox) findViewById(R.id.checkbox2Q2);
                boolean isCheckBox2Q2 = checkBox2Q2.isChecked();

                //Getting the answer to question 2 checkbox 3
                CheckBox checkBox3Q2 = (CheckBox) findViewById(R.id.checkbox3Q2);
                boolean isCheckBox3Q2 = checkBox3Q2.isChecked();

                //Calculate Question 2 score
                int resultQ2 = calculateResultQ2(isCheckBox1Q2, isCheckBox2Q2, isCheckBox3Q2);

                Intent question3Intent = new Intent(Question2Activity.this, Question3Activity.class);
                question3Intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);

                question3Intent.putExtra ("q1result", resultQ1);
                question3Intent.putExtra ("q2result", resultQ2);
                startActivity(question3Intent);
            }
        });

        // Find the View that shows the next TextView
        TextView previousQuestion = (TextView) findViewById(R.id.previous);

        // Set a click listener on that View
        previousQuestion.setOnClickListener(new View.OnClickListener() {
            // The code in this method will be executed when next View is clicked on.
            @Override
            public void onClick(View view) {
                Intent question1Intent = new Intent(Question2Activity.this, Question1Activity.class);
                startActivity(question1Intent);
            }
        });
    }

    /**
     * Check which checkbox was selected in the question 2
     *
     * @param checkBox1 is whether or not the user checked the checkbox1
     * @param checkBox2 is whether or not the user checked the checkbox2
     * @param checkBox3 is whether or not the user checked the checkbox3
     * @return the score the user got for question 2
     */
    private int calculateResultQ2(boolean checkBox1, boolean checkBox2, boolean checkBox3) {
        int result = 0;
        if (checkBox1 && checkBox2 && checkBox3) {
            result = 1;
        }
        return result;
    }

I would appreciate very much if anyone could help me out. Thank you!

EDIT: Below is my working code using sharedPreferences, the solution proposed by @tahsinRupam

package com.example.justynagolawska.quizappiteration2;

import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.CheckBox;
import android.widget.TextView;

public class Question2Activity extends AppCompatActivity {

SharedPreferences mypref;

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

    final CheckBox checkBox1Q2 = (CheckBox) findViewById(R.id.checkbox1Q2);
    final CheckBox checkBox2Q2 = (CheckBox) findViewById(R.id.checkbox2Q2);
    final CheckBox checkBox3Q2 = (CheckBox) findViewById(R.id.checkbox3Q2);

    mypref = PreferenceManager.getDefaultSharedPreferences(this);

    ActionBar actionbar = getSupportActionBar();

    // Applies the custom action bar style
    getSupportActionBar().setDisplayOptions(actionbar.DISPLAY_SHOW_CUSTOM);
    getSupportActionBar().setCustomView(R.layout.action_bar);

    // Changes the action bar title
    TextView title = (TextView) getSupportActionBar().getCustomView().findViewById(R.id.action_bar_title);
    title.setText(R.string.q2_name);

    //Getting the intent with score for question 1
    Intent question2Intent = getIntent();


    final int resultQ1 = question2Intent.getIntExtra("q1result", 0);

    // Find the View that shows the next TextView
    TextView nextQuestion = (TextView) findViewById(R.id.next);

    // Set a click listener on that View
    nextQuestion.setOnClickListener(new View.OnClickListener() {
        // The code in this method will be executed when next View is clicked on.
        @Override
        public void onClick(View view) {


            //Getting the answer to question 2 checkbox 1
            boolean isCheckBox1Q2 = checkBox1Q2.isChecked();

            //Getting the answer to question 2 checkbox 2
            boolean isCheckBox2Q2 = checkBox2Q2.isChecked();

            //Getting the answer to question 2 checkbox 3
            boolean isCheckBox3Q2 = checkBox3Q2.isChecked();

            //Calculate Question 2 score
            int resultQ2 = calculateResultQ2(isCheckBox1Q2, isCheckBox2Q2, isCheckBox3Q2);

            Intent question3Intent = new Intent(Question2Activity.this, Question3Activity.class);
            question3Intent.putExtra ("q1result", resultQ1);
            question3Intent.putExtra ("q2result", resultQ2);
            startActivity(question3Intent);
        }
    });

    // Find the View that shows the next TextView
    TextView previousQuestion = (TextView) findViewById(R.id.previous);

    // Set a click listener on that View
    previousQuestion.setOnClickListener(new View.OnClickListener() {
        // The code in this method will be executed when next View is clicked on.
        @Override
        public void onClick(View view) {
            Intent question1Intent = new Intent(Question2Activity.this, Question1Activity.class);
            startActivity(question1Intent);
        }
    });
}

/**
 * Check which checkbox was selected in the question 2
 *
 * @param checkBox1 is whether or not the user checked the checkbox1
 * @param checkBox2 is whether or not the user checked the checkbox2
 * @param checkBox3 is whether or not the user checked the checkbox3
 * @return the score the user got for question 2
 */
private int calculateResultQ2(boolean checkBox1, boolean checkBox2, boolean checkBox3) {
    int result = 0;
    if (checkBox1 && checkBox2 && checkBox3) {
        result = 1;
    }
    return result;
}

@Override
protected void onPause() {
    super.onPause();

    //Getting the answer to question 2 checkbox 1
    CheckBox checkBox1Q2 = (CheckBox) findViewById(R.id.checkbox1Q2);
    boolean isCheckBox1Q2 = checkBox1Q2.isChecked();

    //Getting the answer to question 2 checkbox 2
    CheckBox checkBox2Q2 = (CheckBox) findViewById(R.id.checkbox2Q2);
    boolean isCheckBox2Q2 = checkBox2Q2.isChecked();

    //Getting the answer to question 2 checkbox 3
    CheckBox checkBox3Q2 = (CheckBox) findViewById(R.id.checkbox3Q2);
    boolean isCheckBox3Q2 = checkBox3Q2.isChecked();

    if(isCheckBox1Q2 == true){
        mypref.edit().putBoolean("Iscb1Checked", true).apply();
    }
    else if(isCheckBox1Q2 == false){
        mypref.edit().putBoolean("Iscb1Checked", false).apply();
    }

    if(isCheckBox2Q2 == true){
        mypref.edit().putBoolean("Iscb2Checked", true).apply();
    }
    else if(isCheckBox2Q2 == false){
        mypref.edit().putBoolean("Iscb2Checked", false).apply();
    }

    if(isCheckBox3Q2 == true){
        mypref.edit().putBoolean("Iscb3Checked", true).apply();
    }
    else if(isCheckBox3Q2 == false){
        mypref.edit().putBoolean("Iscb3Checked", false).apply();
    }

}

@Override
protected void onResume() {
    super.onResume();

    //Getting the answer to question 2 checkbox 1
    CheckBox checkBox1Q2 = (CheckBox) findViewById(R.id.checkbox1Q2);

    //Getting the answer to question 2 checkbox 2
    CheckBox checkBox2Q2 = (CheckBox) findViewById(R.id.checkbox2Q2);

    //Getting the answer to question 2 checkbox 3
    CheckBox checkBox3Q2 = (CheckBox) findViewById(R.id.checkbox3Q2);

    if(mypref.contains("Iscb1Checked")){
        if(mypref.getBoolean("Iscb1Checked",false)){
            checkBox1Q2.setChecked(true);
        }
    }

    if(mypref.contains("Iscb2Checked")){
        if(mypref.getBoolean("Iscb2Checked",false)){
            checkBox2Q2.setChecked(true);
        }
    }

    if(mypref.contains("Iscb3Checked")){
        if(mypref.getBoolean("Iscb3Checked",false)){
            checkBox3Q2.setChecked(true);
        }
    }
}

}

Please note that I replaced the below in the onPause(); method as I was getting null exception:

checkBox1Q2.isChecked()
!checkBox1Q2.isChecked()

with

isCheckBox1Q2 == true
isCheckBox1Q2 == false
  • why do you use `android:launchMode="singleTask"`? – Mehran Zamani Mar 05 '17 at 07:18
  • To save the UI state when a user clicks "previous". It does half of the job I need to do, but of course is not what I want to achieve. I do not have to use it at all if I find a valid solution for the above problem. – Justyna Goławska Mar 05 '17 at 07:21
  • I think if you use viewpager, that will solve your problem, it will save user UI changes until destroy – Hamid Reza Mar 05 '17 at 07:47
  • You can use saveInstanceState or SharedPreference for your requirement. – tahsinRupam Mar 05 '17 at 08:02
  • Hi @tahsinRupam. Thank you for your suggestion. Can you tell more details how should I apply saveInstanceState? I tried to do this but I guess it is only saving UI state when I rotate the screen? – Justyna Goławska Mar 05 '17 at 08:05
  • That's actually the same. While rotation of screen and coming back to an activity the onCreate() method is called. If you save the state it will be reloaded by onCreate(). I'll add the whole process how to apply onSaveInstanceState in your project in a bit. Stay tuned ;) – tahsinRupam Mar 05 '17 at 08:15
  • Thank you so much for your help. Will try to add it into my code and see if it works. This will take a while as I am new to Android, but will certainly get back to you:) – Justyna Goławska Mar 05 '17 at 08:48

1 Answers1

0

You can use SharedPreference to store your value.

1) Declare checkbox and SharedPreference publicly:

 CheckBox checkBox1Q2;
 CheckBox checkBox2Q2;
 CheckBox checkBox3Q2;
 SharedPreferences mypref;

2) In your onCreate() initialize SharedPreference:

 mypref = PreferenceManager.getDefaultSharedPreferences(this);

3) Save Checkbox states in onPause() (cause onPause() is called when the back button is pressed). You could declare in onStop() or onDestroy() too.

  @Override
protected void onPause() {
    super.onPause();
    if(checkBox1Q2.isChecked()){
        mypref.edit().putBoolean("Iscb1Checked", true).apply();
    }
    else if(!checkBox1Q2.isChecked()){
        mypref.edit().putBoolean("Iscb1Checked", false).apply();
    }

    if(checkBox2Q2.isChecked()){
        mypref.edit().putBoolean("Iscb2Checked", true).apply();
    }
    else if(!checkBox2Q2.isChecked()){
        mypref.edit().putBoolean("Iscb2Checked", false).apply();
    }

    if(checkBox3Q2.isChecked()){
        mypref.edit().putBoolean("Iscb3Checked", true).apply();
    }
    else if(!checkBox3Q2.isChecked()){
        mypref.edit().putBoolean("Iscb3Checked", false).apply();
    }


}

4) Get CheckBox states in onResume() method:

@Override
protected void onResume() {
    super.onResume();
        if(mypref.contains("Iscb1Checked")){
            if(mypref.getBoolean("Iscb1Checked",false)){
                checkBox1Q2.setChecked(true);
            }
        }

        if(mypref.contains("Iscb2Checked")){
            if(mypref.getBoolean("Iscb2Checked",false)){
                checkBox2Q2.setChecked(true);
            }
        }

        if(mypref.contains("Iscb3Checked")){
             if(mypref.getBoolean("Iscb3Checked",false)){
                checkBox3Q2.setChecked(true);
             }
         }
}

You're now getting the Checkbox view in onClick. Try to avoid that. Always get views (findViewById) in the initial part of onCreate (After setContentView).

tahsinRupam
  • 6,325
  • 1
  • 18
  • 34
  • Hi @tahsinRupam, for some reason it is not working for me. I applied exactly the same code before onCreate(); method. Can you tell me what can be wrong? I also saw this post: http://stackoverflow.com/questions/151777/saving-android-activity-state-using-save-instance-state/151822#151822 that claims savedInstanceState works only for when you are in the same activity e.g. for screen rotation? Maybe then sharedPrefrences would be a better idea, what do you think? – Justyna Goławska Mar 05 '17 at 09:18
  • Yes you are absolutely correct! I got confused by reading various opinions. Bundle data is useful when System kills the activity for resource purpose or screen Rotation. But when an activity is killed due to app behavior (like back button press) the Activity instance is gone permanently. So saveInstanceState won't work here. It will be wise to use SharedPreference in this matter. – tahsinRupam Mar 05 '17 at 09:58
  • I'll edit my answer to solve the issue using SharedPreference If you like. – tahsinRupam Mar 05 '17 at 10:01
  • That would be great! Thank you! – Justyna Goławska Mar 05 '17 at 10:04
  • I'm testing your case. Unfortunately, I'm facing some trouble with my Android Studio. That's why it is taking some time. Sorry, If I am keeping you waiting. It will be solved pretty soon. If you havn't solved yet you can wait a little a bit – tahsinRupam Mar 05 '17 at 14:38
  • Thanks a lot for spending time on this. I will check it out as soon as I get to the computer. It might be tomorrow though. Will get back to you. – Justyna Goławska Mar 05 '17 at 17:00
  • My pleasure. Sure, No hurry. – tahsinRupam Mar 05 '17 at 17:08
  • Hi, it seems not working. I have put my updated code in the question as an EDIT so you can check what can be wrong there – Justyna Goławska Mar 05 '17 at 20:21
  • In your latest code, you are getting Boolean values in onCreate(). Remember, onCreate is initialized at the creation of the activity. At the start of the activity all the checkbox are unchecked so you will always get the boolean values false. You have to check the checkbox conditions when you are leaving the activity (Such as in onClick, cause here you are starting another activity or onPause, onStop, onDestroy ). You can declare boolean variables globally (You did but didn't used it ), and to get the latest condition of checkboxes assign isChecked() values while leaving the activity. – tahsinRupam Mar 06 '17 at 05:54
  • it does not work even if I get them inside onClick()? – Justyna Goławska Mar 06 '17 at 06:54
  • And you are using final for boolean values. Final values can be assigned only once. Btw, is it truly necessary to use boolean values to determine checkbox state? If not try to avoid that, it is causing some complexity. You can try out my solution, I've testted it and it is working perfectly. – tahsinRupam Mar 06 '17 at 06:59
  • I'm not using final boolean anymore. I even moved this bit: CheckBox checkBox1Q2 = (CheckBox) findViewById(R.id.checkbox1Q2); into onClick(); so I do not have to apply final. Not sure about not using booleans as with your code the app crashed – Justyna Goławska Mar 06 '17 at 07:04
  • Don't move CheckBox checkBox1Q2 = (CheckBox) findViewById(R.id.checkbox1Q2); to onclick. Keep it in onCreate. Let me see your latest modification. What error are you getting? Check your log cat – tahsinRupam Mar 06 '17 at 07:09
  • I might have found the solution, stay tuned:) Can we somehow chat in here? – Justyna Goławska Mar 06 '17 at 07:11
  • Go to http://chat.stackoverflow.com/ I've created a chat room called Let's Chat! Enter there then we can discuss the issue. – tahsinRupam Mar 06 '17 at 07:23
  • I can't chat as I need to have at least 20 reputation:( – Justyna Goławska Mar 06 '17 at 07:33
  • I made your solution work for me. In a minute I'll update the code in my question in a minute. Basically I needed to repeat: CheckBox checkBox1Q2 = (CheckBox) findViewById(R.id.checkbox1Q2); boolean isCheckBox1Q2 = checkBox1Q2.isChecked(); in onResume(); and only the first line in onPause; now time for radiobuttons and Strings;) – Justyna Goławska Mar 06 '17 at 07:40
  • 1
    Stackoverflow will kill me for this extensive commenting block;) but wanted to thank you for your effort and time dedicated to this problem! – Justyna Goławska Mar 06 '17 at 07:52
  • Haha, all my answered comment sections look like this. It's all my pleasure. Glad you solved your problem eventually. Good luck for the future and "Happy Coading" !! – tahsinRupam Mar 06 '17 at 07:58