Java 如何将数据从 DialogFragment 发送到 Fragment?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18579590/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 09:10:52  来源:igfitidea点击:

How to send data from DialogFragment to a Fragment?

javaandroidandroid-fragments

提问by trickedoutdavid

I have a fragment that opens a Dialogfragmentto get user input (a string, and an integer). How do I send these two things back to the fragment?

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

Here is my 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;
    }
}

I need to send the data after the button click and before getDialog().dismiss()

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

Here is the fragment that data needs to be sent to:

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

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

so once the user selects a date in the Dialogfragment, it must return the month and year.

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

Then, the text on the button should change to the month and year specified by user.

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

采纳答案by Marcin Orlowski

NOTE: aside from one or two Android Fragment specific calls, this is a generic recipe for implementation of data exchange between loosely coupled components. You can safely use this approach to exchange data between literally anything, be it Fragments, Activities, Dialogs or any other elements of your application.

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



Here's the recipe:

这是食谱:

  1. Create interface(i.e. named MyContract) containing a signature of method for passing the data, i.e. methodToPassMyData(... data);.
  2. Ensure your DialogFragmentfullfils that contract (which usually means implementing the interface): class MyFragment extends Fragment implements MyContract {....}
  3. On creation of DialogFragmentset your invoking Fragmentas its target fragmentby calling myDialogFragment.setTargetFragment(this, 0);. This is the object you will be talking to later.
  4. In your DialogFragment, get that invoking fragment by calling getTargetFragment();and cast returned object to the contract interface you created in step 1, by doing: MyContract mHost = (MyContract)getTargetFragment();. Casting lets us ensure the target object implements the contract needed and we can expect methodToPassData()to be there. If not, then you will get regular ClassCastException. This usually should not happen, unless you are doing too much copy-paste coding :) If your project uses external code, libraries or plugins etc and in such case you should rather catch the exception and tell the user i.e. plugin is not compatible instead of letting the app crash.
  5. When time to send data back comes, call methodToPassMyData()on the object you obtained previously: ((MyContract)getTargetFragment()).methodToPassMyData(data);. If your onAttach()already casts and assigns target fragment to a class variable (i.e. mHost), then this code would be just mHost.methodToPassMyData(data);.
  6. Voilà. You just successfully passed your data from dialog back to invoking fragment.
  1. 创建interface(即命名MyContract)包含用于传递数据的方法签名,即methodToPassMyData(... data);
  2. 确保您DialogFragment履行该合同(这通常意味着实现接口):class MyFragment extends Fragment implements MyContract {....}
  3. 在创建时通过调用DialogFragment将您的调用设置Fragment为其目标片段myDialogFragment.setTargetFragment(this, 0);。这是您稍后将要与之交谈的对象。
  4. 在你DialogFragment通过调用获取调用片段getTargetFragment();和接口在步骤1中创建的,通过做投返回的对象合同:MyContract mHost = (MyContract)getTargetFragment();。转换让我们确保目标对象实现所需的契约,我们可以期待methodToPassData()在那里。如果没有,那么你会得到定期ClassCastException。这通常不应该发生,除非你做了太多的复制粘贴编码:) 如果你的项目使用外部代码、库或插件等,在这种情况下你应该捕捉异常并告诉用户即插件不兼容而不是让应用程序崩溃。
  5. 当发送数据的时间到来时,调用methodToPassMyData()您之前获得的对象:((MyContract)getTargetFragment()).methodToPassMyData(data);。如果您onAttach()已经将目标片段强制转换并分配给类变量(即mHost),那么此代码将只是mHost.methodToPassMyData(data);.
  6. 瞧。您刚刚成功地将对话框中的数据传递回调用片段。

回答by blizzard

Here's another recipe without using any Interface. Just making use of the setTargetFragmentand Bundleto pass data between DialogFragment and Fragment.

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

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

1. Call the DialogFragmentas shown below:

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. Use the extra Bundlein an Intentin the DialogFragmentto pass whatever info back to the target fragment. The below code in Button#onClick()event of DatePickerFragmentpasses a String and Integer.

2. 使用BundleinIntent中的额外DialogFragment信息将任何信息传递回目标片段。下面的代码中Button#onClick()的事件DatePickerFragment传递一个字符串和整数。

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

3. Use CalendarFragment's onActivityResult()method to read the values:

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

回答by Pedro Massango

A good tip is to use the ViewModeland LiveDataapproach that is the best way to go. Bellow is the code sample about how to share data between Fragments:

一个很好的提示是使用ViewModelLiveData方法是最好的方法。Bellow 是关于如何在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
        })
    }
}

Try it, these new components come to help us, I think that it is more efficient than other approach.

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

Read more about it: https://developer.android.com/topic/libraries/architecture/viewmodel

阅读更多相关信息:https: //developer.android.com/topic/libraries/architecture/viewmodel

回答by DAVID KATHOH

Here's an approach that illustrates Marcin's answer implemented in kotlin.

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

1.Create an interface that have a method for passing data in your dialogFragment class.

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

interface OnCurrencySelected{
    fun selectedCurrency(currency: Currency)
}

2.Add your interface in your dialogFragment constructor.

2.在你的 dialogFragment 构造函数中添加你的界面。

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

3.Now make your Fragment implement the interface you just created

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.Then to show your dialogFragment your just call CurrencyDialogFragment(this).show(fragmentManager,"dialog"). thisis the interface object you will be talking to, to pass data back to your Fragment.

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

5.When you want to sent data back to your Fragment you just call the method to pass data on the interface object your passed in dialogFragment constructor.

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

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