Android 开发如何避免使用片段和选项菜单保存上下文

Android development how to avoid saving context with a fragment and an option menu

在 android 应用程序中,我有一个包含片段的 Activity。

该片段有一个 "updateScore" 函数,可以用当前分数更新 UI。

此函数是从片段中调用的,也应该从驻留在 activity 中的选项菜单中调用。

如果我将上下文保存为片段中的静态变量,这可以实现,但这是一种不好的做法。那么我应该怎么做呢?

主要Activity:

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(); 
        }
    }
}

练习片段:

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));

    }
}

像这样更改您的 ExerciseFragment :

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));

    }
}

在您的 activity 中可以通过其容器的 ID 找到您当前的片段:

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

那你可以用他的方法:

exerciseFragment.updateScore();

我认为视图或方法不需要是静态的

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));

    }
}

您一定是在某处启动片段。

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;
    }
}

这不是最佳做法。我建议你做的是:

  • 创建一个接口,例如 "ScoreUpdater" 使用方法 "updateScore"

    public interface ScoreUpdater {
         void updateScore();
    }
    
  • 让你的activity扩展这个接口
  • 将 "updateScore" 方法的逻辑从片段移动到 activity
  • 中的这个重写方法
  • 在片段的 "onAttach" 方法上,检查片段附加到的上下文是否是此 ScoreUpdater 接口的实例,并向其保存 link

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        if (context instanceof ScoreUpdater)
            scoreUpdater = (ScoreUpdater)context;
        ...
    }
    
  • 每当您需要从片段更新分数时,只需从片段调用 scoreUpdater.updateScore(),从 activity.
  • 调用 updateScore()

这种编程方式更正确,也遵循一些Design Pattern

invoked from an option menu that resides in the activity

这是你的问题。您的片段可以提供自己的选项菜单项。您需要覆盖片段中的 onCreateOptionsMenuonOptionsItemSelected,并在片段的设置生命周期方法之一中调用 setHasOptionsMenu(true)。参见 this answer