Вопрос по android – Получите результат от DialogFragment

214

я используюDialogFragments для ряда вещей: выбор элемента из списка, ввод текста.

Каков наилучший способ вернуть значение (то есть строку или элемент из списка) обратно в вызывающее действие / фрагмент?

В настоящее время я выполняю вызывающую деятельностьDismissListener и предоставление DialogFragment ссылки на действие. Диалог тогда вызываетOnDimiss метод в действии, и действие получает результат из объекта DialogFragment. Очень грязный, и он не работает при изменении конфигурации (изменении ориентации), так как DialogFragment теряет ссылку на действие.

Спасибо за любую помощь.

Спасибо за это. Я был очень близок (как вы сказали). Помочь мне в этом связанном документе было использование onAttach () и приведение действия к слушателю. James Cross
@codinguser, @Styx - & quot; предоставление фрагменту DialogFragment ссылки на действие & quot; - эта деталь немного рискованна, так как обаActivity иDialogFragment может быть воссоздан. С использованиемActivity перешел кonAttach(Activity activity) это правильный и рекомендуемый способ. sstn
DialogFragments - это всего лишь фрагменты. Ваш подход на самом деле является рекомендуемым способом использования фрагментов для обратной связи с основным заданием.developer.android.com/guide/topics/fundamentals/… codinguser

Ваш Ответ

12   ответов
18

Ну, может быть слишком поздно, чтобы ответить, но вот что я сделал, чтобы получить результаты отDialogFragment, очень похоже на ответ @ brandon. Вот я звонюDialogFragment из фрагмента просто поместите этот код туда, где вы вызываете диалог.

FragmentManager fragmentManager = getFragmentManager();
            categoryDialog.setTargetFragment(this,1);
            categoryDialog.show(fragmentManager, "dialog");

гдеcategoryDialog мойDialogFragment который я хочу позвонить и после этого в вашей реализацииdialogfragment разместите этот код там, где вы устанавливаете свои данные намеренно. ЗначениеresultCode это 1, вы можете установить его или использовать систему, определенную.

            Intent intent = new Intent();
            intent.putExtra("listdata", stringData);
            getTargetFragment().onActivityResult(getTargetRequestCode(), resultCode, intent);
            getDialog().dismiss();

Теперь пришло время вернуться к вызывающему фрагменту и реализовать этот метод. проверьте правильность данных или успешность результата, если вы хотите сresultCode а такжеrequestCode в случае если.

 @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);        
        //do what ever you want here, and get the result from intent like below
        String myData = data.getStringExtra("listdata");
Toast.makeText(getActivity(),data.getStringExtra("listdata"),Toast.LENGTH_SHORT).show();
    }
8

Другой подход, чтобы позволитьFragment общаться до егоActivity:

1) Определите открытый интерфейс во фрагменте и создайте для него переменную

public OnFragmentInteractionListener mCallback;

public interface OnFragmentInteractionListener {
    void onFragmentInteraction(int id);
}

2) Приведите активность к переменной mCallback во фрагменте

try {
    mCallback = (OnFragmentInteractionListener) getActivity();
} catch (Exception e) {
    Log.d(TAG, e.getMessage());
}

3) Внедрите слушателя в свою деятельность

public class MainActivity extends AppCompatActivity implements DFragment.OnFragmentInteractionListener  {
     //your code here
}

4) Переопределите OnFragmentInteraction в действии

@Override
public void onFragmentInteraction(int id) {
    Log.d(TAG, "received from fragment: " + id);
}

Больше информации об этом:https://developer.android.com/training/basics/fragments/communicating.html

Спасибо, что подвели итог так хорошо. Учебник по Android для разработчиков предлагает переопределитьpublic void onAttach фрагмента и делать там активность
0

В котлине

// My DialogFragment

Класс FiltroDialogFragment: DialogFragment (), View.OnClickListener {

var listener: InterfaceCommunicator? = null

override fun onAttach(context: Context?) {
    super.onAttach(context)
    listener = context as InterfaceCommunicator
}

interface InterfaceCommunicator {
    fun sendRequest(value: String)
}   

override fun onClick(v: View) {
    when (v.id) {
        R.id.buttonOk -> {    
    //You can change value             
            listener?.sendRequest('send data')
            dismiss()
        }

    }
}

}

// Моя активность

класс MyActivity: AppCompatActivity (), FiltroDialogFragment.InterfaceCommunicator {

override fun sendRequest(value: String) {
// :)
Toast.makeText(this, value, Toast.LENGTH_LONG).show()
}

}

Я надеюсь, что это работает, если вы можете улучшить, пожалуйста, отредактируйте его.  Мой английский не очень хорош

6

Я очень удивлен, увидев, что никто не предлагал использовать местные трансляции дляDialogFragment вActivity общение! Я считаю, что это намного проще и чище, чем другие предложения. По сути, вы регистрируетесь наActivity слушать трансляции, и вы отправляете местные трансляции с вашегоDialogFragment экземпляров. Просто. Пошаговое руководство по настройке всего этого см. ВВот.

Мне нравится это решение, считается ли это хорошей или лучшей практикой в Android?
Помимо Guava EventBus еще одним вариантом являетсяGreenRobot EventBus, Я не использовал Guava EventBus, но использовал GreenRobot EventBus и имел хороший опыт работы с ним. Приятно и просто в использовании. Небольшой пример архитектуры приложения Android для использования GreenRobot EventBus см. В разделеhere.
@AdilHussain Вы правы. Я сделал предположение, что люди используют фрагменты в своей деятельности. Параметр setTargetFragment отлично подходит, если вы общаетесь с фрагментом и DialogFragment. Но вам нужно использовать метод Broadcast, когда он представляет собой Activity, вызывающую DialogFragment.
Мне очень понравился урок, спасибо за публикацию. Я хочу добавить, что в зависимости от того, что вы пытаетесь достичь, один из методов может быть более полезным, чем другой. Я бы предложил местный маршрут широковещания, если у вас есть несколько входов / результатов, отправляемых обратно в активность из диалогового окна. Я бы порекомендовал использовать маршрут onActivityResult, если ваш вывод очень простой / простой. Таким образом, чтобы ответить на вопрос о наилучшей практике, это зависит от того, чего вы пытаетесь достичь!
Ради любви Фу, не используйте трансляции! Это открывает ваше приложение для понимания проблем безопасности. Также я нахожу, что худшее приложение для Android, которое я имею, должно работать в эфире. Можете ли вы придумать лучший способ сделать код полностью непригодным для использования? Теперь мне нужно отключить широковещательные приемники вместо строки кода CLEAR? Чтобы было ясно, есть использование для вещания, но не в этом контексте! НИКОГДА в этом контексте! Это просто небрежно. Местный или нет. Все, что вам нужно - это обратные вызовы.
45

Существует гораздо более простой способ получить результат от DialogFragment.

Во-первых, в вашу активность, фрагмент или действие фрагмента необходимо добавить следующую информацию:

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    // Stuff to do, dependent on requestCode and resultCode
    if(requestCode == 1) { // 1 is an arbitrary number, can be any int
         // This is the return result of your DialogFragment
         if(resultCode == 1) { // 1 is an arbitrary number, can be any int
              // Now do what you need to do after the dialog dismisses.
         }
     }
}

requestCode это в основном ваша int-метка для DialogFragment, который вы назвали, я покажу, как это работает через секунду. ResultCode - это код, который вы отправляете обратно из DialogFragment, сообщая вашей текущей ожидающей активности, фрагменту или фрагменту активности, что произошло.

Следующий фрагмент кода, который нужно ввести, - это вызов DialogFragment. Пример здесь:

DialogFragment dialogFrag = new MyDialogFragment();
// This is the requestCode that you are sending.
dialogFrag.setTargetFragment(this, 1);     
// This is the tag, "dialog" being sent.
dialogFrag.show(getFragmentManager(), "dialog");

Этими тремя строками вы объявляете свой DialogFragment, устанавливая requestCode (который будет вызывать onActivityResult (...) после закрытия диалогового окна, и затем вы показываете диалоговое окно. Это очень просто.

Теперь в вашем DialogFragment вам нужно просто добавить одну строку непосредственно передdismiss() так что вы отправляете resultCode обратно в onActivityResult ().

getTargetFragment().onActivityResult(getTargetRequestCode(), resultCode, getActivity().getIntent());
dismiss();

Вот и все. Обратите внимание, что re, sultCode определяется какint resultCode который я установил наresultCode = 1; в этом случае.

Вот и все, теперь вы можете отправлять результат вашего DialogFragment обратно в вызывающую активность, фрагмент или активность фрагмента.

Кроме того, похоже, что эта информация была опубликована ранее, но не было приведено достаточного примера, поэтому я подумал, что предоставлю более подробную информацию.

EDIT 06.24.2016 Я прошу прощения за вводящий в заблуждение код выше. Но вы, безусловно, не можете получить результат обратно в вид деятельности в виде строки:

dialogFrag.setTargetFragment(this, 1);

ставит цельFragment и неActivity, Таким образом, чтобы сделать это, вам нужно использовать реализоватьInterfaceCommunicator.

В вашемDialogFragment установить глобальную переменную

public InterfaceCommunicator interfaceCommunicator;

Создайте публичную функцию для обработки

public interface InterfaceCommunicator {
    void sendRequestCode(int code);
}

Затем, когда вы будете готовы отправить код обратноActivity когдаDialogFragment закончив работу, вы просто добавляете строку перед вамиdismiss(); вашDialogFragment:

interfaceCommunicator.sendRequestCode(1); // the parameter is any int code you choose.

Теперь в своей деятельности вы должны сделать две вещи, во-первых, удалить одну строку кода, которая больше не применима:

dialogFrag.setTargetFragment(this, 1);  

Затем реализуйте интерфейс, и все готово. Вы можете сделать это, добавив следующую строку вimplements предложение в самом верху вашего класса:

public class MyClass Activity implements MyDialogFragment.InterfaceCommunicator

А потом@Override функция в деятельности,

@Override
public void sendRequestCode(int code) {
    // your code here
}

Вы используете этот метод интерфейса так же, какonActivityResult() метод. За исключением того, что метод интерфейса предназначен дляDialogFragments а другой дляFragments.

Я опубликую некоторые ответы для всех из вас, чтобы объяснить, как это работает.
Привет, ты говоришь, что можешь позвонитьdialogFrag.setTargetFragment(this, 1) из Activity, но этот метод получает Fragment в качестве первого аргумента, так что это не может быть приведено. Я прав ?
Этот подход не будет работать, если целью является Activity, поскольку вы не можете вызвать его onActivityResult (из вашего DialogFragment) из-за уровня защищенного доступа.
@Swift @lcompare, возможно, вам нужно переопределить onAttach (контекстный контекст) в вашем DialogFragment. Вот так:@Override public void onAttach(Context context) { super.onAttach(context); yourInterface = (YourInterface) context; }
Это просто не соответствует действительности. Я использую этот точный код в своих проектах. Вот откуда я его вытащил и он работает просто отлично. Помните, что если у вас есть проблема с уровнем защищенного доступа, вы можете изменить уровень доступа для любого метода и класса с защищенного на частный или общедоступный, если это необходимо.
1

Или поделитесь ViewModel, как показано здесь:

public class SharedViewModel extends ViewModel {
    private final MutableLiveData<Item> selected = new MutableLiveData<Item>();

    public void select(Item item) {
        selected.setValue(item);
    }

    public LiveData<Item> getSelected() {
        return selected;
    }
}


public class MasterFragment extends Fragment {
    private SharedViewModel model;
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
        itemSelector.setOnClickListener(item -> {
            model.select(item);
        });
    }
}

public class DetailFragment extends Fragment {
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
        model.getSelected().observe(this, { item ->
           // Update the UI.
        });
    }
}

https://developer.android.com/topic/libraries/architecture/viewmodel#sharing_data_between_fragments

129

Как вы видетеВот Есть очень простой способ сделать это.

В вашемDialogFragment добавить интерфейс слушателя, например:

public interface EditNameDialogListener {
    void onFinishEditDialog(String inputText);
}

Затем добавьте ссылку на этого слушателя:

private EditNameDialogListener listener;

Это будет использоваться для «активации» метод (ы) слушателя, а также для проверки, реализует ли родительский Activity / Fragment этот интерфейс (см. ниже).

вActivity/FragmentActivity/Fragment это "называется"DialogFragment просто реализуйте этот интерфейс.

В вашемDialogFragment все, что вам нужно добавить, в тот момент, когда вы хотитеDialogFragment и вернуть результат так:

listener.onFinishEditDialog(mEditText.getText().toString());
this.dismiss();

кудаmEditText.getText().toString() это то, что будет передано обратно к призваниюActivity.

Обратите внимание, что если вы хотите вернуть что-то еще, просто измените аргументы, которые принимает слушатель.

Наконец, вы должны проверить, был ли интерфейс фактически реализован родительским действием / фрагментом:

@Override
public void onAttach(Context context) {
    super.onAttach(context);
    // Verify that the host activity implements the callback interface
    try {
        // Instantiate the EditNameDialogListener so we can send events to the host
        listener = (EditNameDialogListener) context;
    } catch (ClassCastException e) {
        // The activity doesn't implement the interface, throw exception
        throw new ClassCastException(context.toString()
                + " must implement EditNameDialogListener");
    }
}

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

@LOG_TAG посмотрите на ответ @ Timmmm.setTargetFragment() а такжеgetTargetFragment() волшебство
Проблема с этим подходом состоит в том, что фрагмент не очень хорош в сохранении объекта, так как он предназначен для воссоздания, например, попытайтесь изменить ориентацию, ОС восстановит фрагмент, но экземпляр слушателя больше не будет доступен.
Это прекрасно работает сActivityиFragmentActivity's, но если вызывающий абонентFragment?
Я не уверен, что полностью вас понимаю. Но это будет работать так же, если вызывающийFragment.
Если бы абонент былFragment тогда вы можете сделать несколько вещей: 1. Передать фрагмент как ссылку (может быть, это не очень хорошая идея, потому что вы можете вызвать утечки памяти). 2. ИспользуйтеFragmentManager и позвонитьfindFragmentById или жеfindFragmentByTag он получит фрагменты, которые существуют в вашей деятельности. Надеюсь, это помогло. Хорошего дня!
0

если вы хотите отправить аргументы и получить результат из второго фрагмента, вы можете использовать Fragment.setArguments для выполнения этой задачи

static class FirstFragment extends Fragment {
    final Handler mUIHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case 101: // receive the result from SecondFragment
                Object result = msg.obj;
                // do something according to the result
                break;
            }
        };
    };

    void onStartSecondFragments() {
        Message msg = Message.obtain(mUIHandler, 101, 102, 103, new Object()); // replace Object with a Parcelable if you want to across Save/Restore
                                                                               // instance
        putParcelable(new SecondFragment(), msg).show(getFragmentManager().beginTransaction(), null);
    }
}

static class SecondFragment extends DialogFragment {
    Message mMsg; // arguments from the caller/FirstFragment

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onViewCreated(view, savedInstanceState);
        mMsg = getParcelable(this);
    }

    void onClickOK() {
        mMsg.obj = new Object(); // send the result to the caller/FirstFragment
        mMsg.sendToTarget();
    }
}

static <T extends Fragment> T putParcelable(T f, Parcelable arg) {
    if (f.getArguments() == null) {
        f.setArguments(new Bundle());
    }
    f.getArguments().putParcelable("extra_args", arg);
    return f;
}
static <T extends Parcelable> T getParcelable(Fragment f) {
    return f.getArguments().getParcelable("extra_args");
}
7

Один простой способ, который я нашел, был следующим: Реализуйте это ваш dialogFragment,

  CallingActivity callingActivity = (CallingActivity) getActivity();
  callingActivity.onUserSelectValue("insert selected value here");
  dismiss();

А затем в действии, которое вызвало фрагмент диалога, создайте соответствующую функцию как таковую:

 public void onUserSelectValue(String selectedValue) {

        // TODO add your implementation.
      Toast.makeText(getBaseContext(), ""+ selectedValue, Toast.LENGTH_LONG).show();
    }

Тост должен показать, что это работает. Работал на меня.

Я не уверен, если это правильный способ сделать это, но это, безусловно, работает :)
это хорошая практика?
Лучше использоватьInterface а не жесткая связь с конкретными классами.
-3

Просто чтобы он был одним из вариантов (так как никто еще не упомянул об этом) - вы можете использовать шину событий, такую как Otto. Итак, в диалоге вы делаете:

bus.post(new AnswerAvailableEvent(42));

И пусть ваш абонент (активность или фрагмент) подписаться на него:

@Subscribe public void answerAvailable(AnswerAvailableEvent event) {
   // TODO: React to the event somehow!
}
1

В моем случае мне нужно было передать аргументы в targetFragment. Но я получил исключение "Фрагмент уже активен". Поэтому я объявил интерфейс в моем DialogFragment, который реализовал parentFragment. Когда parentFragment запускает DialogFragment, он устанавливает себя как TargetFragment. Затем в DialogFragment я позвонил

 ((Interface)getTargetFragment()).onSomething(selectedListPosition);
236

использованиеmyDialogFragment.setTargetFragment(this, MY_REQUEST_CODE) из того места, где вы показываете диалог, а затем, когда ваш диалог закончен, из него вы можете позвонитьgetTargetFragment().onActivityResult(getTargetRequestCode(), ...)и реализоватьonActivityResult() в содержащем фрагмент.

Это похоже на злоупотреблениеonActivityResult()тем более, что это вообще не связано с деятельностью. Но я видел его рекомендованным официальными людьми Google, и, возможно, даже в демоверсиях API. Я думаю, это то, чтоg/setTargetFragment() были добавлены для.

setTargetFragment упоминает, что код запроса предназначен для использования в onActivityResult, поэтому я думаю, что можно использовать этот подход.
Интерфейс - это путь
Если цель является активностью, я бы объявил интерфейс с методом, подобным «void onActivityResult2 (int requestCode, int resultCode, Intent data)» & quot; и реализовать его с помощью деятельности. В DialogFragment просто getActivity и проверьте этот интерфейс и вызовите его соответствующим образом.
Это не хорошее решение. Он не будет работать после сохранения и восстановления состояния фрагмента диалога. LocalBroadcastManager является лучшим решением в этом случае.
Что, если целью является деятельность?

Похожие вопросы