Зворотний виклик фрагменту з діалогового фрагмента


159

Запитання: Як створюється зворотний виклик з діалогового фрагмента до іншого фрагмента. У моєму випадку Діяльність, яка бере участь у роботі, повинна повністю не знати про DialogFragment.

Подумайте, що маю

public class MyFragment extends Fragment implements OnClickListener

Тоді в якийсь момент я міг зробити

DialogFragment dialogFrag = MyDialogFragment.newInstance(this);
dialogFrag.show(getFragmentManager, null);

Де виглядає MyDialogFragment

protected OnClickListener listener;
public static DialogFragment newInstance(OnClickListener listener) {
    DialogFragment fragment = new DialogFragment();
    fragment.listener = listener;
    return fragment;
}

Але немає гарантії, що слухач буде поруч, якщо DialogFragment призупиняється і відновлюється протягом свого життєвого циклу. Єдині гарантії у Фрагменті - це ті, які передаються через пакет із набором аргументів та getArguments.

Існує спосіб посилання на діяльність, якщо вона повинна бути слухачем:

public Dialog onCreateDialog(Bundle bundle) {
    OnClickListener listener = (OnClickListener) getActivity();
    ....
    return new AlertDialog.Builder(getActivity())
        ........
        .setAdapter(adapter, listener)
        .create();
}

Але я не хочу, щоб Активність слухала події, мені потрібен фрагмент. Дійсно, це може бути будь-який об’єкт Java, який реалізує OnClickListener.

Розглянемо конкретний приклад фрагмента, який представляє AlertDialog через DialogFragment. У ньому є кнопки Так / Ні. Як я можу повернути ці натискання кнопок назад на Фрагмент, який його створив?


Ви згадали: "Але немає гарантії, що слухач буде поруч, якщо DialogFragment призупиняється і відновиться протягом свого життєвого циклу". Я думав, що стан Fragment зруйнується під час onDestroy ()? Ви повинні мати рацію, але я просто трохи розгублений, як зараз використовувати стан Fragment. Як я відтворюю проблему, яку ви згадали, слухача немає поруч?
Шон

Я не бачу, чому ви не можете просто використовувати OnClickListener listener = (OnClickListener) getParentFragment();в DialogFragment натомість, а ваш основний фрагмент реалізує інтерфейс, як ви робили раніше.
kiruwka

Ось відповідь на непов'язаний питання , але він покаже вам , як це робиться в чистому вигляді stackoverflow.com/questions/28620026 / ...
user2288580

Відповіді:


190

Діяльність, яка бере участь, абсолютно не знає про DialogFragment.

Клас фрагментів:

public class MyFragment extends Fragment {
int mStackLevel = 0;
public static final int DIALOG_FRAGMENT = 1;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    if (savedInstanceState != null) {
        mStackLevel = savedInstanceState.getInt("level");
    }
}

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putInt("level", mStackLevel);
}

void showDialog(int type) {

    mStackLevel++;

    FragmentTransaction ft = getActivity().getFragmentManager().beginTransaction();
    Fragment prev = getActivity().getFragmentManager().findFragmentByTag("dialog");
    if (prev != null) {
        ft.remove(prev);
    }
    ft.addToBackStack(null);

    switch (type) {

        case DIALOG_FRAGMENT:

            DialogFragment dialogFrag = MyDialogFragment.newInstance(123);
            dialogFrag.setTargetFragment(this, DIALOG_FRAGMENT);
            dialogFrag.show(getFragmentManager().beginTransaction(), "dialog");

            break;
    }
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch(requestCode) {
            case DIALOG_FRAGMENT:

                if (resultCode == Activity.RESULT_OK) {
                    // After Ok code.
                } else if (resultCode == Activity.RESULT_CANCELED){
                    // After Cancel code.
                }

                break;
        }
    }
}

}

Клас DialogFragment:

public class MyDialogFragment extends DialogFragment {

public static MyDialogFragment newInstance(int num){

    MyDialogFragment dialogFragment = new MyDialogFragment();
    Bundle bundle = new Bundle();
    bundle.putInt("num", num);
    dialogFragment.setArguments(bundle);

    return dialogFragment;

}

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {

    return new AlertDialog.Builder(getActivity())
            .setTitle(R.string.ERROR)
            .setIcon(android.R.drawable.ic_dialog_alert)
            .setPositiveButton(R.string.ok_button,
                    new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int whichButton) {
                            getTargetFragment().onActivityResult(getTargetRequestCode(), Activity.RESULT_OK, getActivity().getIntent());
                        }
                    }
            )
            .setNegativeButton(R.string.cancel_button, new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int whichButton) {
                    getTargetFragment().onActivityResult(getTargetRequestCode(), Activity.RESULT_CANCELED, getActivity().getIntent());
                }
            })
            .create();
}
}

100
Я думаю, ключовим тут є setTargetFragmentі getTargetFragment. Використання onActivityResultтрохи незрозуміле. Напевно, було б краще оголосити свій власний специфічний метод у виклику фрагмента та використовувати його, замість того, щоб повторно призначити onActivityResult. Але вся його семантика на той момент.
forevermatt

2
Змінна рівня стека не використовується?
Відображати ім’я

6
чи переживе це зміна конфігурації?
Maxrunner

3
Використовували це. Примітки: рівень стеку не потрібен, щоб пережити ротацію чи сон. Замість OnActivityResult мій фрагмент реалізує DialogResultHandler # handleDialogResult (створений інтерфейс). @myCode, було б дуже корисно показати діалогове значення, яке додається до наміру, а потім прочитати всередині вашого onActivityResult. Інтенції для початківців незрозумілі.
Кріс Бетті

7
@eternalmatt, ваше заперечення цілком розумне, але я вважаю, що значення OnActivityResult () полягає в тому, що він гарантовано існує на будь-якому фрагменті, тому будь-який фрагмент може бути використаний як батьківський. Якщо ви створили свій власний інтерфейс і змусили його батьківський фрагмент реалізувати його, тоді дитина може використовуватися лише з батьками, які реалізують цей інтерфейс. Приєднання дитини до цього інтерфейсу може переслідувати вас, якщо ви почнете використовувати дитину ширше пізніше. Використання "вбудованого" інтерфейсу onActivityResult () не потребує додаткового з'єднання, тому це дозволяє трохи більше гнучкості.
Дальбергія

78

Рішення TargetFragment не здається найкращим варіантом для фрагментів діалогу, оскільки воно може створюватися IllegalStateExceptionпісля того, як програма буде знищена та відтворена. У цьому випадку FragmentManagerне вдалося знайти цільовий фрагмент, і ви отримаєте повідомлення IllegalStateExceptionз таким повідомленням:

"Фрагмент більше не існує для ключа Android: target_state: індекс 1"

Здається, Fragment#setTargetFragment()це не призначене для спілкування між дитиною та батьком Фрагмент, а скоріше для спілкування між братьями та сестрами.

Таким чином, альтернативним способом є створення подібних фрагментів діалогу, використовуючи ChildFragmentManagerбатьківський фрагмент, а не використовуючи дії FragmentManager:

dialogFragment.show(ParentFragment.this.getChildFragmentManager(), "dialog_fragment");

І, використовуючи інтерфейс, у onCreateметоді DialogFragmentви можете отримати батьківський фрагмент:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    try {
        callback = (Callback) getParentFragment();
    } catch (ClassCastException e) {
        throw new ClassCastException("Calling fragment must implement Callback interface");
    }
}

Залишилося лише зателефонувати своєму методу зворотного дзвінка після цих кроків.

Для отримання додаткової інформації про проблему ви можете ознайомитись за посиланням: https://code.google.com/p/android/isissue/detail?id=54520


2
Це також працює з програмою onAttach (контекст контексту), доданою в api 23.
Санта Текладо

1
ЦЕ має бути прийнятою відповіддю. Поточна прийнята відповідь є баггі, і фрагменти не повинні використовуватись так.
NecipAllef

3
@AhmadFadli Проблема тут полягає в тому, щоб знайти правильний контекст (батьківський) для спілкування між фрагментами. Якщо ви будете використовувати фрагмент діалогу як дочірню діяльність, то не повинно бути ніякої плутанини. FragmentManager Activity і getActivity () для отримання зворотного дзвінка достатньо.
Огуз Оскан

1
Це має бути прийнятою відповіддю. Це чітко пояснено і детально, це не просто код, накинутий на людей.
Вінс

1
@lukecross ParentFragment - це фрагмент, який створює DialogFragment (той, який викликає show ()), але схоже, що childFragmentManager не пережив конфігурацію / обертання екрана ...
iwat0qs

34

Я дотримувався цих простих кроків, щоб зробити цей матеріал.

  1. Створіть інтерфейс, як DialogFragmentCallbackInterfaceза допомогою подібного методу callBackMethod(Object data). Який би ви закликали передати дані.
  2. Тепер ви можете реалізувати DialogFragmentCallbackInterfaceінтерфейс, як у вашому фрагментіMyFragment implements DialogFragmentCallbackInterface
  3. Під час DialogFragmentстворення встановіть ваш фрагмент виклику MyFragmentяк цільовий фрагмент, який створив DialogFragmentвикористання myDialogFragment.setTargetFragment(this, 0)check setTargetFragment (Фрагмент фрагмента, int requestCode)

    MyDialogFragment dialogFrag = new MyDialogFragment();
    dialogFrag.setTargetFragment(this, 1); 
  4. Отримайте ваш цільовий фрагмент об'єкта у себе DialogFragment, зателефонувавши getTargetFragment()та передавши його. DialogFragmentCallbackInterfaceТепер ви можете використовувати цей інтерфейс для надсилання даних у ваш фрагмент.

    DialogFragmentCallbackInterface callback = 
               (DialogFragmentCallbackInterface) getTargetFragment();
    callback.callBackMethod(Object data);

    Ось і все зроблено! просто переконайтеся, що ви реалізували цей інтерфейс у своєму фрагменті.


4
Це має бути найкращою відповіддю. Чудова відповідь.
Пані Саєдул Карим

Також не забудьте використовувати один і той же менеджер фрагментів як для вихідних, так і для цільових фрагментів, інакше getTargetFragment не працюватиме. Отже, якщо ви використовуєте childFragmentManager, він не працюватиме, оскільки вихідний фрагмент не здійснює дочірній менеджер фрагментів. Найкраще розглядати ці 2 фрагменти як фрагменти рідних братів, а не фрагменти батьків / дітей.
Туптен

Відверто кажучи, краще використовувати лише шаблон цільового фрагмента при спілкуванні між двома фрагментами братів. Не маючи слухача, ви уникаєте випадкового протікання фрагмента1 у фрагменті2. Під час використання цільового фрагмента не використовуйте слухача / зворотного дзвінка. Використовувати лише onActivityResult(request code, resultcode, intent)для повернення результату до фрагменту1. Із fragment1, setTargetFragment()і from fragment2 використовуйте getTargetFragment(). Якщо ви використовуєте батьківський / дочірній фрагмент для фрагменту, або активність до фрагменту, ви можете використовувати слухач або зворотний виклик, оскільки немає жодної небезпеки протікання батьківського фрагмента в дочірньому фрагменті.
Туптен

@Thupten, коли ви говорите "утечка", ви маєте на увазі витік пам'яті або "деталі впровадження протікання"? Я не думаю, що відповідь Vijay не буде просочувати пам'ять більше, ніж використовувати onActivityResulty для повернення значення. Обидва шаблони будуть зберігати посилання на цільовий фрагмент. Якщо ви маєте на увазі деталі щодо впровадження течі, то я думаю, що його шаблон навіть кращий, ніж onActivityResult. Метод зворотного виклику явний (якщо його правильно вказано). Якщо все, що ви повернетесь, добре і скасовано, перший фрагмент повинен інтерпретувати, що це означає.
tir38

34

Можливо, трохи пізно, але може допомогти іншим людям із таким же питанням, як я.

Ви можете використовувати setTargetFragmentна Dialogперед показом, і в діалозі ви можете зателефонувати , getTargetFragmentщоб отримати посилання.


Ось відповідь на інше питання , але це також відноситься і до вашого запитання і чисте рішення: stackoverflow.com/questions/28620026 / ...
user2288580

IllegalStateException для мене
luke cross

19

У посібнику " Спілкування з іншими фрагментами" йдеться про те, що фрагменти повинні спілкуватися через пов'язану діяльність .

Часто вам потрібно, щоб один фрагмент спілкувався з іншим, наприклад, щоб змінити вміст на основі події користувача. Весь зв'язок між фрагментами здійснюється через пов'язану діяльність. Два фрагменти ніколи не повинні спілкуватися безпосередньо.


1
що стосується внутрішніх фрагментів, тобто як повинен фрагмент у межах іншого фрагменту повідомляти фрагмент хоста
Раві

@Ravi: Кожен фрагмент повинен спілкуватися з діяльністю, спільною для всіх фрагментів, викликаючи getActivity () .
Едвард Брей

1
@Chris: Якщо фрагментам потрібне постійне спілкування, визначте інтерфейс для кожного відповідного фрагмента для реалізації. Завдання діяльності обмежується наданням фрагментів інтерфейсних покажчиків їх фрагментам. Після цього фрагменти можуть безпечно спілкуватися «безпосередньо» через інтерфейси.
Едвард Брей

3
Я думаю, що в міру використання фрагментів розширюється початкова ідея не використовувати прямий фрагмент зв'язку. Наприклад, у навігаційному ящику кожен найближчий дочірній фрагмент активності грубо виступає як діяльність. Таким чином, наявність фрагмента, такого як діафрагмент, спілкується через діяльність, шкодить читабельності / гнучкості ІМО. Насправді, здається, не існує жодного приємного способу інкапсуляції діалогового фрагмента, щоб він міг працювати з обома видами діяльності та фрагментами шляхом багаторазового використання.
Сем

16
Я знаю, що це старе, але у випадку, якщо хтось інший приходить сюди, я відчуваю, що випадок, про який йдеться в цьому документі, не застосовується, коли один фрагмент "володіє" логікою, яка використовується для визначення створення та управління діалоговим фрагментом. Це дивно, щоб створити купу зв'язків від фрагмента до діяльності, коли діяльність навіть не впевнена, чому створюється діалог або за яких умов його слід відхилити. Крім того, що DialogFragment дуже простий і існує лише для повідомлення користувача та потенційного отримання відповіді.
Кріс

12

Ви повинні визначити interfaceсвій клас фрагментів і реалізувати цей інтерфейс у його батьківській діяльності. Деталі викладені тут http://developer.android.com/guide/components/fragments.html#EventCallbacks . Код буде схожий на:

Фрагмент:

public static class FragmentA extends DialogFragment {

    OnArticleSelectedListener mListener;

    // Container Activity must implement this interface
    public interface OnArticleSelectedListener {
        public void onArticleSelected(Uri articleUri);
    }

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

Діяльність:

public class MyActivity extends Activity implements OnArticleSelectedListener{

    ...
    @Override
    public void onArticleSelected(Uri articleUri){

    }
    ...
}

1
Думаю, ви занадто швидко перекинули документи. Обидва ці сегменти коду є, FragmentAі він припускає, що діяльність - це OnArticleSelectedListener, а не Фрагмент, який його запустив.
Вірматматт

2
Я б розглядав те, що ви намагаєтеся зробити поганою практикою. Вказівки щодо Android рекомендують, щоб вся комунікація по фрагментах відбувалася через діяльність (на developer.android.com/training/basics/fragments/… ). Якщо ви дійсно хочете, щоб це все було вирішено всередині MyFragment, можливо, ви захочете перейти на використання звичайногоAlertDialog
Джеймс Маккракен

1
Я думаю, що занепокоєння того, щоб фрагменти спілкувалися безпосередньо один з одним, полягає в тому, що в деяких макетах не всі фрагменти можуть бути завантажені, і як показано в прикладі, можливо, буде потрібно переключити фрагмент. Я не думаю, що ця занепокоєння є дійсною, коли йдеться про запуск діалогового фрагмента з фрагмента.

Я це реалізував для своєї діяльності. Запитання: чи можна розширити це рішення таким чином, щоб фрагмент міг створити цей діалог?
Білл Мот

1
Це хороша практика з архітектурної точки зору, і як така повинна бути прийнята відповідь. Використання onActivityResult призводить до архітектури спагетті
Bruno Carrier

4

Правильний спосіб налаштування слухача на фрагмент - це встановлення його, коли він додається . Проблема, яку я мав, полягала в тому, що onAttachFragment () ніколи не викликався. Після деякого розслідування я зрозумів, що використовую getFragmentManager замість getChildFragmentManager

Ось як я це роблю:

MyDialogFragment dialogFragment = MyDialogFragment.newInstance("title", "body");
dialogFragment.show(getChildFragmentManager(), "SOME_DIALOG");

Приєднайте його до onAttachFragment:

@Override
public void onAttachFragment(Fragment childFragment) {
    super.onAttachFragment(childFragment);

    if (childFragment instanceof MyDialogFragment) {
        MyDialogFragment dialog = (MyDialogFragment) childFragment;
        dialog.setListener(new MyDialogFragment.Listener() {
            @Override
            public void buttonClicked() {

            }
        });
    }
}

3

Відповідно до офіційної документації:

Фрагмент # setTargetFragment

Необов’язкова ціль для цього фрагмента. Це може бути використано, наприклад, якщо цей фрагмент починається іншим, і коли він готовий, хоче повернути результат першому. Цільовий набір тут зберігається в усіх примірниках через FragmentManager # putFragment.

Фрагмент # getTargetFragment

Повернути цільовий фрагмент, встановлений setTargetFragment (Fragment, int).

Тож ви можете це зробити:

// In your fragment

public class MyFragment extends Fragment implements OnClickListener {
    private void showDialog() {
        DialogFragment dialogFrag = MyDialogFragment.newInstance(this);
        // Add this
        dialogFrag.setTargetFragment(this, 0);
        dialogFrag.show(getFragmentManager, null);
    }
    ...
}

// then

public class MyialogFragment extends DialogFragment {
    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        // Then get it
        Fragment fragment = getTargetFragment();
        if (fragment instanceof OnClickListener) {
            listener = (OnClickListener) fragment;
        } else {
            throw new RuntimeException("you must implement OnClickListener");
        }
    }
    ...
}

Ви можете пояснити своє?
Yilmaz

У цьому випадку нам потрібно передати посилання "MyFragment" на "MyialogFragment", а "Fragment" надає метод для цього. Я додав опис офіційного документа, він повинен говорити чіткіше, ніж я.
SUPERYAO

2

Я зіткнувся з подібною проблемою. Я знайшов рішення:

  1. Заявіть про інтерфейс у вашому DialogFragment так, як пояснив вище Джеймс Маккракен.

  2. Впроваджуйте інтерфейс у своїй діяльності (не фрагмент! Це не є хорошою практикою).

  3. З методу зворотного дзвінка у вашій діяльності викличте потрібну функцію публіки у своєму фрагменті, яка виконує ту роботу, яку ви хочете виконати.

Таким чином, він перетворюється на двоетапний процес: DialogFragment -> Activity, а потім Activity -> Fragment


1

Я отримую результат для Fragment DashboardLiveWall (виклик фрагмента) від Fragment LiveWallFilterFragment (отримання фрагмента) Як це ...

 LiveWallFilterFragment filterFragment = LiveWallFilterFragment.newInstance(DashboardLiveWall.this ,"");

 getActivity().getSupportFragmentManager().beginTransaction(). 
 add(R.id.frame_container, filterFragment).addToBackStack("").commit();

де

public static LiveWallFilterFragment newInstance(Fragment targetFragment,String anyDummyData) {
        LiveWallFilterFragment fragment = new LiveWallFilterFragment();
        Bundle args = new Bundle();
        args.putString("dummyKey",anyDummyData);
        fragment.setArguments(args);

        if(targetFragment != null)
            fragment.setTargetFragment(targetFragment, KeyConst.LIVE_WALL_FILTER_RESULT);
        return fragment;
    }

setResult повертається до виклику фрагмента типу

private void setResult(boolean flag) {
        if (getTargetFragment() != null) {
            Bundle bundle = new Bundle();
            bundle.putBoolean("isWorkDone", flag);
            Intent mIntent = new Intent();
            mIntent.putExtras(bundle);
            getTargetFragment().onActivityResult(getTargetRequestCode(),
                    Activity.RESULT_OK, mIntent);
        }
    }

onActivityResult

@Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (resultCode == Activity.RESULT_OK) {
            if (requestCode == KeyConst.LIVE_WALL_FILTER_RESULT) {

                Bundle bundle = data.getExtras();
                if (bundle != null) {

                    boolean isReset = bundle.getBoolean("isWorkDone");
                    if (isReset) {

                    } else {
                    }
                }
            }
        }
    }

1

Оновлено:

Я створив бібліотеку на основі свого коду суті, який створює кастинг для вас за допомогою @CallbackFragmentта @Callback.

https://github.com/zeroarst/callbackfragment .

І приклад наведемо приклад, який надсилає зворотний виклик з фрагмента на інший фрагмент.

Стара відповідь:

Я зробив BaseCallbackFragmentанотацію @FragmentCallback. Зараз він поширюється Fragment, ви можете змінити його DialogFragmentі буде працювати. Він перевіряє виконання у такому порядку: getTargetFragment ()> getParentFragment ()> контекст (активність).

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

public class EchoFragment extends BaseCallbackFragment {

    private FragmentInteractionListener mListener;

    @FragmentCallback
    public interface FragmentInteractionListener {
        void onEcho(EchoFragment fragment, String echo);
    }
}

https://gist.github.com/zeroarst/3b3f32092d58698a4568cdb0919c9a93


1

Котлінські хлопці ось ми їдемо!

Таким чином, проблема у тому, що ми створили діяльність, MainActivityна цій діяльності ми створили фрагмент, FragmentAі тепер ми хочемо створити фрагмент діалогу поверх його FragmentAвиклику FragmentB. Як ми можемо отримувати результати FragmentBназад, FragmentAне проходячи MainActivity?

Примітка:

  1. FragmentA- це дочірній фрагмент MainActivity. Для управління створеними фрагментами FragmentAми будемо використовувати, childFragmentManagerщо це робить!
  2. FragmentAце батьківський фрагмент FragmentB, для доступу FragmentAзсередини FragmentBми будемо використовувати parenFragment.

Сказавши це, всередині FragmentA,

class FragmentA : Fragment(), UpdateNameListener {
    override fun onSave(name: String) {
        toast("Running save with $name")
    }

    // call this function somewhere in a clickListener perhaps
    private fun startUpdateNameDialog() {
        FragmentB().show(childFragmentManager, "started name dialog")
    }
}

Ось фрагмент діалогу FragmentB.

class FragmentB : DialogFragment() {

    private lateinit var listener: UpdateNameListener

    override fun onAttach(context: Context) {
        super.onAttach(context)
        try {
            listener = parentFragment as UpdateNameListener
        } catch (e: ClassCastException) {
            throw ClassCastException("$context must implement UpdateNameListener")
        }
    }

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        return activity?.let {
            val builder = AlertDialog.Builder(it)
            val binding = UpdateNameDialogFragmentBinding.inflate(LayoutInflater.from(context))
            binding.btnSave.setOnClickListener {
                val name = binding.name.text.toString()
                listener.onSave(name)
                dismiss()
            }
            builder.setView(binding.root)
            return builder.create()
        } ?: throw IllegalStateException("Activity can not be null")
    }
}

Ось інтерфейс, що з'єднує два.

interface UpdateNameListener {
    fun onSave(name: String)
}

Це воно.


1
Я дотримувався цього документа: developer.android.com/guide/topics/ui/dialogs, і він не працював. Дуже дякую. Я сподіваюся, що ця структура батьківського фрагмента працює так, як очікували кожного разу :)
UmutTekin

1
не забудьте налаштувати слухача на null всерединіDetach :)
BekaBot

@BekaBot Дякую за коментар. Я провів деякі дослідження, і, здається, не потрібно закривати слухачів. stackoverflow.com/a/37031951/10030693
Ssenyonjo

0

Я вирішив це елегантно з RxAndroid. Прийміть спостерігача в конструкторі DialogFragment і піддайте спостережуваному та натисніть на значення, коли викликається зворотний виклик. Потім у своєму фрагменті створіть внутрішній клас спостерігача, створіть екземпляр і передайте його в конструктор DialogFragment. Я використовував WeakReference у спостерігачі, щоб уникнути витоку пам'яті. Ось код:

BaseDialogFragment.java

import java.lang.ref.WeakReference;

import io.reactivex.Observer;

public class BaseDialogFragment<O> extends DialogFragment {

    protected WeakReference<Observer<O>> observerRef;

    protected BaseDialogFragment(Observer<O> observer) {
        this.observerRef = new WeakReference<>(observer);
   }

    protected Observer<O> getObserver() {
    return observerRef.get();
    }
}

DatePickerFragment.java

public class DatePickerFragment extends BaseDialogFragment<Integer>
    implements DatePickerDialog.OnDateSetListener {


public DatePickerFragment(Observer<Integer> observer) {
    super(observer);
}

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    // Use the current date as the default date in the picker
    final Calendar c = Calendar.getInstance();
    int year = c.get(Calendar.YEAR);
    int month = c.get(Calendar.MONTH);
    int day = c.get(Calendar.DAY_OF_MONTH);

    // Create a new instance of DatePickerDialog and return it
    return new DatePickerDialog(getActivity(), this, year, month, day);
}

@Override
public void onDateSet(DatePicker view, int year, int month, int dayOfMonth) {
        if (getObserver() != null) {
            Observable.just(month).subscribe(getObserver());
        }
    }
}

MyFragment.java

//Show the dialog fragment when the button is clicked
@OnClick(R.id.btn_date)
void onDateClick() {
    DialogFragment newFragment = new DatePickerFragment(new OnDateSelectedObserver());
    newFragment.show(getFragmentManager(), "datePicker");
}
 //Observer inner class
 private class OnDateSelectedObserver implements Observer<Integer> {

    @Override
    public void onSubscribe(Disposable d) {

    }

    @Override
    public void onNext(Integer integer) {
       //Here you invoke the logic

    }

    @Override
    public void onError(Throwable e) {

    }

    @Override
    public void onComplete() {

    }
}

Ви можете побачити вихідний код тут: https://github.com/andresuarezz26/carpoolingapp


1
Найцікавіше в Android - це те, що називається життєвим циклом. Базовий фрагмент або діалоговий фрагмент повинні бути в змозі зберегти стан (і їх зв'язок) протягом подій життєвого циклу. Зворотні виклики або спостерігачі не можуть бути серіалізовані, і, отже, тут є одна і та ж проблема.
GDanger
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.