-1

In an android app, I have an Activity that holds a Fragment.

The fragment has an "updateScore" function that updated the UI with the current score.

This function is called from the fragment and should also be invoked from an option menu that resides in the activity.

This can be achieved if I save the context as a static variable in the Fragment, but this is a bad practice. So what should I do instead?

MainActivity:

public class MainActivity extends AppCompatActivity {

    public static int totalCorrect;

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

         FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
         ExerciseFragment exerciseFragment = new ExerciseFragment();
         fragmentTransaction.add(R.id.fragment_container, exerciseFragment);
         fragmentTransaction.commit();
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        super.onOptionsItemSelected(item);

        int id = item.getItemId();
        if (id == R.id.option_reset_score) {
            totalCorrect = 0;
            ExerciseFragment.updateScore(); 
        }
    }
}

ExerciseFragment:

public class ExerciseFragment extends Fragment {

    private static View view; //bad practice

    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_exercises, container, false);
        ExerciseFragment.view = view;
        updateScore(); 
        return view;
    }

    public static void updateScore() {
        TextView totalCorrectTextView = (TextView) view.findViewById(R.id.total_correct);
        totalCorrectTextView.setText(MyApp.getAppContext().getString(R.string.correct) + ": " + String.valueOf(MainActivity.totalCorrect));

    }
}
gilad s
  • 475
  • 8
  • 16

4 Answers4

1

It's not the best practice. What I advice you to do is to:

  • Create an Interface, like "ScoreUpdater" with a method "updateScore"

    public interface ScoreUpdater {
         void updateScore();
    }
    
  • make your activity extend this interface
  • move the logics of your "updateScore" method from the fragment to this overridden method in the activity
  • on the "onAttach" method of your fragment, check if the context the fragment is attaching to is instance of this ScoreUpdater interface, and save a link to it

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        if (context instanceof ScoreUpdater)
            scoreUpdater = (ScoreUpdater)context;
        ...
    }
    
  • whenever you need to update the score from your fragment, just call scoreUpdater.updateScore() from your fragment, updateScore() from your activity.

This way of programming is much more correct and follows some Design Pattern too.

balsick
  • 1,099
  • 1
  • 10
  • 23
0

Change your ExerciseFragment like this :

public class ExerciseFragment extends Fragment {

    private View mView;

    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        mView = inflater.inflate(R.layout.fragment_exercises, container, false);
        updateScore(); 
        return mView;
    }

    public void updateScore() {
        TextView totalCorrectTextView = (TextView) mView.findViewById(R.id.total_correct);
        totalCorrectTextView.setText(getActivity().getString(R.string.correct) + ": " + String.valueOf(MainActivity.totalCorrect));

    }
}

In your activity can find your current fragment by the ID of it's container :

ExerciseFragment exerciseFragment  = (ExerciseFragment) getSupportFragmentManager().findFragmentById(R.id.fragment_container);

Then you can use his methods:

exerciseFragment.updateScore();
Lubomir Babev
  • 1,892
  • 1
  • 12
  • 14
0

I think View or method doesn't need to be static

public class ExerciseFragment extends Fragment {

    private View view;

    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_exercises, container, false);
        ExerciseFragment.view = view;
        updateScore(); 
        return view;
    }

    public void updateScore() {
        TextView totalCorrectTextView = (TextView) view.findViewById(R.id.total_correct);
        totalCorrectTextView.setText(MyApp.getAppContext().getString(R.string.correct) + ": " + String.valueOf(MainActivity.totalCorrect));

    }
}

You must be initiating the Fragment somewhere.

public class MainActivity extends AppCompatActivity {

    public static int totalCorrect;

    public ExerciseFragment exerciseFragment;

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

        exerciseFragment= new ExerciseFragment();

        FragmentManager fragmentManager = getFragmentManager();
        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
        if (getFragmentManager().findFragmentById(R.id.fragment_container) != null)
            fragmentManager.popBackStack();
        fragmentTransaction.replace(R.id.fragment_container, currentFragment);
        fragmentTransaction.addToBackStack(null);
        fragmentTransaction.commit();

    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        super.onOptionsItemSelected(item);

        int id = item.getItemId();
        if (id == R.id.option_reset_score) {
            totalCorrect = 0;
            exerciseFragment.updateScore();
            return true;
        }

        return false;
    }
}
Sylvester Das
  • 164
  • 11
  • Thank you. static exerciseFragment variable is also not a good practice but it doesn't have to be static. – gilad s May 23 '17 at 08:08
0

invoked from an option menu that resides in the activity

There's your problem. Your fragment can provide its own option menu items. You'll need to override onCreateOptionsMenu and onOptionsItemSelected in the fragment, and call setHasOptionsMenu(true) in one of your fragment's setup lifecycle methods. See this answer.

Kevin Krumwiede
  • 9,868
  • 4
  • 34
  • 82