Обратный вызов фрагмента из диалогового фрагмента



вопрос: Как создать обратный вызов из диалогового фрагмента в другой фрагмент. В моем случае вовлеченная деятельность должна быть полностью не осведомлена о DialogFragment.



считайте, что у меня есть



public class MyFragment extends Fragment implements OnClickListener


потом в какой-то момент я может do



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 приостанавливается и возобновляется через жизненный цикл. Единственные гарантии в фрагменте-это те, которые передаются через пакет через setArguments и getArguments.



есть способ ссылаться на активность, если это должен быть слушатель:



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


но я не хочу, чтобы активность прослушивала события, мне нужен фрагмент. Действительно, это может быть любой объект Java, который реализует OnClickListener.



рассмотрим конкретный пример фрагмента, который представляет AlertDialog через DialogFragment. Он имеет Да / нет кнопки. Как я могу отправить эти нажатия кнопок обратно к фрагменту, который его создал?

689   11  

11 ответов:

вовлеченная деятельность полностью не знает о 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();
}
}

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/issues/detail?id=54520

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

можно использовать setTargetFragment on Dialog перед показом, и в диалоговом окне вы можете вызвать getTargetFragment получить ссылку.

я следовал этим простым шагам, чтобы сделать это.

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

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

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

    вот и все сделано! просто убедитесь, что вы реализовали этот интерфейс в своем фрагменте.

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

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

вы должны определить 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. объявите интерфейс в вашем DialogFragment так же, как Джеймс Маккракен объяснил выше.

  2. реализовать интерфейс в вашей деятельности (не фрагмент! Это не очень хорошая практика).

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

таким образом, он становится двухэтапным процессом : DialogFragment- > Activity и затем Activity - > Fragment

Я получаю результат на фрагмент DashboardLiveWall(вызывающий фрагмент) из фрагмента 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 {
                    }
                }
            }
        }
    }

обновление:

Я сделал библиотеку на основе моего кода gist, который генерирует эти кастинги для вас с помощью @CallbackFragment и @Callback.

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

и пример дает вам пример, который отправляет обратный вызов от фрагмента к другому фрагменту.

ответ:

Я BaseCallbackFragment аннотации @FragmentCallback. В настоящее время он расширяется Fragment, вы можете изменить его к DialogFragment и будет работать. Он проверяет реализации в следующем порядке: getTargetFragment () > getParentFragment () > context (activity).

тогда вам просто нужно расширить его и объявить ваши интерфейсы в вашем фрагменте и дать ему аннотацию, а базовый фрагмент сделает все остальное. Аннотация также имеет параметр 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

я решил это элегантным способом с помощью RxAndroid. Получите наблюдателя в конструкторе DialogFragment и suscribe для observable и нажмите значение при вызове обратного вызова. Затем в вашем фрагменте создайте внутренний класс наблюдателя, создайте экземпляр и передайте его в конструктор 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

правильный способ установки слушателя на фрагмент-это установить его, когда он прикрепленный. Проблема у меня была в том, что 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() {

            }
        });
    }
}

Comments

    Ничего не найдено.