【问题标题】:How to pass selected item from ListFragment to previous Fragment after calling popBackStack()?调用 popBackStack() 后,如何将 ListFragment 中的选定项目传递给前一个 Fragment?
【发布时间】:2015-01-27 10:34:27
【问题描述】:

我为我的问题准备了一个非常简单的测试用例,其中包含 1 个活动和 2 个片段:

最初 MainActivity 显示 MyMainFragment。

当用户触摸“选择项目...”按钮时,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 完成了所有“繁重的工作”:它显示 2 个片段中的 1 个,并将用户数据保存在共享首选项中。

我的问题是:如何在 MyMainFragment 的mSelectedTextView 中显示选中的项目(上图中的“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 中的项目时才有效。但是,当用户通过触摸返回按钮返回 MyMainFragment 时,它不起作用。

【问题讨论】:

  • 查看Android Custom Event Listener的以下帖子,使用自定义事件片段将从另一个片段获取更新
  • @AlexanderFarber:就像你将它从 Fragment2 传递给 Activity 一样?将popBackStack() 替换为popBackStackImmediate() 并使用findFragmentById() 检索Fragment
  • 即使用户在没有选择任何项目的情况下按下返回按钮,您是否需要与主Fragment 通信?在这种情况下,您需要添加 OnBackStackChangedListener 来收听该事件。
  • @AlexanderFarber:您需要将所选项目保存为主 Fragment 状态的一部分,因为它在移入和移出后堆栈时会被销毁并重新创建。
  • @AlexanderFarber:使用 onSaveInstanceState() 回调来保存您的状态。

标签: android android-fragments android-listfragment


【解决方案1】:

在我看来,你已经有了一个合理的答案的第一部分(corsair992 的建议 - 传递所选项目的值)第二部分是你如何处理后退键。

有几种方法可以做到这一点(听后台堆栈等),但我最常用的一种是:

声明一个后退键监听接口:

public interface BackKeyListener {
   boolean onBackPressed();
}

在您的主要活动中添加以下内容:

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

在片段上使用接口可以让您“不关心”活动片段是什么。 如果它实现了 BackKeyListener,则 MainActivity 将在按下返回键时调用它。 这使您可以选择将值传递回活动。

【讨论】:

  • 有时可以在Activity的onBackPressed()回调之前分离片段。
  • 一个有效的点。使用片段或活动引用时进行空指针检查总是值得的。 (findFragment/getActivity 等)
【解决方案2】:

发生这种情况时,您的第一个片段是否仍保留在内存中?我假设是这样。这意味着您可以使用SharedPreferences.OnSharedPreferenceChangeListener。只需让 MyMainFragment 实现接口,然后使用registerOnSharedPreferenceChangeListener 来监视更改。不过别忘了注销!

一个更好的想法可能是将您的活动注册为侦听器,并使其在再次显示时将该数据传递给 MyMainFragment。

【讨论】:

  • “让 Activity 将该数据传递给 MyMainFragment”——这是我的问题。
【解决方案3】:

首先,您可以在 UI 线程内(即片段内)使用 SharedPrefences。这种常见的误解源于只有commit() 方法的时候。现在我们让apply() 异步工作,get*() 使用缓存数据。

第二种,在不使用 SDK 的情况下解决您的问题的最简单方法是通知 Activity 您的第二个片段中的项目选择(例如,在您的片段中:((MainActivity)getActivity()).setSelectedItem(itemId);)并在您的第一个片段中获取此值onResume()(例如,((MainActivity)getActivity()).getSelectedItem();)。当然,如果没有选择任何内容,您应该检查此值是否为 null。

【讨论】:

    【解决方案4】:

    为 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 设置适当的值,您可能需要进行一些额外的检查。

    【讨论】:

    • 参数只能在片段尚未初始化时设置,在这种情况下,片段是完全托管的,因此在管理器从返回堆栈。
    • @corsair992,你错了,因为我在生产中使用它并且一切正常。
    • 好的,您编辑的代码可能会工作,但只有在 Fragment 尚未从 FragmentManager删除(只是分离)时它才能工作。我猜这就是您在生产代码中实现它的方式。
    • @corsair992,就像他的问题中的 Alexander 一样替换。
    • 替换事务会导致之前的FragmentFragmentManager 中删除,并且弹出返回堆栈会导致创建它的新实例,所以我看不出你怎么能在这种情况下与Fragment 实例交互,然后再弹出回栈。
    【解决方案5】:

    一种解决方案是使用 setTargetFragment/getTargetFragment 方法。用这种方法 您可以在没有 Activity 和 SharedPreferences 帮助的情况下仅使用 Fragments。 在您的 MyMainFragment 中,当用户单击按钮时 “选择项目...”您可以将回调 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();
     }
    

    你必须定义MainFragment的回调onActivityResult:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多