【问题标题】:Sending data from nested fragments to parent fragment将数据从嵌套片段发送到父片段
【发布时间】:2014-06-02 07:18:03
【问题描述】:

我有一个FragmentFR1,其中包含几个NestedFragmentsFRaFRbFRc。这些Nested Fragments 通过在FR1 的布局上按Buttons 进行更改。每个Nested Fragments 中都有几个输入字段;其中包括 EditTextsNumberPickersSpinners 之类的内容。当我的用户完成并填写Nested Fragments 的所有值时,FR1(父片段)有一个提交按钮。

然后我怎样才能从我的NestedFragments 中检索我的值并将它们带入FR1

  1. 在每个 Nested Fragment 中声明并以编程方式处理所有 Views
  2. FragmentFR1处理NestedFragments的事务。

我希望这个问题足够清楚,我不确定是否需要发布代码,但如果有人觉得我可以这样做。

编辑 1:

这是我添加Nested Fragments 的方法:

tempRangeButton.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {

            getChildFragmentManager().beginTransaction()
                    .add(R.id.settings_fragment_tertiary_nest, tempFrag)
                    .commit();

        }
    });

    scheduleButton.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {

            getChildFragmentManager().beginTransaction()
                    .add(R.id.settings_fragment_tertiary_nest, scheduleFrag)
                    .commit();
        }
    });

    alertsButton.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {

            getChildFragmentManager().beginTransaction()
                    .add(R.id.settings_fragment_tertiary_nest, alertsFrag)
                    .commit();

        }
    });

    submitProfile.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            constructNewProfile();
        }
    });

我的constructNewProfile() 方法需要来自我的Nested Fragments 的值。

public Fragment tempFrag = fragment_profile_settings_temperature
        .newInstance();
public Fragment scheduleFrag= fragment_profile_settings_schedules
            .newInstance();
public Fragment alertsFrag = fragment_profile_settings_alerts
        .newInstance();

以上是指父Fragment的字段;以及它们最初是如何实例化的。

【问题讨论】:

标签: android android-fragments parameter-passing android-nested-fragment navigation-architecture


【解决方案1】:

片段之间的数据传递可以通过 FragmentManager 完成。从 Fragment 1.3.0-alpha04 开始,我们可以使用 setFragmentResultListener() 和 setFragmentResult() API 在 Fragment 之间共享数据。

Official Documentation

【讨论】:

    【解决方案2】:

    回答太晚了,我可以建议在子片段中创建 EditText 对象

    EditText tx;
    

    在 Oncreateview 中初始化它。然后为桥创建另一个类,例如

    public class bridge{
    public static EditText text = null; 
    }
    

    现在在父片段中获得它的引用。

    EditText childedtx = bridge.text;
    

    现在点击方法获取值

    onclick(view v){
    childedtx.getText().tostring();
    }
    

    在我的项目中进行了测试,它的工作就像魅力一样。

    【讨论】:

      【解决方案3】:

      您可以在片段之间使用共享数据。

      public class SharedViewModel extends ViewModel {
          private final MutableLiveData<Item> selected = new MutableLiveData<Item>();
      
          public void select(Item item) {
              selected.setValue(item);
          }
      
          public LiveData<Item> getSelected() {
              return selected;
          }
      }
      
      
      public class MasterFragment extends Fragment {
          private SharedViewModel model;
          public void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
              itemSelector.setOnClickListener(item -> {
                  model.select(item);
              });
          }
      }
      
      public class DetailFragment extends Fragment {
          public void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
              model.getSelected().observe(this, item -> {
                 // Update the UI.
              });
          }
      }
      

      更多信息ViewModel Architecture

      【讨论】:

        【解决方案4】:

        不使用接口,你可以通过下面的方式调用子fragment:

        ( (YourFragmentName) getParentFragment() ).yourMethodName();
        

        【讨论】:

        • 这应该是接受的答案恕我直言。它是一行代码,完全符合需要。希望我能多点赞
        • 如果可能,您应该避免使用此解决方案。片段的全部意义在于它是 UI 的可重用部分。如果您的子片段包含对父片段的硬编码引用,那么您将失去对子片段的可重用性。这就是接口很棒的原因,父母会选择如何实现它们。
        • 除了懒惰和不正确使用片段之外,这不是一个好的解决方案。如果子 Fragment 知道父 Fragment 是什么类,那么您可能不应该创建子 Fragment,而只需将代码放在父 Fragment 中即可。
        • @uDevel 有时您必须使用子 Fragment - 假设您希望在父 Fragment 中有标签。在这种情况下,子 Fragment 可能只与父 Fragment 一起工作。我想在这种情况下,单线解决方案很棒。
        • 这不是一个好的解决方案。您的子片段与您的父片段紧密耦合
        【解决方案5】:

        在片段之间传递数据的最佳方式是使用接口。这是您需要做的: 在你嵌套的片段中:

        public interface OnDataPass {
            public void OnDataPass(int i);
        }
        
        OnDataPass dataPasser;
        
        @Override
        public void onAttach(Activity a) {
            super.onAttach(a);
            dataPasser = (OnDataPass) a;
        }
        
        public void passData(int i) {
            dataPasser.OnDataPass(i);
        }
        

        在你的父片段中:

        public class Fragment_Parent extends Fragment implements OnDataPass {
        ...
        
            @Override
            public void OnDataPass(int i) {
                this.input = i;
            }
        
            btnOk.setOnClickListener(new OnClickListener() {
        
                @Override
                public void onClick(View v) {
                    Fragment fragment = getSupportFragmentManager().findFragmentByTag("0");
                    ((Fragment_Fr1) fragment).passData();
                }
            }
        
        }
        

        【讨论】:

        • onAttach(Activity a) 现已弃用。相反,我实现了onAttach(@NonNull Context context),但dataPasser = (OnDataPass) context; 给了我一个致命的异常:java.lang.ClassCastException: com.myapp.MainActivity 不能转换为 com.myapp.OnDataPass
        • 当我定义时它起作用了:dataPasser = (OnDataPass) getParentFragment();。不好吗?
        【解决方案6】:

        最好的方法是使用接口:

        1. 在嵌套片段中声明一个接口

          // Container Activity or Fragment must implement this interface
          public interface OnPlayerSelectionSetListener
          {
              public void onPlayerSelectionSet(List<Player> players_ist);
          }
          
        2. 将接口附加到父片段

          // In the child fragment.
          public void onAttachToParentFragment(Fragment fragment)
          {
              try
              {
                  mOnPlayerSelectionSetListener = (OnPlayerSelectionSetListener)fragment;
          
              }
              catch (ClassCastException e)
              {
                    throw new ClassCastException(
                        fragment.toString() + " must implement OnPlayerSelectionSetListener");
              }
          }
          
          
          @Override
          public void onCreate(Bundle savedInstanceState)
          {
              Log.i(TAG, "onCreate");
              super.onCreate(savedInstanceState);
          
              onAttachToParentFragment(getParentFragment());
          
              // ...
          }
          
        3. 在按钮点击时调用监听器。

          // In the child fragment.
          @Override
          public void onClick(View v)
          {
              switch (v.getId())
              {
                  case R.id.tv_submit:
                      if (mOnPlayerSelectionSetListener != null)
                      {                
                           mOnPlayerSelectionSetListener.onPlayerSelectionSet(selectedPlayers);
                      }
                      break;
                  }
              }
          
        4. 让你的父片段实现接口。

           public class Fragment_Parent extends Fragment implements Nested_Fragment.OnPlayerSelectionSetListener
           {
                // ...
                @Override
                public void onPlayerSelectionSet(final List<Player> players_list)
                {
                     FragmentManager fragmentManager = getChildFragmentManager();
                     SomeOtherNestFrag someOtherNestFrag = (SomeOtherNestFrag)fragmentManager.findFragmentByTag("Some fragment tag");
                     //Tag of your fragment which you should use when you add
          
                     if(someOtherNestFrag != null)
                     {
                          // your some other frag need to provide some data back based on views.
                          SomeData somedata = someOtherNestFrag.getSomeData();
                          // it can be a string, or int, or some custom java object.
                     }
                }
           }
          

        在进行片段交易时添加标签,以便您可以在之后查找它以调用其方法。 FragmentTransaction

        这是处理片段和嵌套片段之间通信的正确方法,对于活动和片段几乎相同。 http://developer.android.com/guide/components/fragments.html#EventCallbacks

        其实官方还有另外一种方式,就是使用activity result,不过这个已经够用了,也很常用。

        【讨论】:

        • getParentFragment() 仅适用于 API 17+,因此此答案应明确说明,否则不够清楚
        • @uDevel onAttachFragment(getParentFragment());(在第 2 步中)有什么作用?是在Activity吗?
        • @ShrimpCrackers 可能会有些混淆,因为由于原生 Android 中的 Support Library v24.0.0 或 API 24,Google 工程师引入了一种名为 onAttachFragment(Fragment) 的新方法,它实际上与此答案中的方法略有不同这里。 uDevei 2年前写这个答案时恰好选择了相同的方法名称。需要明确的是:您没有覆盖 Android 框架中的 onAttachFragment(),您是在实现您自己的。
        • @TonyChan 感谢您澄清困惑。由于谷歌创建了一个同名的新方法,我想用一个更好的方法名称来更新这个答案,但我想不出一个不那么容易混淆的名字。来自谷歌的那个实际上命名不正确,imo。通过阅读它的作用,它应该被称为 onChildFragmentAttached。
        • 将方法名称更新为 onAttachToParentFragment 以避免与 Google 的方法混淆。
        【解决方案7】:

        在获取更好的父片段之前检查instanceOf

        if (getParentFragment() instanceof ParentFragmentName) {
          getParentFragment().Your_parent_fragment_method();
        }
        

        【讨论】:

          【解决方案8】:

          您可以使用getChildFragmentManager() 并找到嵌套片段,获取它们并运行一些方法来检索输入值

          【讨论】:

          • 我已经使用 childFragmentManager 最初在我的父片段中提交交易;但是,当我尝试在某些父片段的方法中调用 getChildFragmentManager() 时,它会返回错误。
          • 所以 FR1 有方法 "constructFRaProfile()";我在 FR1 中点击提交后调用它。我应该调用 childFragmentManager().findFragmentByTag("FRa"); 吗?然后开始检索值?
          • 我不确定我是否正确设置了标签; android是自动为子fragment设置标签还是必须手动设置?
          • 您是如何使用您的子片段的?从代码添加或在 XML 布局中使用它们
          • @Jedil 你不能在 XML 中添加子片段,它们必须以编程方式添加
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2015-12-02
          • 2014-05-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多