【问题标题】:How to send data from DialogFragment to a Fragment?如何将数据从 DialogFragment 发送到 Fragment?
【发布时间】:2013-09-02 19:21:31
【问题描述】:

我有一个片段,它打开一个Dialogfragment 以获取用户输入(一个字符串和一个整数)。我如何将这两件事发送回片段?

这是我的 DialogFragment:

public class DatePickerFragment extends DialogFragment {
    String Month;
    int Year;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        getDialog().setTitle(getString(R.string.Date_Picker));
        View v = inflater.inflate(R.layout.date_picker_dialog, container, false);

        Spinner months = (Spinner) v.findViewById(R.id.months_spinner);
        ArrayAdapter<CharSequence> monthadapter = ArrayAdapter.createFromResource(getActivity(),
                R.array.Months, R.layout.picker_row);
              months.setAdapter(monthadapter);
              months.setOnItemSelectedListener(new OnItemSelectedListener(){
                  @Override
                  public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int monthplace, long id) {
                      Month = Integer.toString(monthplace);
                  }
                  public void onNothingSelected(AdapterView<?> parent) {
                    }
              });

        Spinner years = (Spinner) v.findViewById(R.id.years_spinner);
        ArrayAdapter<CharSequence> yearadapter = ArrayAdapter.createFromResource(getActivity(),
             R.array.Years, R.layout.picker_row);
        years.setAdapter(yearadapter);
        years.setOnItemSelectedListener(new OnItemSelectedListener(){
          @Override
          public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int yearplace, long id) {
              if (yearplace == 0){
                  Year = 2012;
              }if (yearplace == 1){
                  Year = 2013;
              }if (yearplace == 2){
                  Year = 2014;
              }
          }
          public void onNothingSelected(AdapterView<?> parent) {}
        });

        Button button = (Button) v.findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
           public void onClick(View v) {
               getDialog().dismiss();
            }
        });

        return v;
    }
}

我需要在按钮点击之后和getDialog().dismiss()之前发送数据

这里是需要发送数据的片段:

public class CalendarFragment extends Fragment {
int Year;
String Month;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    int position = getArguments().getInt("position");
    String[] categories = getResources().getStringArray(R.array.categories);
    getActivity().getActionBar().setTitle(categories[position]);
    View v = inflater.inflate(R.layout.calendar_fragment_layout, container, false);    

    final Calendar c = Calendar.getInstance();
    SimpleDateFormat month_date = new SimpleDateFormat("MMMMMMMMM");
    Month = month_date.format(c.getTime());
    Year = c.get(Calendar.YEAR);

    Button button = (Button) v.findViewById(R.id.button);
    button.setText(Month + " "+ Year);
    button.setOnClickListener(new View.OnClickListener() {
        public void onClick(View v) {
           new DatePickerFragment().show(getFragmentManager(), "MyProgressDialog");
        }
    });
   return v;
  }
}

所以一旦用户在Dialogfragment 中选择了一个日期,它就必须返回月份和年份。

然后,按钮上的文本应更改为用户指定的月份和年份。

【问题讨论】:

    标签: java android android-fragments


    【解决方案1】:

    注意:除了一两个 Android Fragment 特定调用之外,这是实现松散耦合组件之间数据交换的通用方法。您可以安全地使用这种方法在几乎任何东西之间交换数据,无论是片段、活动、对话框还是应用程序的任何其他元素。


    食谱如下:

    1. 创建interface(即命名为MyContract),其中包含用于传递数据的方法的签名,即methodToPassMyData(... data);
    2. 确保您的DialogFragment 完成该合同(这通常意味着实现接口):class MyFragment extends Fragment implements MyContract {....}
    3. 在创建DialogFragment 时,通过调用myDialogFragment.setTargetFragment(this, 0); 将调用Fragment 设置为其target fragment。这是您稍后要与之交谈的对象。
    4. 在您的DialogFragment 中,通过调用getTargetFragment(); 获取调用片段并将返回的对象转换为您在步骤1 中创建的合约接口,方法是:MyContract mHost = (MyContract)getTargetFragment();。铸造让我们确保目标对象实现所需的合同,我们可以期待methodToPassData() 在那里。如果没有,那么您将获得常规的ClassCastException。这通常不应该发生,除非您进行过多的复制粘贴编码:) 如果您的项目使用外部代码、库或插件等,在这种情况下,您应该捕获异常并告诉用户,即插件不兼容而不是让应用崩溃。
    5. 当返回数据的时间到来时,在您之前获得的对象上调用methodToPassMyData()((MyContract)getTargetFragment()).methodToPassMyData(data);。如果您的onAttach() 已经将目标片段转换并分配给类变量(即mHost),那么此代码将只是mHost.methodToPassMyData(data);
    6. 瞧。您刚刚成功地将数据从对话框传递回调用片段。

    【讨论】:

    • 你能不能只在片段内部定义方法而不是通过接口传递?
    • 你可以,但是对话框必须知道调用它的片段类型。这将使对话框只能从一个特定的片段中使用。通过接口回调是一种更灵活的方法。
    • 我喜欢这个答案。它不仅清楚地帮助了我,而且您在没有编写代码块和解释步骤的情况下就做到了。 +1
    • 很遗憾,无法为 BottomSheetDialog 设置目标片段。有没有其他方法可以完成 BottomSheetDialog 案例的第三步?
    • 您能否将以下内容转换为示例代码,因为阅读说明并实施它真的很混乱
    【解决方案2】:

    这是另一个不使用任何接口的配方。只需使用setTargetFragmentBundle 在DialogFragment 和Fragment 之间传递数据。

    public static final int DATEPICKER_FRAGMENT = 1; // class variable
    

    1。 拨打DialogFragment如下图:

    // create dialog fragment
    DatePickerFragment dialog = new DatePickerFragment();
    
    // optionally pass arguments to the dialog fragment
    Bundle args = new Bundle();
    args.putString("pickerStyle", "fancy");
    dialog.setArguments(args);
    
    // setup link back to use and display
    dialog.setTargetFragment(this, DATEPICKER_FRAGMENT);
    dialog.show(getFragmentManager().beginTransaction(), "MyProgressDialog")
    

    2。 在DialogFragment 中的Intent 中使用额外的Bundle 将任何信息传递回目标片段。以下代码在DatePickerFragmentButton#onClick() 事件中传递一个字符串和整数。

    Intent i = new Intent()
            .putExtra("month", getMonthString())
            .putExtra("year", getYearInt());
    getTargetFragment().onActivityResult(getTargetRequestCode(), Activity.RESULT_OK, i);
    dismiss();
    

    3。 使用CalendarFragmentonActivityResult()方法读取值:

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
            case DATEPICKER_FRAGMENT:
                if (resultCode == Activity.RESULT_OK) {
                    Bundle bundle = data.getExtras();
                    String mMonth = bundle.getString("month", Month);
                    int mYear = bundle.getInt("year");
                    Log.i("PICKER", "Got year=" + year + " and month=" + month + ", yay!");
                } else if (resultCode == Activity.RESULT_CANCELED) {
                    ...
                }
                break;
        }
    }
    

    【讨论】:

    • 一定要使用onActivityResult吗?您如何将数据从片段传递到对话框 - 这看起来只是对话框到片段?
    • @xphill64x:是的,onActivityResult 是 Fragment 中接收 DialogFragment 响应的方法。您可以使用Bundle 传递来自Fragment to DialogFragment 的数据。
    • 这不是最好的方法,因为您无法在不离开目标的情况下传递数据,而使用界面则没有此限制。
    • @Philip 您可以像往常一样使用Fragment.setArguments() 传递数据。
    • 很好的答案先生
    【解决方案3】:

    这是一种说明 Marcin 在 kotlin 中实现的答案的方法。

    1.在你的 dialogFragment 类中创建一个具有传递数据方法的接口。

    interface OnCurrencySelected{
        fun selectedCurrency(currency: Currency)
    }
    

    2.在你的 dialogFragment 构造函数中添加你的接口。

    class CurrencyDialogFragment(val onCurrencySelected :OnCurrencySelected)    :DialogFragment() {}
    

    3.现在让你的 Fragment 实现你刚刚创建的接口

    class MyFragment : Fragment(), CurrencyDialogFragment.OnCurrencySelected {
    
    override fun selectedCurrency(currency: Currency) {
    //this method is called when you pass data back to the fragment
    }}
    

    4.然后显示您刚刚调用的 dialogFragment CurrencyDialogFragment(this).show(fragmentManager,"dialog")this 是您将与之交谈的接口对象,用于将数据传回您的 Fragment。

    5.当您想将数据发送回您的 Fragment 时,您只需调用该方法将数据传递给您传入的 dialogFragment 构造函数的接口对象。

    onCurrencySelected.selectedCurrency(Currency.USD)
    dialog.dismiss()
    

    【讨论】:

      【解决方案4】:

      一个好的提示是使用ViewModelLiveData 方法,这是最好的方法。下面是关于如何在Fragments 之间共享数据的代码示例:

      class SharedViewModel : ViewModel() {
          val selected = MutableLiveData<Item>()
      
          fun select(item: Item) {
              selected.value = item
          }
      }
      
      class MasterFragment : Fragment() {
      
          private lateinit var itemSelector: Selector
      
          private lateinit var model: SharedViewModel
      
          override fun onCreate(savedInstanceState: Bundle?) {
              super.onCreate(savedInstanceState)
              model = activity?.run {
                  ViewModelProviders.of(this).get(SharedViewModel::class.java)
              } ?: throw Exception("Invalid Activity")
              itemSelector.setOnClickListener { item ->
                  // Update the UI
              }
          }
      }
      
      class DetailFragment : Fragment() {
      
          private lateinit var model: SharedViewModel
      
          override fun onCreate(savedInstanceState: Bundle?) {
              super.onCreate(savedInstanceState)
              model = activity?.run {
                  ViewModelProviders.of(this).get(SharedViewModel::class.java)
              } ?: throw Exception("Invalid Activity")
              model.selected.observe(this, Observer<Item> { item ->
                  // Update the UI
              })
          }
      }
      

      试试吧,这些新组件来帮助我们,我认为它比其他方法更有效。

      了解更多信息:https://developer.android.com/topic/libraries/architecture/viewmodel

      【讨论】:

      • 我一直遇到这样的问题:当对话框被解除时,与 android Navigation 一起使用的 LiveData 不会触发回调。似乎认为父片段处于停止状态(事实并非如此)。否则,这在文档中的推荐方法中。
      • 你能打开另一个问题并分享一个示例代码吗?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-04
      • 2013-09-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多