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
How to send data from DialogFragment to a Fragment?
提问by trickedoutdavid
I have a fragment that opens a Dialogfragment
to 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:
这是食谱:
- Create
interface
(i.e. namedMyContract
) containing a signature of method for passing the data, i.e.methodToPassMyData(... data);
. - Ensure your
DialogFragment
fullfils that contract (which usually means implementing the interface):class MyFragment extends Fragment implements MyContract {....}
- On creation of
DialogFragment
set your invokingFragment
as its target fragmentby callingmyDialogFragment.setTargetFragment(this, 0);
. This is the object you will be talking to later. - In your
DialogFragment
, get that invoking fragment by callinggetTargetFragment();
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 expectmethodToPassData()
to be there. If not, then you will get regularClassCastException
. 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. - When time to send data back comes, call
methodToPassMyData()
on the object you obtained previously:((MyContract)getTargetFragment()).methodToPassMyData(data);
. If youronAttach()
already casts and assigns target fragment to a class variable (i.e.mHost
), then this code would be justmHost.methodToPassMyData(data);
. - Voilà. You just successfully passed your data from dialog back to invoking fragment.
- 创建
interface
(即命名MyContract
)包含用于传递数据的方法签名,即methodToPassMyData(... data);
。 - 确保您
DialogFragment
履行该合同(这通常意味着实现接口):class MyFragment extends Fragment implements MyContract {....}
- 在创建时通过调用
DialogFragment
将您的调用设置Fragment
为其目标片段myDialogFragment.setTargetFragment(this, 0);
。这是您稍后将要与之交谈的对象。 - 在你
DialogFragment
通过调用获取调用片段getTargetFragment();
和接口在步骤1中创建的,通过做投返回的对象合同:MyContract mHost = (MyContract)getTargetFragment();
。转换让我们确保目标对象实现所需的契约,我们可以期待methodToPassData()
在那里。如果没有,那么你会得到定期ClassCastException
。这通常不应该发生,除非你做了太多的复制粘贴编码:) 如果你的项目使用外部代码、库或插件等,在这种情况下你应该捕捉异常并告诉用户即插件不兼容而不是让应用程序崩溃。 - 当发送数据的时间到来时,调用
methodToPassMyData()
您之前获得的对象:((MyContract)getTargetFragment()).methodToPassMyData(data);
。如果您onAttach()
已经将目标片段强制转换并分配给类变量(即mHost
),那么此代码将只是mHost.methodToPassMyData(data);
. - 瞧。您刚刚成功地将对话框中的数据传递回调用片段。
回答by blizzard
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. The below code in Button#onClick()
event of DatePickerFragment
passes a String and Integer.
2. 使用Bundle
inIntent
中的额外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. 使用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;
}
}
回答by Pedro Massango
A good tip is to use the ViewModel
and LiveData
approach that is the best way to go. Bellow is the code sample about how to share data between Fragment
s:
一个很好的提示是使用ViewModel
和LiveData
方法是最好的方法。Bellow 是关于如何在Fragment
s之间共享数据的代码示例:
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")
. this
is 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()