I am Using Navigation Drawer in my app. I have one MainActivity and rest of are Fragments. So the issue is Suppose i have three fragments like A,B,C.
Now in A i have one button and i am sending data from A>B.
For example putSring("datafrom A","datafrom A");
Now in B i receive data From A.
I have one button in B,and i am sending data from B>C.
For example putSring("datafrom B","datafrom B");
Now in C i receive data From B.
Then, I have one Button in C,and sending data from C>B.
For example putSring("datafrom C","datafrom C");
So,seems like in B i am getting data from two different fragments. I tried with all using activity and it work well with startActivityforresult. but how can i manager when all are fragments.
UPDATE
Starting with Androidx Activity 1.2.0-alpha02 and Androidx Fragment 1.3.0-alpha4, the official Android developer guide recommends to use the Activity/Fragment Result APIs over the deprecated Activity.onActivityResult(int, int, Intent) and Fragment.setTargetFragment(Fragment, int) methods:
it is strongly recommended to use the Activity Result APIs introduced in AndroidX Activity 1.2.0-alpha02 and Fragment 1.3.0-alpha02.
Thus, to pass data back to fragment B from C, call setFragmentResultListener() on fragment B's FragmentManager, as shown in the following example:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Use the Kotlin extension in the fragment-ktx artifact
setFragmentResultListener("requestKey") { requestKey, 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
}
}
In fragment C, set the result on the same FragmentManager by using the same requestKey using the setFragmentResult() API. Example:
setFragmentResult("requestKey", bundleOf("bundleKey" to "result"))
More details can be found at this guide.
The below answer is deprecated
You may call setTargetFragment() when you start the Fragment C from B. Example:
FragmentC fragmentC = FragmentC.newInstance();
fragmentC.setTargetFragment(FragmentB.this, REQUEST_CODE);
getFragmentManager().beginTransaction().replace(R.id.container, fragmentC).commit();
and then when you want to pass data back to fragment B from C, you can call the following code:
getTargetFragment().onActivityResult(
getTargetRequestCode(),
Activity.RESULT_OK,
new Intent().putExtra("datafrom C", "datafrom C")
);
and get it from the onActivityResult() method in your fragment B:
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode==REQUEST_CODE && resultCode==Activity.RESULT_OK) {
String datafromC = data.getStringExtra("datafrom C");
}
}
When u are sending the data from Fragment A to Fragment B use the same boolean like below:-
FragmentA -> FragmentB
FragmentB ldf = new FragmentB ();
Bundle args = new Bundle();
args.putBoolean("BOOLEAN_VALUE",true);
ldf.setArguments(args);
getFragmentManager().beginTransaction().add(R.id.container, ldf).commit();
And when u are send data from Fragment C to Fragment B use the same BOOLEAN which is used in Fragment A to B like below-
FragmentC -> FragmentB
FragmentB ldf = new FragmentB ();
Bundle args = new Bundle();
args.putBoolean("BOOLEAN_VALUE",false);
ldf.setArguments(args);
getFragmentManager().beginTransaction().add(R.id.container, ldf).commit();
And in the last we have to check that value is recevied in FragmentB is from where like Fragment A OR FragemntC
FragmentB
Boolean getValue= getArguments().getBoolean("BOOLEAN_VALUE");
if(getValue)
{
//VALUE RECEIVED FROM FRAGMENT A
}
else
{
//VALUE RECEIVED FROM FRAGMENT C
}
Things changed a lot since 2017. The answer I post is basically an example from https://developer.android.com and it presents a good solution where your fragments, in any number, do not know anything about each other and still you are able to create a simple and elegant mechanism that can be used without much struggle.
The answer is based on ViewModels and LiveData.
Note: If you are not familiar with Architecture Components I strongly advise you to learn about it as much as you can any time you can as it will increase your production speed and decrease the number of errors in your projects.
Everything below is a citation from the following link: source (Kotlin/Java)
Share data between fragments
It's very common that two or more fragments in an activity need to
communicate with each other. Imagine a common case of master-detail
fragments, where you have a fragment in which the user selects an item
from a list and another fragment that displays the contents of the
selected item. This case is never trivial as both fragments need to
define some interface description, and the owner activity must bind
the two together. In addition, both fragments must handle the scenario
where the other fragment is not yet created or visible.
This common pain point can be addressed by using ViewModel objects.
These fragments can share a ViewModel using their activity scope to
handle this communication, as illustrated by the following sample
code:
class SharedViewModel : ViewModel() {
val selected = MutableLiveData<Item>()
fun select(item: Item) {
selected.value = item
}
}
class MasterFragment : Fragment() {
private lateinit var itemSelector: Selector
// Use the 'by activityViewModels()' Kotlin property delegate
// from the fragment-ktx artifact
private val model: SharedViewModel by activityViewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
itemSelector.setOnClickListener { item ->
// Update the UI
}
}
}
class DetailFragment : Fragment() {
// Use the 'by activityViewModels()' Kotlin property delegate
// from the fragment-ktx artifact
private val model: SharedViewModel by activityViewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
model.selected.observe(viewLifecycleOwner, Observer<Item> { item ->
// Update the UI
})
}
}
Notice that both fragments retrieve the activity that contains them.
That way, when the fragments each get the ViewModelProvider, they
receive the same SharedViewModel instance, which is scoped to this
activity.
This approach offers the following benefits:
The activity does not need to do anything, or know anything about this
communication.
Fragments don't need to know about each other besides
the SharedViewModel contract. If one of the fragments disappears, the
other one keeps working as usual.
Each fragment has its own lifecycle,
and is not affected by the lifecycle of the other one. If one fragment
replaces the other one, the UI continues to work without any problems.
Related
I have implemented NavHost(Navigation Component) in MainFragment(that contain NavHost) and it has three other fragment in it's nav (CategoryFragment,GalleryFragment and PreviewFragment)
Above three fragment are sibliing in nav_graph. I want to send a model to our parentFragment(MainFragment)
I have tried two different ways to send data to parentFragment(MainFragment)
PreviewFragment.kt on Button Click
parentFragmentManager.setFragmentResult("requestKey", bundleOf("bundleKey" to args.photo.imageUrl))
findNavController().previousBackStackEntry?.savedStateHandle?.set("requestKey", args.photo.imageUrl)
MainFragment.kt 0nViewCreated
navController.currentBackStackEntry?.savedStateHandle?.getLiveData<String>("requestKey")?.observe(
viewLifecycleOwner) { result ->
Toast.makeText(requireContext(), "$result in MainFragment", Toast.LENGTH_SHORT).show()
}
childFragmentManager.setFragmentResultListener("requestKey") { requestKey, bundle ->
// We use a String here, but any type that can be put in a Bundle is supported
val result = bundle.getString("bundleKey")
Toast.makeText(requireContext(), "$result in MainFragment", Toast.LENGTH_SHORT).show()
// Do something with the result
}
I have seen post that said FragmentManager should be same to handle fragment result api.
I have tried it parentFragmentManager , childFragmentManager and directly.
As there is another Fragment(NavHostFragment) between childFragment and ParentFragment in Navigation Component. I have found this here
In ChildFragment you have to call
parentFragment?.parentFragmentManager?.setFragmentResult
And in ParentFragment(that has NavHost)
childFragmentManager.setFragmentResultListener
You can try these:
requireActivity().supportFragmentManager.setFragmentResult
requireActivity().supportFragmentManager.setFragmentResultListener
this is what I want to achieve:
I'm in Activity A with couple options to choose from (buttons)
after clicking any of them, I want to be taken to Activity B
Activity B should contain a constant part (audio) and a Fragment with image and text, depending on the button you choose in Activity A.
Is it achievable? I tried to both startActivity and getSupportFragmentManager (etc.) as my onClick method but with no use, maybe there's another way?
In activity B create a method (static in java, companion in kotlin) to create an intent:
companion object {
const val ARG_FRAGMENT_TO_LOAD = "argFragment"
fun newIntent(context:Context, fragmentTagToLoad:String): Intent {
return Intent(context, ActivityB::class.java).apply {
putString(ARG_FRAGMENT_TO_LOAD, fragmentTagToLoad)
})
}
}
Then, in activity A you can use this intent to start activity B.
myButton?.setOnClickListener {
startActivity(ActivityB.newIntent(this, SomeFragmentToLoad.ARG_TAG))
}
Then, again in activity B you will receive this argument in the intent, so that you can handle it. It is typically done inside onCreate():
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val fragmentTagToLoad = intent.getStringExtra(ARG_FRAGMENT_TO_LOAD) ?: ""
if(fragmentToLoad.isNotEmpty() {
// Use fragment manager to load fragment into container.
}
...
}
Finally, in activity B you can use the argument to load the desired fragment using a (usually support) fragment manager.
Of course you should define ARG_TAG as a String constant inside SomeFragmentToLoad.
I have one fragment page in android, the first time I open it, I get data from bundle and show. Now, when I go to web page from this page and come back by deep-link, I lose my current data
(in other words, I have to go to the web page for payment, after that, I have to come back to the current fragment by deep link without losing current data)
Recently, I've been faced with a the same problem. I'm still a bit newbie in Android, so it's possibly not the best way to do it, but it does work.
First, in the Android Manifest, we have to add the attribute android:launchMode="singleTop" to the Activity that's hosting the Fragment. This way, when we go back to the app via deep link, the old Activity will receive the Intent instead of creating a new instance.
Then, in the code of the hosting Activity, we use the onNewIntent() method to look for the existing attached Fragment. As Hossein Kurd pointed out in his answer, this step depends on how we are managing our Fragments. In my case, I'm using a NavHostFragment, so first I have to find that and then I can search for the Fragment I want to come back to (in my case, an instance of LoginFragment):
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
if (intent != null && intent.action == Intent.ACTION_VIEW && intent.data != null) {
val navHostFragment = supportFragmentManager.fragments.find { fragment -> fragment is NavHostFragment }
navHostFragment?.let { navHostFrag ->
val loginFragment = navHostFrag.childFragmentManager.fragments.find { childFragment -> childFragment is LoginFragment }
loginFragment?.let { loginFrag ->
val bundle = bundleOf("uri" to intent.data)
loginFrag.arguments = bundle
val fragmentTransaction = loginFrag.parentFragmentManager.beginTransaction()
fragmentTransaction.show(loginFrag).commit()
}
}
}
}
This method will receive the Intent from the browser, with the corresponding data (in my case, the Uri). We wrap the data in a Bundle and pass that to the Fragment's arguments and then show() the Fragment.
Finally, in the onResume() method of the Fragment, we check if the arguments include the data we are expecting, then we can go on with our logic:
override fun onResume() {
super.onResume()
this.arguments?.get("uri")?.let { data ->
// Your logic with the data you received from the browser
}
}
You can use getInstance static method, Better way is update fragment from activity
and the solution depends on the way you call your fragments: SupportFragmentManager, NavController ...
get current fragment by view Id or ...
Kotlin:
supportFragmentManager.primaryNavigationFragment?.childFragmentManager?.fragments?.forEach { fragment ->
// fragment.onActivityResult(requestCode, resultCode, boundle)
fragment.method(param1, param2)
}
Is there any better way to send back the data to the previous fragment/parent fragment other than listener?
I have a fragment which consists of list of items. Clicking on the items will open a bottom sheet fragment. While closing the bottom sheet popup I need to pass data back to the fragment itself.
What I have done so far is created a listener and implemented it.
It really depends on what components you're using. If you are using Android Jetpack components then check out this article: LINK
You should be able to pass data back and forth similar to passing data with startActivityForResult()
Also, while you're at it please check out the official documentation too, there's a good example that will help you understand this better: LINK
Although you mention any way other than listener, but according to
documents:
Starting with Fragment 1.3.0-alpha04, each FragmentManager
implements FragmentResultOwner. This means that a FragmentManager can
act as a central store for fragment results. This change allows
components to communicate with each other by setting fragment results
and listening for those results...
Sets the FragmentResultListener for a given requestKey. Once the given
LifecycleOwner is at least in the STARTED state, any results set by
setFragmentResult using the same requestKey will be delivered to the
callback. The callback will remain active until the LifecycleOwner
reaches the DESTROYED state or clearFragmentResultListener is called
with the same requestKey.
To pass data back to fragment A from fragment B, first set a result listener on fragment A, the fragment that receives the result. Call setFragmentResultListener() on fragment A's FragmentManager, as shown in below:
in your BottomSheet Class:
btncloseBottomSheet.setOnClickListener {
val result = Bundle().apply {
// put your data in bundle
putInt("MY_KEY", 6)
}
setFragmentResult("requestCode", result)
dismiss()
}
in your previous fragment/parent fragment, you need to implement FragmentResultListener:
class PreviousFragment : FragmentResultListener {
...
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// set fragment listener
parentFragmentManager.setFragmentResultListener(
"requestCode",
viewLifecycleOwner,
this
)
}
...
// get result from other fragments by FragmentResultListener
override fun onFragmentResult(requestKey: String, result: Bundle) {
when (requestKey) {
"requestCode" -> {
val resultFromBundle = result.getInt("MY_KEY")
// Do somthing
}
}
}
}
I have multiple fragments in sliding tab layout. i want to pass data from 1st tab to other tab. please provide code. while passing data tab should swipe from 1st tab to 2nd tab. please help me. thanks in advance
Considering using ViewModel (https://developer.android.com/topic/libraries/architecture/viewmodel)
(https://developer.android.com/topic/libraries/architecture/adding-components)
First, Open the ROOT build.gradle file for your project (not the ones for your app or module) and add the google() repository as shown below:
allprojects {
repositories {
google()
jcenter()
}
}
Then, in your APP MODULE build.gradle just simply Add this Library to your Gradle then you can Start Using ViewModel in your project:
implementation "androidx.lifecycle:lifecycle-extensions:2.0.0" // Its Version May Vary
To use ViewModel :
Create ViewModel Class and Define that Data you wanted to Pass
public class MyViewModel extends ViewModel {
// Assume you wanted to Pass the Data of 'name' from 1st Tab to 2nd Tab
String name = "";
void resetData() { // Function that will Reset the Data
name= "";
}
}
Let's say 1st Tab is FragmentA, 2nd Tab is FragmentB and you wanted to Pass the 'name' Data when you Swipe from 1st Tab to 2nd Tab
Now, In FragmentA you can Set the 'name' Data of ViewModel before you pass the data to FragmentB
public class FragmentA extends Fragment {
private MyViewModel myViewModel ;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// MUST Initialize your ViewModel
myViewModel = ViewModelProviders.of(getActivity()).get(MyViewModel.class);
}
protected void onStart() {
super.onStart();
// You can Set your Value anywhere, Let Set the Value at onStart as an Example
// Set your Name Data to "Pritham Bnr"
myViewModel.name= "Pritham Bnr"
}
}
Note: Whenever you wanted to Set the 'name' Data of the ViewModel, just use code of myViewModel.name= "Value you want to Set"
Now you can get the 'name' data from the ViewModel in FragmentB When you Swipe from 1st Tab (FragmentA) to 2nd Tab (FragmentB)
public class FragmentB extends Fragment {
private MyViewModel myViewModel ;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// MUST Initialize your ViewModel
myViewModel = ViewModelProviders.of(getActivity()).get(MyViewModel.class);
}
protected void onStart() {
super.onStart();
// You can Get your Value anywhere by call "myViewModel.name", Let Get the Value at onStart as an Example
// Using Toast to Display the 'name' Data pass from FragmentA using ViewModel
Toast.makeText(getContext(), myViewModel.name, Toast.LENGTH_SHORT).show();
}
// for Example, If you want Clear the Data when Swipe Back to FragmentA, you can call resetData() function of the ViewModel
// Let say we Clear the Data when the Fragment onStop() as you Swipe back to FragmentA
// This is Optional, just an Example Telling you how to Reset the Data if you want to
protected void onStop() {
super.onStop();
// Reset Data when Screen is Being Swipe to FragmentA
// After Call this function, the ViewModel previous Data of "Pritham Bnr" will be Reset and become "" empty value.
// So FragmentA now will get "" Data from the ViewModel
myViewModel.destroyViewModelData();
}
}
Note: Whenever you wanted to Get the 'name' Data of the ViewModel, just use code of myViewModel.name
ViewModel not only can Store String, Int, Double, etc DataType, it can also store Object which is very helpful when Passing Large Amount of Data from Fragment to Fragment.
This is one of the Simple Way to Pass Data from Fragment to Fragment.
Hope this would help, Thank you.