[英]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:这是食谱:
interface
(ie named MyContract
) containing a signature of method for passing the data, ie methodToPassMyData(... data);
interface
(即名为MyContract
),即methodToPassMyData(... data);
. DialogFragment
fullfils that contract (which usually means implementing the interface): class MyFragment extends Fragment implements MyContract {....}
DialogFragment
满足该合同(这通常意味着实现接口): class MyFragment extends Fragment implements MyContract {....}
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);
. 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();
. 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.methodToPassMyData()
on the object you obtained previously: ((MyContract)getTargetFragment()).methodToPassMyData(data);
methodToPassMyData()
: ((MyContract)getTargetFragment()).methodToPassMyData(data);
. 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);
. Here's another recipe without using any Interface.这是另一个不使用任何接口的方法。 Just making use of the
setTargetFragment
and Bundle
to pass data between DialogFragment and Fragment.只是利用
setTargetFragment
和Bundle
在 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. 在
DialogFragment
的Intent
中使用额外的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. 使用
CalendarFragment
的onActivityResult()
方法读取值:
@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.一个很好的提示是使用
ViewModel
和LiveData
方法,这是最好的方法。 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.