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