Есть ли метод, который работает как стартовый фрагмент для результата? - PullRequest
73 голосов
/ 19 июля 2011

У меня есть фрагмент в оверлее.Это для входа в сервис.В приложении для телефона каждый из шагов, которые я хочу показать в оверлее, это их собственные экраны и действия.Процесс входа в систему состоит из 3 частей, каждая из которых имеет свою собственную активность, которая вызывается с помощью startActivityForResult ().

Теперь я хочу сделать то же самое, используя фрагменты и наложение.Наложение покажет фрагмент, соответствующий каждой деятельности.Проблема в том, что эти фрагменты размещены в действии в Honeycomb API.Я могу заставить работать первый фрагмент, но затем мне нужно запустить ActivityForResult (), что невозможно.Есть ли что-то вроде startFragmentForResult (), где я могу запустить новый фрагмент, и когда это будет сделано, вернуть результат предыдущему фрагменту?

Ответы [ 10 ]

54 голосов
/ 08 ноября 2011

Если вы хотите, есть несколько способов связи между Фрагментами,

setTargetFragment(Fragment fragment, int requestCode)
getTargetFragment()
getTargetRequestCode()

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

Fragment invoker = getTargetFragment();
if(invoker != null) {
    invoker.callPublicMethod();
}
47 голосов
/ 19 июля 2011

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

Просто помните, что у вас всегда есть связь между Фрагментом и его Активностью. Начать и закончить с результатом - это механизм связи между действиями - действия могут затем делегировать любую необходимую информацию своим фрагментам.

4 голосов
/ 12 мая 2013

Мои 2 цента.

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

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

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

@Override
public void onHiddenChanged(boolean hidden) {
    super.onHiddenChanged(hidden);
    if (hidden) return;
    Result result = Result.getAndReset();
    if (result == Result.Refresh) {
        refresh();
    }
}

public enum Result {
    Refresh;

    private static Result RESULT;

    public static void set(Result result) {
        if (RESULT == Refresh) {
            // Refresh already requested - no point in setting anything else;
            return;
        }
        RESULT = result;
    }

    public static Result getAndReset() {
        Result result = RESULT;
        RESULT = null;
        return result;
    }
}
1 голос
/ 22 июля 2016

В вашем фрагменте вы можете вызвать getActivity (). Это даст вам доступ к деятельности, которая создала фрагмент. Оттуда вы можете вызвать свой метод настройки, чтобы установить значения или передать значения.

0 голосов
/ 29 января 2019

Мы можем просто разделить одну и ту же ViewModel между фрагментами

SharedViewModel

import android.arch.lifecycle.MutableLiveData
import android.arch.lifecycle.ViewModel

class SharedViewModel : ViewModel() {

    val stringData: MutableLiveData<String> by lazy {
        MutableLiveData<String>()
    }

}

FirstFragment

import android.arch.lifecycle.Observer
import android.os.Bundle
import android.arch.lifecycle.ViewModelProviders
import android.support.v4.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup

class FirstFragment : Fragment() {

    private lateinit var sharedViewModel: SharedViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        activity?.run {
            sharedViewModel = ViewModelProviders.of(this).get(SharedViewModel::class.java)
        }
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.fragment_first, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        sharedViewModel.stringData.observe(this, Observer { dateString ->
            // get the changed String
        })

    }

}

SecondFragment

import android.arch.lifecycle.ViewModelProviders
import android.os.Bundle
import android.support.v4.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGrou

class SecondFragment : Fragment() {

    private lateinit var sharedViewModel: SharedViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        activity?.run {
            sharedViewModel = ViewModelProviders.of(this).get(SharedViewModel::class.java)
        }
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.fragment_first, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        changeString()
    }

    private fun changeString() {
        sharedViewModel.stringData.value = "Test"
    }

}
0 голосов
/ 06 января 2019

Еще одна вещь, которую вы можете сделать в зависимости от вашей архитектуры, - это использовать общую ViewModel между фрагментами. Таким образом, в моем случае FragmentA - это форма, а FragmentB - это представление выбора элемента, где пользователь может искать и выбирать элемент, сохраняя его в ViewModel. Затем, когда я возвращаюсь во FragmentA, информация уже сохраняется!

0 голосов
/ 23 октября 2018

Решение с использованием интерфейсов (и Kotlin). Основная идея состоит в том, чтобы определить интерфейс обратного вызова, внедрить его в свою деятельность, а затем вызвать его из вашего фрагмента.

Сначала создайте интерфейс ActionHandler:

interface ActionHandler {
    fun handleAction(actionCode: String, result: Int)
}

Далее, позвоните этому от вашего ребенка (в данном случае, ваш фрагмент):

companion object {
    const val FRAGMENT_A_CLOSED = "com.example.fragment_a_closed"
}

fun closeFragment() {
    try {
        (activity as ActionHandler).handleAction(FRAGMENT_A_CLOSED, 1234)
    } catch (e: ClassCastException) {
        Timber.e("Calling activity can't get callback!")
    }
    dismiss()
}

Наконец, реализуйте это в своем родителе, чтобы получить обратный вызов (в данном случае, ваша активность):

class MainActivity: ActionHandler { 
    override fun handleAction(actionCode: String, result: Int) {
        when {
            actionCode == FragmentA.FRAGMENT_A_CLOSED -> {
                doSomething(result)
            }
            actionCode == FragmentB.FRAGMENT_B_CLOSED -> {
                doSomethingElse(result)
            }
            actionCode == FragmentC.FRAGMENT_C_CLOSED -> {
                doAnotherThing(result)
            }
        }
    }
0 голосов
/ 23 августа 2018

Самый простой способ вернуть данные - это setArgument (). Например, у вас есть фрагмент1, который вызывает фрагмент2, который вызывает фрагмент3, фрагмент1 -> framgnet2 -> fargment3

Во фрагменте1

public void navigateToFragment2() {
    if (fragmentManager == null) return;

    Fragment2 fragment = Fragment2.newInstance();
    String tag = "Fragment 2 here";
    fragmentManager.beginTransaction()
            .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
            .add(R.id.flContent, fragment, tag)
            .addToBackStack(null)
            .commitAllowingStateLoss();
}

Во фрагменте 2 мы называем фрагмент 3 как обычно

private void navigateToFragment3() {
    if (fragmentManager == null) return;
    Fragment3 fragment = new Fragment3();
    fragmentManager.beginTransaction()
            .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
            .replace(R.id.flContent, fragment, tag)
            .addToBackStack(null)
            .commit();
}

Когда мы закончили нашу задачу во фрагменте 3, мы теперь называем так:

FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
if (fragmentManager == null) return;
fragmentManager.popBackStack();
Bundle bundle = new Bundle();
bundle.putString("bundle_filter", "data");
fragmentManager.findFragmentByTag("Fragment 2 here").setArguments(bundle);

Теперь во фрагменте 2 мы можем легко вызывать аргументы

@Override
public void onResume() {
    super.onResume();
    Bundle rgs = getArguments();
    if (args != null) 
        String data = rgs.getString("bundle_filter");
}
0 голосов
/ 23 августа 2018

Вы можете использовать EventBus . Это упрощает связь между операциями, фрагментами, потоками, сервисами и т. Д. Меньше кода, лучшее качество.

0 голосов
/ 06 июня 2018

Существует библиотека Android - FlowR , позволяющая запускать фрагменты для результатов.

Запуск фрагмента для результата.

Flowr.open(RequestFragment.class)
    .displayFragmentForResults(getFragmentId(), REQUEST_CODE);

Обработка результатов в вызывающем фрагменте.

@Override
protected void onFragmentResults(int requestCode, int resultCode, Bundle data) {
    super.onFragmentResults(requestCode, resultCode, data);

    if (requestCode == REQUEST_CODE) {
        if (resultCode == Activity.RESULT_OK) {
            demoTextView.setText("Result OK");
        } else {
            demoTextView.setText("Result CANCELED");
        }
    }
}

Установка результата во фрагменте.

Flowr.closeWithResults(getResultsResponse(resultCode, resultData));
...