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.
Related
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 an activity that performs network operation, and on success, it needs to send data to view pager fragment.
So this is my structure.
Activity -> Home Fragment -> ViewPager [Fragment#1, Fragment#2]
Now the issue is that i can send data from Activity to Home Fragment, but i am unable to send data to View Pager Fragment, as activity does not have any direct connection with it.
To Fix this, I have taken network call from activity to fragment, and once i get response from service call, i pass data to viewPager fragment from Home Fragment.
This is working fine, and giving me desired result, but i am little confused if this is the right approach.
or there is some other recommended approach available that i can use, and pass data from activity to child fragment, or view Pager fragment, whose reference is not directly available to activity.
I would look at the new Android Architecture Components, specifically ViewModel and LiveData. https://developer.android.com/topic/libraries/architecture/viewmodel
The ViewModel class is designed to store and manage UI-related data in
a lifecycle conscious way.
You can create a shared view model that can be accessed by the activity and any fragment in that activity.
Example from the link:
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.
});
}
}
Notice that both fragments use getActivity() when getting the
ViewModelProvider. As a result, both fragments receive the same
SharedViewModel instance, which is scoped to the activity.
For your example, this avoids passing data down the hierarchy from activity to fragment then child fragment, they can all reference SharedViewModel.
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.
Need your help to figure out what is the best approach to achieve the following:
Activity -> Fragment A -> Fragment B.
When a user is on fragment B, and he clicks save. I want it to Remove/popBackStack Fragment B and return to Fragment A with the data passed from Fragment B.
Thanks in advance!
you could define interface and attribute in Fragment B,such as:
public interface OnSelectedListener {
void onSelected(String s);
}
private OnSelectedListener mOnSelectedListener;
public void setOnSelectedListener(OnSelectedListener onSelectedListener) {
mOnSelectedListener = onSelectedListener;
}
when Fragment A -> Fragment B:
new OnSelectedListener();
when click save in Fragment B
onSelectedListener.onSelected("");
other method : DataBase, SharedPreferencesHelper
Try to use
LocalBroadcastManager
Web Reference with Example -
https://gist.github.com/Antarix/8131277
The quickest solution would be to save the desired values in onPause or onStop of your fragment-B using any preferred storage method (like SharedPreferences or SQLite) and load them back in the onResume or onStart of fragment-A.
or replace the save part by introducing a stub method in your Activity which takes the Bundle of values and pass-on to the other fragment.
i need to save a custom object that i use in a fragment so it will not be lost when the screen rotates (when the app calls onDestroy and then recalls onCreate)
now the normal way to do so is to implement Parcelable interface and save it to the bundle as a Parcelable object.
that is a very tedious way of doing things.
is there a way to just pass the object along as "putObject" method?
You can save your data in fragment, retained during a configuration change like in example.
Extend the Fragment class and declare references to your stateful
objects.
public class RetainedFragment extends Fragment {
// data object we want to retain
private MyDataObject data;
// this method is only called once for this fragment
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// retain this fragment
setRetainInstance(true);
}
.. getter and setter
}
Then use FragmentManager to add the fragment to the activity.
public class MyActivity extends Activity {
private RetainedFragment dataFragment;
#Override
public void onCreate(Bundle savedInstanceState) {
..
// find the retained fragment on activity restarts
FragmentManager fm = getFragmentManager();
dataFragment = (RetainedFragment) fm.findFragmentByTag(“data”);
// create the fragment and data the first time
if (dataFragment == null) {
// add the fragment
dataFragment = new DataFragment();
fm.beginTransaction().add(dataFragment, “data”).commit();
} else {
// available dataFragment.getData()
..
// save data in onDestroy dataFragment.setData(yourData);
The best way is to implement Parcelable (Faster).
Easier (not efficient) way is to implement Serializable and add the object into the bundle as serializable.
well searching i found no official way of doing so, so here are two "hacks" i found around the problem:
1)create a class that extends Application class, in it add an arrayList of objects.
inside onSaveInstanceState call:
getApplication().getObjectArray().add(YourObject);
save the Object index inside the bundle using putInt.
extract it inside the method onReturnestoreInstanceState.
2)my less favorite one:
android automatically saves the states of its views
therefor a way to save an object will be to create a view set its visibility to none so it wont show on the screen and then add each object we want to the view using the methods:
view.setTag(key,Object); or view.setTag(Object);
now inside onReturnestoreInstanceState get the view and extract the tags.
unfortunately i couldn't find a more simple way of saving an object
hope this one helps you out (in my app i ended up using the first method)