场景如下,我有一个包含片段的ViewPager,这些片段中的每一个都有一些需要确认的操作。
我继续创建一个针对片段的DialogFragment,该片段也知道如何处理结果,但是可以在用户确认或拒绝对话框之前重新创建该片段。
我可以将lambda或某种其他形式的侦听器传递给对话框,然后在用户确认对话框时调用该lambda,但问题是如果旋转设备,则lambda将丢失,因为它不能坚持下去...
我能想到的唯一方法是为对话框分配一些UUID,然后将应用程序中的UUID连接到lambda,后者保存在应用程序内部的Map上,但这似乎是很草率的解决方案。
我尝试在线搜索现有的解决方案,例如material-dialogs librarys sample,但是大多数情况下似乎忽略了轮换对话框,但这似乎也很草率,因为对话框可能是更长的一部分流,例如
请求购买->取消->显示带有说明的对话框->如果用户愿意,请再次购买
如果我们仅忽略旋转对话框,则会丢失流的状态
答案 0 :(得分:2)
如果您传递匿名lambda / Listener,您将在轮换后丢失它,但是如果您使自己的活动实现监听器并以片段的onAttach(context)
方法进行分配,则将在重新创建活动后重新分配它。
interface FlowStepListener {
fun onFirstStepPassed()
fun onSecondStepPassed()
fun onThirdStepPassed()
}
class ParentActivity: Activity(), FlowStepListener {
override fun onFirstStepPassed() {
//control your fragments here
}
override fun onSecondStepPassed() {
//control your fragments here
}
override fun onThirdStepPassed() {
//control your fragments here
}
}
open class BaseDialogFragment : DialogFragment() {
var listener: FlowStepListener? = null
override fun onAttach(context: Context) {
super.onAttach(context)
if (context is FlowStepListener) {
listener = context
} else {
throw RuntimeException("$context must implement FlowStepListener")
}
}
override fun onDetach() {
super.onDetach()
listener = null
}
}
答案 1 :(得分:0)
请尝试使用LocalBroadcastManager
(docs),而不要使用回调来捕获对目标对象的引用。
此方法的主要优点是:
您的项目没有任何额外的依赖关系,因为LocalBroadcastManager是support-v4
和(或)AndroidX的legacy-support-v4
(您很可能已经拥有)的一部分。
无需保留任何类型的引用。
简而言之:
Intent
发送LocalBroadcastManager
和消息,然后BroadcastReceiver
来监听通过LocalBroadcastManager
传递的消息,而无需将回调传递给DialogFragment。用于从DialogFragment内部发送:
public static final String MY_ACTION = "DO SOMETHING";
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
final Button button = view.findViewById(R.id.accept);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent broadcastIntent = new Intent(MY_ACTION);
LocalBroadcastManager.getInstance(getContext()).sendBroadcast(broadcastIntent);
dismiss();
}
});
}
并用于侦听目标片段中的消息:
private final BroadcastReceiver localReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// Do whatever you need to do here
}
};
@Override
protected void onStart() {
super.onStart();
final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(MyDialogFragment.MY_ACTION);
LocalBroadcastManager.getInstance(getContext())
.registerReceiver(localReceiver, intentFilter);
}
@Override
protected void onStop() {
super.onStop();
LocalBroadcastManager.getInstance(this)
.unregisterReceiver(localReceiver);
}
答案 2 :(得分:0)
处理我发现的对话框的最佳方法是使用EventBus。您基本上是从对话框发送事件,并在“活动/片段”中拦截它们。
您可以在实例化时为对话框分配ID,并将此ID添加到事件中,以区分来自不同对话框的事件(即使对话框来自同一类型)。
通过查看the code here,您可以了解此方案的工作原理并获得其他一些想法。您还可以发现我写过的this helper class很有用(尽管要小心,因为这段代码很旧;例如,我不再保留对话框了。)
为使答案完整,我将在此处发布一些摘要。请注意,这些片段已经使用了新的FragmentFactory,因此对话框具有构造函数参数。这是相对较新的功能,因此您的代码可能不会使用它。
这可能是显示一些信息并具有一个按钮的对话框的实现。您想知道何时关闭此对话框:
public class InfoDialog extends BaseDialog {
public static final String ARG_TITLE = "ARG_TITLE";
public static final String ARG_MESSAGE = "ARG_MESSAGE";
public static final String ARG_BUTTON_CAPTION = "ARG_POSITIVE_BUTTON_CAPTION";
private final EventBus mEventBus;
private TextView mTxtTitle;
private TextView mTxtMessage;
private Button mBtnPositive;
public InfoDialog(EventBus eventBus) {
mEventBus = eventBus;
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getContext());
LayoutInflater inflater = LayoutInflater.from(getContext());
View dialogView = inflater.inflate(R.layout.dialog_info_prompt, null);
dialogBuilder.setView(dialogView);
initSubViews(dialogView);
populateSubViews();
setCancelable(true);
return dialogBuilder.create();
}
private void initSubViews(View rootView) {
mTxtTitle = (TextView) rootView.findViewById(R.id.txt_dialog_title);
mTxtMessage = (TextView) rootView.findViewById(R.id.txt_dialog_message);
mBtnPositive = (Button) rootView.findViewById(R.id.btn_dialog_positive);
// Hide "negative" button - it is used only in PromptDialog
rootView.findViewById(R.id.btn_dialog_negative).setVisibility(View.GONE);
mBtnPositive.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dismiss();
}
});
}
private void populateSubViews() {
String title = getArguments().getString(ARG_TITLE);
String message = getArguments().getString(ARG_MESSAGE);
String positiveButtonCaption = getArguments().getString(ARG_BUTTON_CAPTION);
mTxtTitle.setText(TextUtils.isEmpty(title) ? "" : title);
mTxtMessage.setText(TextUtils.isEmpty(message) ? "" : message);
mBtnPositive.setText(positiveButtonCaption);
}
@Override
public void onDismiss(DialogInterface dialog) {
super.onDismiss(dialog);
mEventBus.post(new InfoDialogDismissedEvent(getDialogTag()));
}
}
此对话框为用户提供了两个选项之间的选择:
public class PromptDialog extends BaseDialog {
public static final String ARG_TITLE = "ARG_TITLE";
public static final String ARG_MESSAGE = "ARG_MESSAGE";
public static final String ARG_POSITIVE_BUTTON_CAPTION = "ARG_POSITIVE_BUTTON_CAPTION";
public static final String ARG_NEGATIVE_BUTTON_CAPTION = "ARG_NEGATIVE_BUTTON_CAPTION";
private final EventBus mEventBus;
private TextView mTxtTitle;
private TextView mTxtMessage;
private Button mBtnPositive;
private Button mBtnNegative;
public PromptDialog(EventBus eventBus) {
mEventBus = eventBus;
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getContext());
LayoutInflater inflater = LayoutInflater.from(getContext());
View dialogView = inflater.inflate(R.layout.dialog_info_prompt, null);
dialogBuilder.setView(dialogView);
initSubViews(dialogView);
populateSubViews();
setCancelable(false);
return dialogBuilder.create();
}
private void initSubViews(View rootView) {
mTxtTitle = (TextView) rootView.findViewById(R.id.txt_dialog_title);
mTxtMessage = (TextView) rootView.findViewById(R.id.txt_dialog_message);
mBtnPositive = (Button) rootView.findViewById(R.id.btn_dialog_positive);
mBtnNegative = (Button) rootView.findViewById(R.id.btn_dialog_negative);
mBtnPositive.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dismiss();
mEventBus.post(new PromptDialogDismissedEvent(getDialogTag(), PromptDialogDismissedEvent.BUTTON_POSITIVE));
}
});
mBtnNegative.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dismiss();
mEventBus.post(new PromptDialogDismissedEvent(getDialogTag(), PromptDialogDismissedEvent.BUTTON_NEGATIVE));
}
});
}
private void populateSubViews() {
String title = getArguments().getString(ARG_TITLE);
String message = getArguments().getString(ARG_MESSAGE);
String positiveButtonCaption = getArguments().getString(ARG_POSITIVE_BUTTON_CAPTION);
String negativeButtonCaption = getArguments().getString(ARG_NEGATIVE_BUTTON_CAPTION);
mTxtTitle.setText(TextUtils.isEmpty(title) ? "" : title);
mTxtMessage.setText(TextUtils.isEmpty(message) ? "" : message);
mBtnPositive.setText(positiveButtonCaption);
mBtnNegative.setText(negativeButtonCaption);
}
@Override
public void onCancel(DialogInterface dialog) {
dismiss();
mEventBus.post(new PromptDialogDismissedEvent(getDialogTag(), PromptDialogDismissedEvent.BUTTON_NONE));
}
}
答案 3 :(得分:0)
您可以使用ViewModel
:
ViewModel类旨在以生命周期感知的方式存储和管理与UI相关的数据。 ViewModel类允许数据在配置更改(例如屏幕旋转)中幸免。
文档还介绍了Share data between fragments
部分中的片段。
...此常见的痛点可以通过使用ViewModel对象解决。这些片段可以使用其活动范围共享一个ViewModel来处理这种通信...
有趣的部分可能是:
请注意,两个片段都检索包含它们的活动。这样,当每个片段都获得ViewModelProvider时,它们会收到相同的SharedViewModel实例,该实例的作用范围是此活动。
答案 4 :(得分:0)
Listener将产生一些代码耦合,在您的情况下,为什么不使用事件总线。内部事件总线的工作方式类似于侦听器,但您不必自己管理任何事情。以下是使用事件总线的步骤。 创建一个事件对象(最好保持一个对象的干净)
public class DialogDataEvent {
String someData;
public DialogDataEvent(String data){
this.someData=data;
}
}
然后发布您的活动
EventBus.getDefault().post(new DialogDataEvent("data"));
并在“活动/片段”中接收它
@Subscribe
public void onEvent(DialogDataEvent event) {
//Do Some work here
}
别忘了在接收类中注册和注销事件总线
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (!EventBus.getDefault().isRegistered(this)) {
EventBus.getDefault().register(this);
}
}
@Override
public void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
对于 MAMA Gradle:D
implementation "org.greenrobot:eventbus:3.1.1"