Is there a method that works like start fragment for result?

后端 未结 10 1280
孤城傲影
孤城傲影 2020-12-02 09:51

I currently have a fragment in an overlay. This is for signing in to the service. In the phone app, each of the steps I want to show in the overlay are their own screens and

相关标签:
10条回答
  • 2020-12-02 09:58

    We can simply share the same ViewModel between fragments

    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(activity).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(activity).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 讨论(0)
  • 2020-12-02 10:01

    A solution using interfaces (and Kotlin). The core idea is to define a callback interface, implement it in your activity, then call it from your fragment.

    First, create an interface ActionHandler:

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

    Next, call this from your child (in this case, your fragment):

    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()
    }
    

    Finally, implement this in your parent to receive the callback (in this case, your Activity):

    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 讨论(0)
  • 2020-12-02 10:03

    My 2 cents.

    I switch beween fragments by swapping an old fragment with a new one using hide and show/add (existing/new). So this answer is for devs who use fragments like I do.

    Then I use the onHiddenChanged method to know that the old fragment got switched to back from the new one. See code below.

    Before leaving the new fragment, I set a result in a global parameter to be queried by the old fragment. This is a very naive solution.

    @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;
        }
    }
    
    0 讨论(0)
  • 2020-12-02 10:04

    All of the Fragments live inside Activities. Starting a Fragment for a result doesn't make much sense, because the Activity that houses it always has access to it, and vice versa. If the Fragment needs to pass on a result, it can access its Activity and set its result and finish it. In the case of swapping Fragments in a single Activity, well the Activity is still accessible by both Fragments, and all your message passing can simply go through the Activity.

    Just remember that you always have communication between a Fragment and its Activity. Starting for and finishing with a result is the mechanism for communication between Activities - The Activities can then delegate any necessary information to their Fragments.

    0 讨论(0)
  • 2020-12-02 10:06

    Recently, Google has just added a new ability to FragmentManager which made the FragmentManager be able to act as a central store for fragment results. We can pass the data back and forth between Fragments easily.

    Starting fragment.

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // Use the Kotlin extension in the fragment-ktx artifact
        setResultListener("requestKey") { key, bundle ->
            // We use a String here, but any type that can be put in a Bundle is supported
            val result = bundle.getString("bundleKey")
            // Do something with the result...
        }
    }
    

    A Fragment that we want the result back.

    button.setOnClickListener {
        val result = "result"
        // Use the Kotlin extension in the fragment-ktx artifact
        setResult("requestKey", bundleOf("bundleKey" to result))
    }
    

    The snippet is taken from Google's official documents. https://developer.android.com/training/basics/fragments/pass-data-between#kotlin

    At the date of this answer written, this feature is still in alpha state. You can try it out using this dependency.

    androidx.fragment:fragment:1.3.0-alpha05
    
    0 讨论(0)
  • 2020-12-02 10:09

    There is an Android library - FlowR that allows you to start fragments for results.

    Starting a fragment for result.

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

    Handling results in the calling fragment.

    @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");
            }
        }
    }
    

    Setting the result in the Fragment.

    Flowr.closeWithResults(getResultsResponse(resultCode, resultData));
    
    0 讨论(0)
提交回复
热议问题