Показать диалог из фрагмента?

119

У меня есть несколько фрагментов, которые нужно показать обычному диалогу. В этих диалогах пользователь может выбрать ответ да / нет, и тогда фрагмент должен вести себя соответствующим образом.

Теперь у Fragmentкласса нет onCreateDialog()метода для переопределения, поэтому я думаю, мне нужно реализовать диалоги снаружи, в содержащем Activity. Ничего страшного, но тогда Activityнужно как-то доложить выбранный ответ фрагменту. Я, конечно, мог бы использовать здесь шаблон обратного вызова, чтобы фрагмент регистрировался в Activityклассе слушателя, и Activity сообщало бы ответ через это или что-то в этом роде.

Но для такой простой задачи, как отображение «простого» диалога «да-нет» во фрагменте, это кажется большим беспорядком. Кроме того, таким образом my Fragmentбудет менее замкнутым.

Есть ли более чистый способ сделать это?

Редактировать:

Ответ на этот вопрос не объясняет подробно, как следует использовать DialogFragments для отображения диалогов из фрагментов. Итак, AFAIK, путь:

  1. Показать фрагмент.
  2. При необходимости создайте экземпляр DialogFragment.
  3. Установите исходный фрагмент в качестве цели этого DialogFragment с помощью .setTargetFragment().
  4. Покажите DialogFragment с помощью .show () из исходного фрагмента.
  5. Когда пользователь выбирает какую-либо опцию в этом DialogFragment, уведомляет исходный фрагмент об этом выборе (например, пользователь нажал «да»), вы можете получить ссылку на исходный фрагмент с помощью .getTarget ().
  6. Закройте DialogFragment.
Жомбор Эрдёды-Надь
источник
1
Ваша техника работает, кроме случаев, когда происходит поворот экрана. Тогда я приближаюсь к силе. Любые идеи?
Weston
@Weston ознакомьтесь с первым ответом Зомбора: stackoverflow.com/questions/8235080/…
mightimaus

Ответы:

37

Вместо этого вы должны использовать DialogFragment .

МГВ
источник
9
К сожалению, этот подход немного более подробен, чем классический подход с управляемыми диалогами в предыдущих версиях Android, но теперь это предпочтительный метод. Вы можете избежать ссылки на Activityобъект целиком, используя методы putFragmentи , позволяя объекту отчитываться непосредственно вызывающему фрагменту (даже после изменения ориентации). getFragmentFragmentManagerDialogFragment
Дэйв
Что делать, если у вас есть ListFragment, который должен отображать диалоги, но вы не можете расширить их оба
марчинрам
16
Подкласс ListFragment будет использовать DialogFragments путем создания новых экземпляров, а не путем создания подкласса DialogFragment. (DialogFragment - это диалог, реализованный как фрагмент, а не как фрагмент, который может отображать диалоги.)
nmr
4
Пожалуйста, добавьте отрывок, чтобы мы могли лучше понять
Arpit Patel
35

Я должен осторожно сомневаться в ранее принятом ответе о том, что использование DialogFragment - лучший вариант. Предполагаемая (основная) цель DialogFragment, кажется, состоит в том, чтобы отображать фрагменты, которые сами по себе являются диалогами, а не отображать фрагменты, у которых есть диалоги для отображения.

Я считаю, что использование активности фрагмента в качестве посредника между диалогом и фрагментом является предпочтительным вариантом.

Марк D
источник
10
Поскольку onCreateDialogподход с управляемыми диалогами ( ) скоро станет устаревшим, я не согласен и скажу, что DialogFragmentэто действительно правильный путь.
Дэйв
4
Принятый ответ означает использование DialogFragment вместо Dialog, а не вместо ListFragment, AFAICS.
nmr
На самом деле фрагмент диалогового окна может использоваться как как диалоговое окно, так и как обычный фрагмент - см. Встраивание developer.android.com/reference/android/app/DialogFragment.html
Клайв Джефферис,
@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.
Warpzit
источник
4
Это не объясняет, как запустить его из фрагмента
akohout
@raveN, вы просто выполняете обратный вызов своей активности, которая затем запускает фрагмент.
Warpzit
@Warpzit Спасибо за исчерпывающий ответ. Я боюсь прикоснуться к FragmentManager, с которым я уже делаю нечестивые вещи ... Как я могу передать события onClick обратно во фрагмент (не диалоговый), который требует ввода пользователя? Кстати, не следует ли добавлять транзакцию в стек?
kaay 08
@kaay из активности вы можете вызвать любой общедоступный метод в данном фрагменте, которому нужен новый ввод. Оттуда должно быть довольно легко обновляться с новым контентом.
Warpzit 09
1
@RichardLeMesurier Действительно, фрагменты - это взлеты и падения.
Warpzit
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, что я сказал , поэтому я добавил скрытую кнопку, чтобы вместо этого «разрезать» пополам. Простите за взлом.

EpicPandaForce
источник
1
+1 набор ссылок setTargetFragment()правильно воссоздается системой, когда она перезапускает набор действий / фрагментов после вращения. Таким образом, ссылка будет указывать на новую цель автоматически.
Ричард Ле Мезурье
Я бы сейчас ударил себя по носу за то, что не использовал ButterKnife в тот день. Используйте ButterKnife, это сделает ваш вид намного красивее.
EpicPandaForce
И вы можете использовать Otto для отправки события из фрагмента в действие, так что вам не понадобится магия присоединения интерфейса. Пример Отто здесь: stackoverflow.com/a/28480952/2413303
EpicPandaForce
@EpicPandaForce Пожалуйста, можете ли вы добавить интерфейс / класс ShowDialog? Это единственное, чего не хватает в вашем примере.
ntrch
@ntrch это былоpublic 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();

        }
    });

Я надеюсь это поможет тебе

Сообщите мне, если возникнет путаница. :)

Джунаид Азиз
источник
0
    public static void OpenDialog (Activity activity, DialogFragment fragment){

    final FragmentManager fm = ((FragmentActivity)activity).getSupportFragmentManager();

    fragment.show(fm, "tag");
}
Эльмар Фазлагич
источник
3
пожалуйста, добавьте пояснения к своему ответу
RealCheeseLord