如何在调用 popBackStack() 后将所选项目从 ListFragment 传递到上一个 Fragment?
How to pass selected item from ListFragment to previous Fragment after calling popBackStack()?
我已经为我的问题准备了一个非常简单的测试用例,其中包含 1 个 activity 和 2 个片段:
- MainActivity.java(将所选项目存储在共享首选项中)
- MyMainFragment.java(显示一个文本视图和一个 "select" 按钮)
- MyListFragment.java(显示项目列表)
最初 MainActivity 显示 MyMainFragment。
当用户触摸 "Select item..." 按钮时,MainActivity 显示 MyListFragment:
public void selectedButtonClicked() {
SharedPreferences prefs = getSharedPreferences(PREFS, 0);
int index = prefs.getInt(INDEX, -1);
Fragment fragment = MyListFragment.newInstance(index);
getFragmentManager().beginTransaction()
.addToBackStack(null)
.replace(R.id.root, fragment, LIST)
.commit();
}
用户触摸列表中的一项后,MainActivity 将所选项目位置存储在共享首选项中,并调用 popBackStack()
再次显示 MyMainFragment:
public void itemSelected(int index) {
SharedPreferences prefs = getSharedPreferences(PREFS, 0);
Editor editor = prefs.edit();
editor.putInt(INDEX, index);
editor.commit();
getFragmentManager().popBackStack();
// How to show the index in mSelectedTextView?
}
即我的应用程序中的 MainActivity 执行所有 "heavy work":它显示 2 个片段中的 1 个并将用户数据保存在共享首选项中。
我的问题是:如何在MyMainFragment的mSelectedTextView
中显示选中的item(上面截图中的"Item 07")?
我正在寻找一种方法来正确地做到这一点,没有任何黑客攻击(例如片段不应该触及共享偏好)。
更新: 我试过 corsair992 的建议(谢谢!)
public void itemSelected(int index) {
SharedPreferences prefs = getSharedPreferences(PREFS, 0);
Editor editor = prefs.edit();
editor.putInt(INDEX, index);
editor.commit();
getFragmentManager().popBackStackImmediate();
MyMainFragment f = (MyMainFragment) getFragmentManager().findFragmentById(R.id.root);
// call a method in MyMainFragment to update mSelectedTextView
f.setText("Selected index: " + index);
}
但它仅在用户触摸 MyListFragment 中的项目时有效。但是,当用户 returns 通过触摸“后退”按钮转到 MyMainFragment 时,它不起作用。
发生这种情况时,您的第一个片段是否仍保留在内存中?我想是的。这意味着您可以使用 SharedPreferences.OnSharedPreferenceChangeListener。只需让 MyMainFragment 实现接口,然后使用 registerOnSharedPreferenceChangeListener
来监视更改。不过不要忘记注销!
一个更好的主意可能是将您的 activity 注册为侦听器,并让它在再次显示时将该数据传递给 MyMainFragment。
为MyMainFragment分配一个标签,假设它将是MY_MAIN_FRAGMENT_TAG。
将此代码添加到您的 itemSelected
方法中:
Fragment myMainFragment = getFragmentManger.findFragmentByTag("MY_MAIN_FRAGMENT_TAG");
if (null != myMainFragment) {
Bundle args = new Bundle();
myMainFragment.getArguments().putInt("selectedItem", index);
getFragmentManager().popBackStack();
}
然后在您的 MyMainFragment 中覆盖 onViewStateRestored() 方法:
@Override
public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
super.onViewStateRestored(savedInstanceState);
mSelectedTextView.setText(String.valueOf(getArguments.getInt("selectedItem")));
}
如果 null != getArguments()
并将适当的值设置为 TextView
,您可能需要一些额外的检查。
首先,您可以在UI线程内使用SharedPrefences
,即在片段内。这种常见的误解源于只有 commit()
方法的时代。现在我们 apply()
异步工作并且 get*()
使用缓存数据。
其次,解决您的问题的最简单方法是在您的第二个片段中通知 Activity 项目选择(例如,在您的片段中:((MainActivity)getActivity()).setSelectedItem(itemId);
)并在您的第一个片段的 onResume()
上获取此值(例如,((MainActivity)getActivity()).getSelectedItem();
)。当然,如果没有选择任何内容,您应该检查此值是否为 null。
据我所知,您已经有了合理的第一部分答案(corsair992 的建议 - 传递所选项目的值)第二部分是您如何处理后退键。
有几种方法可以做到这一点(听回堆栈等),但我最常使用的是:
声明一个返回键监听器接口:
public interface BackKeyListener {
boolean onBackPressed();
}
在您的主 activity 中添加以下内容:
import BackKeyListener;
void onBackKeyPressed() {
boolean backKeyHandled = false;
Fragment activeFragment = getActiveFragment();
if (activeFragment instanceof BackKeyListener) {
backKeyHandled = ((BackKeyListener) activeFragment).onBackKeyPressed();
}
if (!handled) {
// If should process back key normally, pass to super..
super.onBackKeyPressed();
}
}
Fragment getActiveFragment() {
// I'm assuming you place all your fragments here, if not..
// alter to suit
return getFragmentManager().findFragmentById(R.id.root);
}
在 MyListFragment 中添加以下内容:
import BackKeyListener;
class MyListFragment extends Fragment(orsomething) implements BackKeyListener
public boolean onBackPressed() {
// User pressed back, select default item..
((MainActivity) getActivity()).itemSelected(selectedItem);
// In this case, prevent normal back processing or
// you might get a double backstack pop.
return true;
}
在片段上使用接口允许您 "not care" 活动片段是什么。
如果它实现了 BackKeyListener,MainActivity 将在按下后退键时调用它。
这使您可以选择将值传递回 activity.
一种解决方案是使用setTargetFragment/getTargetFragment 方法。用这种方法
您可以在没有 Activity 和 SharedPreferences 帮助的情况下仅使用您的片段。
在您的 MyMainFragment 中,当用户单击按钮时
"Select item..." 您可以将回调 selectedButtonClicked() 用作:
public void selectedButtonClicked() {
Fragment fragment = MyListFragment.newInstance(index);
//Set the TargetFragment
fragment.setTargetFragment(MyMainFragment.this, MyListFragment.REQUESTCODE)
getFragmentManager().beginTransaction()
.addToBackStack(null)
.replace(R.id.root, fragment, LIST)
.commit();
}
您必须在 ActivityMainFragment 的结果上定义回调:
public void onActivityResult(int requestCode, int resultCode, Intent data){
if(requestCode == MyListFragment.REQUESTCODE){
f.setText("Selected index: " + intent.getInt("INDEX");
}
}
在 MyListFragment 中,一旦用户选择了一个项目,您就可以调用您的回调 itemSelected()
public void itemSelected(int index) {
Intent i = new Intent();
i.putExtraInt("INDEX",index);
getTargetFragment.onActivityResult(MyListFragment.REQUESTCODE,Context.Result_OK,i);
getFragmentManager().popBackStackImmediate();
}
我已经为我的问题准备了一个非常简单的测试用例,其中包含 1 个 activity 和 2 个片段:
- MainActivity.java(将所选项目存储在共享首选项中)
- MyMainFragment.java(显示一个文本视图和一个 "select" 按钮)
- MyListFragment.java(显示项目列表)
最初 MainActivity 显示 MyMainFragment。
当用户触摸 "Select item..." 按钮时,MainActivity 显示 MyListFragment:
public void selectedButtonClicked() {
SharedPreferences prefs = getSharedPreferences(PREFS, 0);
int index = prefs.getInt(INDEX, -1);
Fragment fragment = MyListFragment.newInstance(index);
getFragmentManager().beginTransaction()
.addToBackStack(null)
.replace(R.id.root, fragment, LIST)
.commit();
}
用户触摸列表中的一项后,MainActivity 将所选项目位置存储在共享首选项中,并调用 popBackStack()
再次显示 MyMainFragment:
public void itemSelected(int index) {
SharedPreferences prefs = getSharedPreferences(PREFS, 0);
Editor editor = prefs.edit();
editor.putInt(INDEX, index);
editor.commit();
getFragmentManager().popBackStack();
// How to show the index in mSelectedTextView?
}
即我的应用程序中的 MainActivity 执行所有 "heavy work":它显示 2 个片段中的 1 个并将用户数据保存在共享首选项中。
我的问题是:如何在MyMainFragment的mSelectedTextView
中显示选中的item(上面截图中的"Item 07")?
我正在寻找一种方法来正确地做到这一点,没有任何黑客攻击(例如片段不应该触及共享偏好)。
更新: 我试过 corsair992 的建议(谢谢!)
public void itemSelected(int index) {
SharedPreferences prefs = getSharedPreferences(PREFS, 0);
Editor editor = prefs.edit();
editor.putInt(INDEX, index);
editor.commit();
getFragmentManager().popBackStackImmediate();
MyMainFragment f = (MyMainFragment) getFragmentManager().findFragmentById(R.id.root);
// call a method in MyMainFragment to update mSelectedTextView
f.setText("Selected index: " + index);
}
但它仅在用户触摸 MyListFragment 中的项目时有效。但是,当用户 returns 通过触摸“后退”按钮转到 MyMainFragment 时,它不起作用。
发生这种情况时,您的第一个片段是否仍保留在内存中?我想是的。这意味着您可以使用 SharedPreferences.OnSharedPreferenceChangeListener。只需让 MyMainFragment 实现接口,然后使用 registerOnSharedPreferenceChangeListener
来监视更改。不过不要忘记注销!
一个更好的主意可能是将您的 activity 注册为侦听器,并让它在再次显示时将该数据传递给 MyMainFragment。
为MyMainFragment分配一个标签,假设它将是MY_MAIN_FRAGMENT_TAG。
将此代码添加到您的 itemSelected
方法中:
Fragment myMainFragment = getFragmentManger.findFragmentByTag("MY_MAIN_FRAGMENT_TAG");
if (null != myMainFragment) {
Bundle args = new Bundle();
myMainFragment.getArguments().putInt("selectedItem", index);
getFragmentManager().popBackStack();
}
然后在您的 MyMainFragment 中覆盖 onViewStateRestored() 方法:
@Override
public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
super.onViewStateRestored(savedInstanceState);
mSelectedTextView.setText(String.valueOf(getArguments.getInt("selectedItem")));
}
如果 null != getArguments()
并将适当的值设置为 TextView
,您可能需要一些额外的检查。
首先,您可以在UI线程内使用SharedPrefences
,即在片段内。这种常见的误解源于只有 commit()
方法的时代。现在我们 apply()
异步工作并且 get*()
使用缓存数据。
其次,解决您的问题的最简单方法是在您的第二个片段中通知 Activity 项目选择(例如,在您的片段中:((MainActivity)getActivity()).setSelectedItem(itemId);
)并在您的第一个片段的 onResume()
上获取此值(例如,((MainActivity)getActivity()).getSelectedItem();
)。当然,如果没有选择任何内容,您应该检查此值是否为 null。
据我所知,您已经有了合理的第一部分答案(corsair992 的建议 - 传递所选项目的值)第二部分是您如何处理后退键。
有几种方法可以做到这一点(听回堆栈等),但我最常使用的是:
声明一个返回键监听器接口:
public interface BackKeyListener {
boolean onBackPressed();
}
在您的主 activity 中添加以下内容:
import BackKeyListener;
void onBackKeyPressed() {
boolean backKeyHandled = false;
Fragment activeFragment = getActiveFragment();
if (activeFragment instanceof BackKeyListener) {
backKeyHandled = ((BackKeyListener) activeFragment).onBackKeyPressed();
}
if (!handled) {
// If should process back key normally, pass to super..
super.onBackKeyPressed();
}
}
Fragment getActiveFragment() {
// I'm assuming you place all your fragments here, if not..
// alter to suit
return getFragmentManager().findFragmentById(R.id.root);
}
在 MyListFragment 中添加以下内容:
import BackKeyListener;
class MyListFragment extends Fragment(orsomething) implements BackKeyListener
public boolean onBackPressed() {
// User pressed back, select default item..
((MainActivity) getActivity()).itemSelected(selectedItem);
// In this case, prevent normal back processing or
// you might get a double backstack pop.
return true;
}
在片段上使用接口允许您 "not care" 活动片段是什么。 如果它实现了 BackKeyListener,MainActivity 将在按下后退键时调用它。 这使您可以选择将值传递回 activity.
一种解决方案是使用setTargetFragment/getTargetFragment 方法。用这种方法 您可以在没有 Activity 和 SharedPreferences 帮助的情况下仅使用您的片段。 在您的 MyMainFragment 中,当用户单击按钮时 "Select item..." 您可以将回调 selectedButtonClicked() 用作:
public void selectedButtonClicked() {
Fragment fragment = MyListFragment.newInstance(index);
//Set the TargetFragment
fragment.setTargetFragment(MyMainFragment.this, MyListFragment.REQUESTCODE)
getFragmentManager().beginTransaction()
.addToBackStack(null)
.replace(R.id.root, fragment, LIST)
.commit();
}
您必须在 ActivityMainFragment 的结果上定义回调:
public void onActivityResult(int requestCode, int resultCode, Intent data){
if(requestCode == MyListFragment.REQUESTCODE){
f.setText("Selected index: " + intent.getInt("INDEX");
}
}
在 MyListFragment 中,一旦用户选择了一个项目,您就可以调用您的回调 itemSelected()
public void itemSelected(int index) {
Intent i = new Intent();
i.putExtraInt("INDEX",index);
getTargetFragment.onActivityResult(MyListFragment.REQUESTCODE,Context.Result_OK,i);
getFragmentManager().popBackStackImmediate();
}