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


119

У мене є деякі фрагменти, які потрібно показувати звичайним діалогом. У цих діалогових вікнах користувач може вибрати відповідь «так / ні», і тоді фрагмент повинен вести себе відповідно.

Тепер у Fragmentкласу немає onCreateDialog()способу перевизначення, тому я думаю, що мені доведеться реалізувати діалоги зовні, що містять Activity. Це нормально, але тоді Activityпотрібно якось повідомити про вибраний відповідь фрагменту. Я, звичайно, міг би використовувати тут шаблон зворотного виклику, тому фрагмент реєструється уActivity в класі слухача, і Діяльність повідомляє про відповідь через це чи щось подібне.

Але це здається досить великим безладом для простого завдання, як відображення у простому фрагменті "простого" так-ні. Також таким чином мійFragment би був менш самостійним.

Чи є чистіший спосіб зробити це?

Редагувати:

Відповідь на це питання насправді не пояснює детально, як слід використовувати DialogFragments для відображення діалогів із фрагментів. Тож AFAIK, шлях:

  1. Відобразити фрагмент.
  2. За потреби інстанціюйте діалоговий фрагмент.
  3. Встановіть оригінальний фрагмент як ціль цього діалогового фрагмента, за допомогою .setTargetFragment() .
  4. Покажіть DialogFragment з .show () з оригінального фрагмента.
  5. Коли користувач вибере якусь опцію цього діалогового фрагмента, сповістить про цей вибір вихідний фрагмент (наприклад, користувач натиснув "так"), ви можете отримати посилання на вихідний фрагмент за допомогою .getTarget ().
  6. Відхиліть діалоговий фрагмент.

1
Ваша техніка працює, за винятком випадків, коли відбувається обертання екрана. Тоді я зближую силу. Будь-які ідеї?
Вестон

@Weston перевірити перший відповідь на Zsombor: stackoverflow.com/questions/8235080 / ...
mightimaus

Відповіді:


37

Натомість слід використовувати DialogFragment .


9
На жаль, цей підхід є дещо докладнішим, ніж класичний підхід із керованими діалогами попередніх версій Android, але зараз це кращий метод. Ви можете уникнути посилання Activityповністю, використовуючи putFragmentі getFragmentметоди FragmentManager, дозволяючи DialogFragmentзвіту повертатися безпосередньо до викликового фрагмента (навіть після зміни орієнтації).
Дейв

Що робити, якщо у вас є ListFragment, який потребує відображення діалогів, не можна розширити їх обоє
marchinram

16
Підклас ListFragment використовує 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

@CliveJefferies Він може, але все ж не повинен показувати інші діалоги всередині себе. Я думаю, хлопці тут неправильно розуміють питання.
Марсель Бро

@anoniim, це залежить від того, як використовується фрагмент діалогу. Якщо його використовувати як звичайний фрагмент, то це добре. Якщо він використовується як діалог, то так, ви не повинні показувати інше діалогове вікно.
Клайв Джеффріс

24

Ось повний приклад діалогового фрагмента так / ні:

Клас:

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

@raveN ви просто зробите зворотний дзвінок до своєї діяльності, який потім запустить фрагмент.
Warpzit

@Warpzit Дякую за вичерпну відповідь. Я боявся торкатися FragmentManager, з яким я вже роблю нечестиві речі ... Як я можу передати події Клік назад до (недіалогового) фрагмента, який потребував введення користувача? BTW, чи не слід додавати транзакцію до backstack?
kaay

@kaay від активності ви можете викликати будь-який публічний метод у даному фрагменті, який потребує нового введення. Звідси це має бути досить простим оновленням нового вмісту.
Warpzit

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, що я сказав , тому я додав приховану кнопку, яку слід "розрізати" навпіл. Вибачте за злом.


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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.