简体   繁体   English

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

[英]How to send data from DialogFragment to a Fragment?

I have a fragment that opens a Dialogfragment to get user input (a string, and an integer).我有一个片段,它打开一个Dialogfragment来获取用户输入(一个字符串和一个整数)。 How do I send these two things back to the fragment?我如何将这两件事发送回片段?

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.然后,按钮上的文本应更改为用户指定的月份和年份。

NOTE: aside from one or two Android Fragment specific calls, this is a generic recipe for implementation of data exchange between loosely coupled components.注意:除了一两个 Android Fragment 特定调用之外,这是实现松散耦合组件之间数据交换的通用方法。 You can safely use this approach to exchange data between literally anything, be it Fragments, Activities, Dialogs or any other elements of your application.您可以安全地使用这种方法在任何东西之间交换数据,无论是片段、活动、对话框还是应用程序的任何其他元素。


Here's the recipe:这是食谱:

  1. Create interface (ie named MyContract ) containing a signature of method for passing the data, ie methodToPassMyData(... data);创建包含传递数据的方法签名的interface (即名为MyContract ),即methodToPassMyData(... data); . .
  2. Ensure your DialogFragment fullfils that contract (which usually means implementing the interface): class MyFragment extends Fragment implements MyContract {....}确保您的DialogFragment满足该合同(这通常意味着实现接口): class MyFragment extends Fragment implements MyContract {....}
  3. On creation of DialogFragment set your invoking Fragment as its target fragment by calling myDialogFragment.setTargetFragment(this, 0);在创建DialogFragment通过调用myDialogFragment.setTargetFragment(this, 0);将调用Fragment设置为其目标片段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();在您的DialogFragment ,通过调用getTargetFragment();获取该调用片段getTargetFragment(); and cast returned object to the contract interface you created in step 1, by doing: MyContract mHost = (MyContract)getTargetFragment();并将返回的对象MyContract mHost = (MyContract)getTargetFragment();您在第 1 步中创建的合约接口,方法是: MyContract mHost = (MyContract)getTargetFragment(); . . Casting lets us ensure the target object implements the contract needed and we can expect methodToPassData() to be there.转换让我们确保目标对象实现所需的契约,我们可以期待methodToPassData()存在。 If not, then you will get regular ClassCastException .如果没有,那么您将收到常规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 ie 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);当返回数据的时间到来时,对之前获得的对象调用methodToPassMyData()((MyContract)getTargetFragment()).methodToPassMyData(data); . . If your onAttach() already casts and assigns target fragment to a class variable (ie mHost ), then this code would be just mHost.methodToPassMyData(data);如果您的onAttach()已经将目标片段转换并分配给一个类变量(即mHost ),那么此代码将只是mHost.methodToPassMyData(data); . .
  6. Voilà.瞧。 You just successfully passed your data from dialog back to invoking fragment.您刚刚成功地将对话框中的数据传递回调用片段。

Here's another recipe without using any Interface.这是另一个不使用任何接口的方法。 Just making use of the setTargetFragment and Bundle to pass data between DialogFragment and Fragment.只是利用setTargetFragmentBundle在 DialogFragment 和 Fragment 之间传递数据。

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

1. Call the DialogFragment as 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 Bundle in an Intent in the DialogFragment to pass whatever info back to the target fragment. 2. 在DialogFragmentIntent中使用额外的Bundle将任何信息传递回目标片段。 The below code in Button#onClick() event of DatePickerFragment passes a String and Integer. DatePickerFragment Button#onClick()事件中的以下代码传递一个字符串和整数。

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

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") . 4.然后显示你的 dialogFragment 你只需调用CurrencyDialogFragment(this).show(fragmentManager,"dialog") this is the interface object you will be talking to, to pass data back to your Fragment. 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()

A good tip is to use the ViewModel and LiveData approach that is the best way to go.一个很好的提示是使用ViewModelLiveData方法,这是最好的方法。 Bellow is the code sample about how to share data between Fragment s: Bellow 是关于如何在Fragment之间共享数据的代码示例:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM