显示片段中的对话框?


119

我有一些片段需要显示常规对话框。在这些对话框上,用户可以选择是/否答案,然后片段应相应地表现。

现在,Fragment该类没有onCreateDialog()重写的方法,所以我想我必须在contains中实现外部对话框Activity。可以,但是随后Activity需要以某种方式向片段报告所选答案。我当然可以在这里使用回调模式,因此该片段会在自己Activity的侦听器类中注册自己,并且Activity将通过该事件或类似事件来报告答案。

但这对于一个简单的任务来说似乎是一个很大的混乱,因为在片段中显示“简单”是-否对话框。而且,这样我Fragment就不会那么独立了。

有没有更清洁的方法可以做到这一点?

编辑:

这个问题的答案并没有真正详细解释如何使用DialogFragments显示来自Fragments的对话框。因此,AFAIK的方法是:

  1. 显示片段。
  2. 需要时,实例化DialogFragment。
  3. 使用将原始片段设置为该DialogFragment的目标.setTargetFragment()
  4. 使用原始片段中的.show()显示DialogFragment。
  5. 当用户在此DialogFragment中选择某个选项时,将有关此选择的信息通知原始Fragment(例如,用户单击“是”),则可以使用.getTarget()获取原始Fragment的引用。
  6. 关闭DialogFragment。

1
您的技术有效,除非发生屏幕旋转。那我就逼近了。有任何想法吗?
韦斯顿

@Weston签出Zsombor的第一个答案:stackoverflow.com/questions/8235080/…–
mayimamus

Answers:


37

您应该改用DialogFragment


9
不幸的是,这种方法比以前的Android版本的经典托管对话框方法更为冗长,但现在是首选方法。您可以Activity使用的putFragmentgetFragment方法来避免完全引用,即使FragmentManager允许在DialogFragment不改变方向的情况下也可以直接向调用片段报告。
戴夫

如果您有一个需要显示对话框的ListFragment,则不能同时扩展它们
怎么办

16
ListFragment子类将通过实例化新片段而不是通过对DialogFragment进行子类化来使用DialogFragments。(一个DialogFragment是实现为片段的对话框,而不是可能显示对话框的片段。)
nmr

4
请添加一些代码段,以便我们轻松理解
Arpit Patel

35

我必须谨慎地怀疑先前接受的答案,即使用DialogFragment是最佳选择。DialogFragment的预期(主要)目的似乎是显示属于对话框本身的片段,不是显示具有要显示的对话框的片段。

我认为,使用片段的活动在对话框和片段之间进行调解是更可取的选择。


10
由于不建议使用托管对话框(onCreateDialog)方法,因此我不同意并说这DialogFragment确实是可行的方法。
戴夫

4
接受的答案意味着使用DialogFragment而不是Dialog,而不是ListFragment,AFAICS。
nmr

实际上,对话框片段既可以用作对话框又可以用作常规片段-请参阅嵌入developer.android.com/reference/android/app/DialogFragment.html
Clive Jefferies 2014年

@CliveJefferies它可以,但是仍然不能从自身内部显示其他对话框。我认为这里的人把问题弄错了。
马塞尔兄弟

@anoniim,这取决于对话框片段的使用方式。如果将其用作常规片段,则可以。如果将其用作对话框,则可以,您不应该显示其他对话框。
克莱夫·杰佛瑞斯

24

这是一个是/否DialogFragment的完整示例:

班上:

public class SomeDialog extends DialogFragment {

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        return new AlertDialog.Builder(getActivity())
            .setTitle("Title")
            .setMessage("Sure you wanna do this!")
            .setNegativeButton(android.R.string.no, new OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    // do nothing (will close dialog)
                }
            })
            .setPositiveButton(android.R.string.yes,  new OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    // do something
                }
            })
            .create();
    }
}

要启动对话框:

        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        // Create and show the dialog.
        SomeDialog newFragment = new SomeDialog ();
        newFragment.show(ft, "dialog");

您还可以让该类实现onClickListener并使用它代替嵌入式侦听器。

回呼活动

如果要实现回调,请按以下步骤操作:

YourActivity extends Activity implements OnFragmentClickListener

@Override
public void onFragmentClick(int action, Object object) {
    switch(action) {
        case SOME_ACTION:
        //Do your action here
        break;
    }
}

回调类:

public interface OnFragmentClickListener {
    public void onFragmentClick(int action, Object object);
}

然后要从片段执行回调,您需要确保已将侦听器连接如下:

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    try {
        mListener = (OnFragmentClickListener) activity;
    } catch (ClassCastException e) {
        throw new ClassCastException(activity.toString() + " must implement listeners!");
    }
}

然后执行如下回调:

mListener.onFragmentClick(SOME_ACTION, null); // null or some important object as second parameter.

4
这并不能解释如何从片段开始
akohout 2014年

@raveN您只需对活动进行回调,然后将启动片段。
Warpzit 2014年

@Warpzit谢谢您的全面答复。我不敢接触FragmentManager,我已经在用它做不可思议的事情了。如何将onClick事件传递回需要用户输入的(非Dialog)片段?顺便说一句,难道不应该将交易添加到后台吗?
kaay 2014年

通过活动中的@kaay,您可以调用给定片段中需要新输入的任何公共方法。从那里应该很容易用新内容进行更新。
Warpzit 2014年

1
@RichardLeMesurier确实,片段是起伏不定的。
Warpzit 2014年

13

对我来说,以下是-

MyFragment:

public class MyFragment extends Fragment implements MyDialog.Callback
{
    ShowDialog activity_showDialog;

    @Override
    public void onAttach(Activity activity)
    {
        super.onAttach(activity);
        try
        {
            activity_showDialog = (ShowDialog)activity;
        }
        catch(ClassCastException e)
        {
            Log.e(this.getClass().getSimpleName(), "ShowDialog interface needs to be     implemented by Activity.", e);
            throw e;
        }
    }

    @Override
    public void onClick(View view) 
    {
        ...
        MyDialog dialog = new MyDialog();
        dialog.setTargetFragment(this, 1); //request code
        activity_showDialog.showDialog(dialog);
        ...
    }

    @Override
    public void accept()
    {
        //accept
    }

    @Override
    public void decline()
    {
        //decline
    }

    @Override
    public void cancel()
    {
        //cancel
    }

}

MyDialog:

public class MyDialog extends DialogFragment implements View.OnClickListener
{
    private EditText mEditText;
    private Button acceptButton;
    private Button rejectButton;
    private Button cancelButton;

    public static interface Callback
    {
        public void accept();
        public void decline();
        public void cancel();
    }

    public MyDialog()
    {
        // Empty constructor required for DialogFragment
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        View view = inflater.inflate(R.layout.dialogfragment, container);
        acceptButton = (Button) view.findViewById(R.id.dialogfragment_acceptbtn);
        rejectButton = (Button) view.findViewById(R.id.dialogfragment_rejectbtn);
        cancelButton = (Button) view.findViewById(R.id.dialogfragment_cancelbtn);
        acceptButton.setOnClickListener(this);
        rejectButton.setOnClickListener(this);
        cancelButton.setOnClickListener(this);
        getDialog().setTitle(R.string.dialog_title);
        return view;
    }

    @Override
    public void onClick(View v)
    {
        Callback callback = null;
        try
        {
            callback = (Callback) getTargetFragment();
        }
        catch (ClassCastException e)
        {
            Log.e(this.getClass().getSimpleName(), "Callback of this class must be implemented by target fragment!", e);
            throw e;
        }

        if (callback != null)
        {
            if (v == acceptButton)
            {   
                callback.accept();
                this.dismiss();
            }
            else if (v == rejectButton)
            {
                callback.decline();
                this.dismiss();
            }
            else if (v == cancelButton)
            {
                callback.cancel();
                this.dismiss();
            }
        }
    }
}

活动:

public class MyActivity extends ActionBarActivity implements ShowDialog
{
    ..

    @Override
    public void showDialog(DialogFragment dialogFragment)
    {
        FragmentManager fragmentManager = getSupportFragmentManager();
        dialogFragment.show(fragmentManager, "dialog");
    }
}

DialogFragment布局:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/dialogfragment_textview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="10dp"
        android:text="@string/example"/>

    <Button
        android:id="@+id/dialogfragment_acceptbtn"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:layout_centerHorizontal="true"
        android:layout_below="@+id/dialogfragment_textview"
        android:text="@string/accept"
        />

    <Button
        android:id="@+id/dialogfragment_rejectbtn"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:layout_alignLeft="@+id/dialogfragment_acceptbtn"
        android:layout_below="@+id/dialogfragment_acceptbtn"
        android:text="@string/decline" />

     <Button
        android:id="@+id/dialogfragment_cancelbtn"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:layout_marginBottom="20dp"
        android:layout_alignLeft="@+id/dialogfragment_rejectbtn"
        android:layout_below="@+id/dialogfragment_rejectbtn"
        android:text="@string/cancel" />

     <Button
        android:id="@+id/dialogfragment_heightfixhiddenbtn"
        android:layout_width="200dp"
        android:layout_height="20dp"
        android:layout_marginTop="10dp"
        android:layout_marginBottom="20dp"
        android:layout_alignLeft="@+id/dialogfragment_cancelbtn"
        android:layout_below="@+id/dialogfragment_cancelbtn"
        android:background="@android:color/transparent"
        android:enabled="false"
        android:text=" " />
</RelativeLayout>

顾名思义dialogfragment_heightfixhiddenbtn,我只是想不出一种方法来解决将底部按钮的高度切成两半的问题wrap_content,所以我添加了一个隐藏按钮,将其“切成两半”。对不起,骇客。


1
setTargetFragment()旋转后重新启动“活动/片段”集时,系统会正确地创建+1引用集。因此,引用将自动指向新目标。
理查德·勒·梅苏里尔

不过,由于我今天不使用ButterKnife,所以现在我要punch鼻涕。使用ButterKnife,它可以使您的视图处理得更漂亮。
EpicPandaForce 2014年

而且,您可以使用Otto将事件从Fragment发送到Activity,这样就不需要接口附加魔术。奥托示例在这里:stackoverflow.com/a/28480952/2413303
EpicPandaForce 2015年

@EpicPandaForce请,您可以添加“ ShowDialog”接口/类吗?这是您的示例中唯一缺少的东西。
ntrch

@ntrchpublic interface ShowDialog { void showDialog(DialogFragment dialogFragment); }
EpicPandaForce

3
 public void showAlert(){


     AlertDialog.Builder alertDialog = new AlertDialog.Builder(getActivity());
     LayoutInflater inflater = getActivity().getLayoutInflater();
     View alertDialogView = inflater.inflate(R.layout.test_dialog, null);
     alertDialog.setView(alertDialogView);

     TextView textDialog = (TextView) alertDialogView.findViewById(R.id.text_testDialogMsg);
     textDialog.setText(questionMissing);

     alertDialog.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
         public void onClick(DialogInterface dialog, int which) {
             dialog.cancel();
         }
     });
     alertDialog.show();

}

其中.test_dialog是xml自定义的


2

我本人是一个初学者,老实说我找不到我能理解或实现的令人满意的答案。

因此,这是我真正帮助我实现了想要的目标的外部链接。这非常简单,也很容易遵循。

http://www.helloandroid.com/tutorials/how-display-custom-dialog-your-android-application

我尝试使用此代码实现的目的:

我有一个承载片段的MainActivity。我希望对话框出现在布局顶部,以要求用户输入,然后相应地处理输入。 看截图

这是我片段的onCreateView的外观

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

    View rootView = inflater.inflate(R.layout.fragment_home_activity, container, false);

    Button addTransactionBtn = rootView.findViewById(R.id.addTransactionBtn);

    addTransactionBtn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Dialog dialog = new Dialog(getActivity());
            dialog.setContentView(R.layout.dialog_trans);
            dialog.setTitle("Add an Expense");
            dialog.setCancelable(true);

            dialog.show();

        }
    });

希望对您有帮助

让我知道是否有任何混乱。:)


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.