0

I am working on a Quiz app. First when a user opens the app they go to the MainActivity, from there when they press start they go to the Categories Activity , from there after selecting a category they go to the Sets Activity, from there after selecting a set the go to the Questions Activity and finally after completing all the questions they reach the Score Activity. Here in the score activity when the click on Done button they are redirected to the MainActivity. In the Score Activity i want to change the color of the Set that they completed to green instead of the default color. How can i do this? I created a sets item layout xml file and used an adapter to fill the gridview in the Sets Activity with views from the adapter. Currently i am getting a null object reference after clicking the Done button in the ScoreActivity.

Here is the code :

SetsAdapter.java

public class SetsAdapter extends BaseAdapter {

    private int numOfSets;

    public SetsAdapter(int numOfSets) {
        this.numOfSets = numOfSets;
    }

    @Override
    public int getCount() {
        return numOfSets;
    }

    @Override
    public Object getItem(int position) {
        return null;
    }

    @Override
    public long getItemId(int position) {
        return 0;
    }

    @Override
    public View getView(final int position, View convertView, final ViewGroup parent) {

        View view;
        if(convertView == null){
            view = LayoutInflater.from(parent.getContext()).inflate(R.layout.set_item_layout, parent, false);
        }
        else {
            view = convertView;
        }

        view.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent questionIntent = new Intent(parent.getContext(), QuestionActivity.class);
                questionIntent.putExtra("SETNUM", position +1);
                parent.getContext().startActivity(questionIntent);
            }
        });

        ((TextView) view.findViewById(R.id.setNumber)).setText(String.valueOf(position+1));

        return view;
    }
}

SetsActivity.java

public class SetsActivity extends AppCompatActivity {

    private GridView sets_grid;
    private FirebaseFirestore firestore;
    public static int categoryID;
    private Dialog loadingDialog;

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

        Toolbar toolbar = (Toolbar)findViewById(R.id.set_toolbar);
        setSupportActionBar(toolbar);
        String title = getIntent().getStringExtra("CATEGORY");
        categoryID = getIntent().getIntExtra("CATEGORY_ID",1);
        getSupportActionBar().setTitle(title);
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);

        sets_grid = findViewById(R.id.sets_gridView);

        loadingDialog = new Dialog(SetsActivity.this);
        loadingDialog.setContentView(R.layout.loading_progressbar);
        loadingDialog.setCancelable(false);
        loadingDialog.getWindow().setBackgroundDrawableResource(R.drawable.progress_background);
        loadingDialog.getWindow().setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        loadingDialog.show();

        firestore = FirebaseFirestore.getInstance();
        loadSets();



    }

    private void loadSets() {
        firestore.collection("Quiz").document("CAT" + String.valueOf(categoryID))
                .get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
            @Override
            public void onComplete(@NonNull Task<DocumentSnapshot> task) {
                if (task.isSuccessful()) {
                    DocumentSnapshot doc = task.getResult();

                    if (doc.exists()) {
                        long sets = (long) doc.get("SETS");
                        SetsAdapter adapter = new SetsAdapter(Integer.valueOf((int)sets));


                        sets_grid.setAdapter(adapter);


                    } else {
                        Toast.makeText(SetsActivity.this, "No Sets Exists!", Toast.LENGTH_SHORT).show();
                        finish();

                    }
                } else {
                    Toast.makeText(SetsActivity.this, task.getException().getMessage(), Toast.LENGTH_SHORT).show();

                }
                loadingDialog.cancel();
            }
        });
    }

    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
        if(item.getItemId() == android.R.id.home)
            finish();
        return super.onOptionsItemSelected(item);
    }
}

ScoreActivity.java

public class ScoreActivity extends AppCompatActivity {

    private TextView score;
    private Button done;

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

        score = findViewById(R.id.score_tv);
        done = findViewById(R.id.score_activity_done);

        String score_str = getIntent().getStringExtra("SCORE");
        final int setNum = getIntent().getIntExtra("SetNum", 1);
        score.setText(score_str);

        done.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                // Here is the issue I am facing
                View view = findViewById(R.id.setNumber);
                view.setBackgroundColor(Color.GREEN);
                Intent mainIntent = new Intent(ScoreActivity.this, MainActivity.class);
                startActivity(mainIntent);
                ScoreActivity.this.finish();
            }
        });


    }
}

Arnab
  • 77
  • 1
  • 10
  • What you can do is as the sequence is `Sets` -> `Score`, you can declare the view as a public static global variable and use it in `Score` as `Sets.variable_name` or preferred way is to create a public function in `Sets` activity in which you change the color of that view, now call this function as `Sets.function_name()` in `Score` activity. But, remember this will be temporary, if you want to change the color permanently, you should use `SharedPreferences` and save the value of completed sets, & next time when app opens, change the color of any set which has its value as completed. – Lalit Fauzdar May 19 '20 at 09:15
  • Thank You. I am new to programming , could you help me with how to implement sharedPreference. I have learned about it but never used it myself. – Arnab May 19 '20 at 09:44
  • I've answered your question. Hope it helps. – Lalit Fauzdar May 19 '20 at 10:27

3 Answers3

1

As your activity Sequence is MainActivity -> Categories -> Sets -> Scores.

You've two options to change the color with two different life cycle of the change.

  1. To change the color on a temporary basis, this will reset itself after closing the app or resrtating the 'Sets' activity. It can be done in two ways: Using Public Static Variable and using a public function.

  2. To change the color on a permanent basis until the app is uninstalled/reinstalled. You should use SharedPreferences. SharedPreferences acts like a private data stored in device's memory for further use and it stays there unchanged until and unless the app is removed/data is cleared. Although, apps with root permission can access any app's SharedPreferences data and can modify it as well.
    You can use SharedPreferences as explained here. Or, you can use some library to access it an easy way. The way I use it in all my apps is TinyDB(it's just a java/kotlin file). This works as:

    //store the value from ScoreActivity after completion as
    
    TinyDB tinyDB = TinyDB(this);
    tinyDB.putBoolean("isSet1Completed",true);
    
    //access the boolean variable in SetsActivity to change the color of any set that 
    //is completed and if it's true, just change the color. 
    
    TinyDB tinyDB = TinyDB(this);
    Boolean bool1 = tinyDB.getBoolean("isSet1Completed");
    

But, it's your choice what way you want to prefer. Now, this was about the lifecycle of the change you'll do: Temp or Permanent. Now, we'll talk about how you change the color.

  • Using public static variable in Sets activity. What you can do is you can set the imageView/textview whose background you want to change as public static variable. Remember, this idea is not preferred as it causes memory leak but it's just easy.

    Declare it as public static ImageView imageview;(or TextView) intialize it in the onCreated() as imageView = finViewById(R.id.viewId); in Sets activity.
    Call it as new SetsActivity().imageView.setBackgroundColor(yourColor); in ScoreActivity.

  • Second way is to create a public function in SetsAcitvity, putting the color change code in it, and then calling it from the ScoreActivity. Just declare it as public void changeColor(){ //your work} and call it from ScoreActivity as new SetsActivity().changeCOlor(). You can also pass some arguments to the function like setId.

I've provided you every thing you need. Rest you should figure out yourself to actually learn it and not copy it.

Lalit Fauzdar
  • 5,953
  • 2
  • 26
  • 50
  • Thank You so much, your answer has cleared all my doubts. And explained everything to me very clearly. I have just one question , the view whose color i want to change is a `LinearLayout`. In my xml file i have a `LinearLayout` which sets the background color of the set and a textview which sets the set number. So while changing the color in the last step instead of imageView what should i call. Thank You one again for being so helpful. – Arnab May 19 '20 at 10:39
  • You have your answer already -> the view whose color i want to change is a `LinearLayout`. To change the color of `LinearLayout`, use it as a `ViewGroup`. So, intead of `ImageView`, use `ViewGroup` and pass the id of the `LinearLayout`. @arnab – Lalit Fauzdar May 19 '20 at 10:42
  • Sorry , you asked me to call the method to change the color in the ScoreActivity. Where should i check if the boolean value is true or not. – Arnab May 19 '20 at 11:27
  • You didn't understand it properly. The boolean thing is required so that the color stays changed even after restarting the app. So, in this ScoreActivity, you need to set/store the boolean first, then you call the function of setactivity and then if user goes back, the color seems changed because you called the function. You can also use onResume() (even a better way) of SetsActivity and check the stored boolean here and if it's true, change the color. onResume() is called when an activity gets focus after minimized or after user returned to it from next activity. – Lalit Fauzdar May 19 '20 at 11:32
  • So, first store the value, second let the user return, third check if value is true in `OnResume()` of `SetsActivity` and if true, change the color. This way, you don't even have to create/call a function or a public static variable or anything else. This will work in both ways, when user comes back or restarts the app. – Lalit Fauzdar May 19 '20 at 11:34
  • I did that , when i run the app after clicking on Done button in Score Activity , i am getting a Null object reference on the line `tinyDB.putBoolean("isSet1Completed",true);` in score activity and `preferences.edit().putBoolean(key, value).apply();` in TinyDB.java ! Thank You – Arnab May 19 '20 at 12:02
  • Have you initialized it as `TinyDB tinyDB = TinyDB(this);`? – Lalit Fauzdar May 19 '20 at 12:04
  • I had initialized it inside the onClick method of Score Activity. I moved it to onCreate and it solved the error there . But now after clicking the done button in the score activity when i am getting back to the main activity , and again selecting the category , the app is crashing with a null object reference. Now i am getting the error in the setsActivity in the onResume method.. I have initialised the linearLayout in the SetsActivity's onCreate. I am getting the null object reference in this line. please check the next comment – Arnab May 19 '20 at 12:10
  • `protected void onResume() { super.onResume(); if(bool1){ setBackground.setBackgroundColor(Color.GREEN); } }` – Arnab May 19 '20 at 12:11
  • I did all the steps.. Please tell me if i am doing something wrong in the last step! – Arnab May 19 '20 at 13:07
  • The app was crashing after clicking on the Done button in Score Activity , after moving the `TinyDB` initialisation and `putBoolean` code from `onClick` to `onCreate` , it stopped crashing and i was redirected to MainActivity . But now as soon as i click on any category to go to the Sets Activity , the app crashes with a null object reference in the `onResume` method of the Sets Activity. I know i am doing something wrong as i am never checking which set is completed by its ID before changing the color, maybe because of that i am getting the error. – Arnab May 20 '20 at 05:00
  • The code of the onResume method in the Sets Activity is `protected void onResume() { super.onResume(); if(bool1){ setBackground.setBackgroundColor(Color.GREEN); } }` and i am initialising the LinearLayout whose color i want to change as `public static ViewGroup setBackground; ` and then inside onCreate of the Sets Activity `setBackground = findViewById(R.id.set_background);` – Arnab May 20 '20 at 05:03
  • First thing, as you're using the viewGroup variable only in this class so you don't have to declare it using public static, it is used to access one class's elements in other class. Second, how are you initalising the boolean value? Create and Initialise it in `onResume()` as `boolean bool1 = tinyDB.getBoolean("Your_boolean");`. Also check that this your_boolean is same as in both activity means the keys match. Now, I know why you're getting the error possibly. Because `getBoolean("your_boolean")` is never initialized and you're not null-cheking it. – Lalit Fauzdar May 20 '20 at 06:18
  • So, do one thing which you have to do with `SharedPreferences` as well. Either first null check the boolean or check for first run of the app using [this](https://stackoverflow.com/a/7217834/8244632) and if first run, initialize whatever key for boolean you're going to use in the project as false. Then, you won't get the null pointer exception – Lalit Fauzdar May 20 '20 at 06:21
  • Or easy way, use `putString()` in case of boolean and do in this `onResume()` as `String idOfCompletedSet = tinyDB.getString("ID"); if(!idOfCompletedSet.isNullOrBlank()){ //match the id with completed set and turn them green}`. – Lalit Fauzdar May 20 '20 at 06:24
  • I am using Intent to pass the ID of the set from Set Adapter to Question Activity and then again from Question Activity to Score Activity. I am putting this set ID in Score Activity using `tinyDB.putString("Set ID", String.valueOf(setNum));` as you suggested . Then in the onResume method of the Set Activity i am getting the value `String idOfCompletedSet = tinyDB.getString("Set ID"); if(!idOfCompletedSet.isEmpty()){ \\ }` . I am facing two problems, first i am unable to find the method `isNullorBlank()` and how should i get the id of the set to change color . Thank You – Arnab May 20 '20 at 06:53
  • The code of the setAdapter class is posted above. How shall i get a set with a given ID? I am sorry , i am very new to programming maybe i am asking very silly questions. But Thank you so much for your help – Arnab May 20 '20 at 06:55
  • Currently I am trying this code `protected void onResume() { int numOfSets = 1; super.onResume(); TinyDB tinyDB = new TinyDB(this); String idOfCompletedSet = tinyDB.getString("Set ID"); if(!idOfCompletedSet.isEmpty()){ SetsAdapter adapter = new SetsAdapter(numOfSets); setBackground = (ViewGroup) adapter.getItem((Integer) Integer.valueOf(idOfCompletedSet)); setBackground.setBackgroundColor(Color.GREEN); } }` still getting this error `ViewGroup.setBackgroundColor(int)' on a null object reference` – Arnab May 20 '20 at 07:34
  • and if i add a null check before changing the color like `if(setBackground != null) { setBackground.setBackgroundColor(Color.GREEN); }` the app doesn't crash with a null object reference , but the color of the set is not changed at the end of the Score Activity. – Arnab May 20 '20 at 07:45
  • Ah, `NullPointerException` is a cumbersome issue in Java. If you're not using this `ViewGroup` anywhere else, why don't you initialize it here same as you do in `OnCreate()` because `OnCreate()` is called before `OnResume()` so that won't give you NPE. – Lalit Fauzdar May 20 '20 at 08:21
  • But the color of the set is still not changing . Please check the code in the `onResume` i posted in the previous comment. What am i doing wrong? – Arnab May 20 '20 at 08:46
0

I think simply you add flag in MainActivity.

for example, add flag in MainActivity.

boolean isFromDone = false;

and when done clicked,

 done.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                // Here is the issue I am facing
                Intent mainIntent = new Intent(ScoreActivity.this, MainActivity.class);
                mainIntent.putExtra("FromDone", true);
                startActivity(mainIntent);
                ScoreActivity.this.finish();
            }
        });

and in MainActivity, add this.

@Override
protected void onResume() {
    super.onResume();
    isFromDone = getIntent().getBooleanExtra("FromDone", false);
    if(isFromDone) {
       (TextView) view.findViewById(R.id.setNumber)).setBackgroundColor(Color.GREEN);
    }

}
S T
  • 1,068
  • 2
  • 8
  • 16
  • Nope, as he has mentioned, the Sets are never in the `MainActivity`, they're in `SetsActivity`, so no, he can't change the Set's color in `OnResume()` of `MainActivity`. – Lalit Fauzdar May 19 '20 at 09:18
  • When i run the app i get a null object reference after clicking Done is Score Activity in onResume of the mainActivity in the findViewById line.. android.view.View.findViewById(int)' on a null object reference . Thank You – Arnab May 19 '20 at 09:32
0

Suppose you have a Linear Layout in Activity A and you want to change it's background color from a button click which is present in Activity B.

Step 1 Create a class and declare a static variable.

class Util { private static LinearLayout mylayout ; }

Step 2 In the activity which is holding this layout, initialize it.

Util.mylayout = findviewbyid(R.id.linear);

Step 3Change the background color on button click from Activity B

onClick{
Util.mylayout.setBackgroundColor(Color.RED);
}
Hritik Gupta
  • 611
  • 5
  • 20
  • I want to change the color of only one view , the set that the user has completed ,not all the sets. Thank you – Arnab May 19 '20 at 09:38