My activity contains "FrameLayout" element only.
In its 'onCreate' method I dynamically load fragment:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
UsersListFragment fragment = new UsersListFragment();
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.add(R.id.fragment_container, fragment);
ft.commit();
}
On some event my fragment communicates back to activity and I need to instantiate another fragment and pass some data to it. I do it in the activity by implementing special interface that is in turn called by fragment. It looks like this way:
#Override
public void onUserSelected(long userId) {
UserDetailsFragment fragment = new UserDetailsFragment();
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.fragment_container, fragment);
ft.addToBackStack(null);
ft.commit();
fragment.updateUser(userId);
}
As a result, "fragment.updateUser(userId);" is called way to early (fragment is not initialized yet, it is not attached to activity).
Question: what is the proper way to pass data into the fragment?
Thanks a lot in advance. Any thoughts are greatly appreciated.
P.S. I've seen a lot of examples that show how to load fragments, but was not able to find those that show how to pass data...
That usually happens to me: once wrote a question I got a new idea to try...
Looks like one of the possible options is to pass data by setting them via 'setArguments' method... and use during 'onCreateView' fragment method...
Any better options?
Related
So, I have this problem: I am initializing a fragment - AddingTaskFragment
here's code:
Initializing AddingTaskFragment
private void initFragment()
{
// Get fragment manager
FragmentManager fm = getSupportFragmentManager();
// Begin transaction
FragmentTransaction ft = fm.beginTransaction();
// Create the Fragment and add
addingTaskFragment = new AddingTaskFragment();
ft.add(R.id.fragment_task_type_container, addingTaskFragment, "addTaskFragment");
// ft.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out);
// Commit the changes
ft.commit();
}
And it works fine
But then, I call some event, that replaces this fragment with other one(AddingScheduleFragment).
Replacing fragment
#Override
public void onScheduleTypePick()
{
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
// Create the fragment and attach book index
addingScheduleFragment = new AddingScheduleFragment();
// Replace the book list with the description
ft.replace(R.id.fragment_task_type_container, addingScheduleFragment, "addScheduleFragment");
ft.addToBackStack(null);
ft.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out);
ft.commit();
}
And when I'm poping my previous fragment(AddingTaskFragment) from the stack. All of my EditViews are gaining focus.
Returning previous fragment
#Override
public void onTaskTypePick()
{
getSupportFragmentManager().popBackStack();
}
What can be wrong? Why is this happening? Thanks for answers.
Important: When I replacing AddingTaskFragent with new object everything works great.
So, I've solved the problem by myself. Rather found the similar one.
afterTextChanged() callback being called without the text being actually changed
When the fragment was attached second time(because of .replace() method) it was restoring the previous state of the views. And then all textChangeListeners triggered.
I have a fragment in which there is a nested fragment which I add in this way:
if (home == null) {
home = new MyFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
transaction.addToBackStack(MyFragment.class.getName());
transaction.add(R.id.child_fragment, home).commit();
}
When I enter another fragment and go back the child fragment from above is not there. I checked and the instance is different from null.
UPDATE: I changed the code as suggested by Ashwin S Ashok but it's still not working.
Try using these methods:
// Use this if you don't want to retain the fragment.
protected void replaceFragmentStack(int container, Fragment fragment) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(container, fragment);
fragmentTransaction.commit();
}
// Use this if you want to add the fragments in a stack.
protected void addFragmentStack(int container, Fragment fragment, String tag) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
fragmentTransaction.add(container, fragment, tag);
fragmentTransaction.addToBackStack(tag);
fragmentTransaction.commit();
}
I would suggest you to use getChildFragmentManager() when making transactions inside a fragment. And its a bug i guess.
You can check out this thread it will help you alot Android 4.2: back stack behaviour with nested fragments
Also you need to go through The Curious Techizen's blog
Here is the link for the github project sample for same mechanism
I hope this will help you.
If you have some Fragment declared right inside your XML layout file, then looks like SetRetainInstance works expectedly.
However what if the Fragment is loaded dynamically into some container in the OnCreate callback of the main Activity? I don't see how setRetainInstance means or can do in this case.
Check out this code:
protected override void OnCreate(Bundle bundle) {
base.OnCreate(bundle);
var f = new SomeFragment();
var ft = FragmentManager.BeginTransaction();
ft.Replace(Android.Resource.Id.Content, f);
ft.Commit();
}
You can see that everytime the OnCreate is called (such as when the screen is rotated), a new SomeFragment is created and fills the main content of the Activity.
Now even if I declare a variable (field) holding reference to the instance of fragment and create it only when it's null, something like this:
SomeFragment f;
protected override void OnCreate(Bundle bundle) {
base.OnCreate(bundle);
if(f == null) {
f = new SomeFragment(this);
f.RetainInstance = true;
}
var ft = FragmentManager.BeginTransaction();
ft.Replace(Android.Resource.Id.Content, f);
ft.Commit();
}
This also does not work, everytime the Activity is created, the field f is still null initially (not retained). The only solution I found working here is declare the field f as static, like this:
static SomeFragment f;
With that I don't even need to use RetainInstance (or setRetainInstance in Java). In fact the requirement of loading Fragments dynamically is very popular, so at that point RetainInstance seems to be less helpful?
Or I missed something simple here to still take advantage of RetainInstance? The problem is use RetainInstance and dynamically load Fragments, if you have some solution or pattern to use here, please share with me, thanks!
Although I am not familiar to Xamarin, I can answer this in Java.
Instead of using
replace(int containerViewId, Fragment fragment)
You should use
replace(int containerViewId, Fragment fragment, String tag)
For example,
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
ExampleFragment fragment;
fragment = (ExampleFragment) fragmentManager.findFragmentByTag("tag");
if(fragment == null)
fragment = new ExampleFragment();
fragmentTransaction.replace(R.id.fragment_container, fragment, "tag");
fragmentTransaction.commit();
But you should only use setRetainInstance if you are running longe task at background. You may have a look at this blog post. Recreating the fragment is more suggested as it can avoid unexpected behavior (e.g. not initializing some variables).
In MainFragment at onViewCreated() I try to create nested fragment by following code ...
FragmentManager fm = getChildFragmentManager();
MainSubAFragment mainSubAFragment = MainSubAFragment.newInstance();
MainSubBFragment mainSubBFragment = MainSubBFragment.newInstance();
fm.beginTransaction()
.add(R.id.contentContainer, mainSubAFragment, MainSubAFragment.class.getName())
.add(R.id.contentContainer, mainSubBFragment, MainSubBFragment.class.getName())
.detach(mainSubBFragment)
.commit();
if (fm.executePendingTransactions()) {
showSubA();
}
After executePendingTransaction I call showSubA(), but no hope return null..
public void showSubA() {
FragmentManager fm = getChildFragmentManager();
MainSubAFragment mainSubAFragment = (MainSubAFragment)
fm.findFragmentByTag(MainSubAFragment.class.getName());
MainSubBFragment mainSubBFragment = (MainSubBFragment)
fm.findFragmentByTag(MainSubBFragment.class.getName());
fm.beginTransaction()
.attach(mainSubAFragment)
.detach(mainSubBFragment)
.commit();
}
It seem detach is a cause of problem ?? The question is .. if I call detach I cannot retreive it again # findFragmentByTag() ??
project sample: dropbox download
First Create your Fragments. It is much better to Create your Fragments in a Fragment class Instead of the Original Class name as long as you extended the Fragment class. Like this.
Fragment mainSubAFragment, mainSubBFragment;
Now we have to supply those pointers with the Fragments.
mainSubAFragment = new MainSubAFragment(); <- No need to call the New Instance which is in the Object Class.
mainSubBFragment = new MainSubBFragment();
Remember they need to extended to Fragment. Which I know you did.
Now I don't know why you have to get the ChildFragmentManager but you can just directly get the FragmentManager;
FragmentManager fm = getFragmentManger();
FragmentTransaction ft = fm.beginTransaction();
Now everything is ready, we go to the Execution part.
ft.add(R.id.contentContainer,mainSubAFragment,MainSubAFragment.class.getName());
ft.add(R.id.contentContainer,mainSubBFragment,MainSubBFragment.class.getName());
ft.commit();
NOTE: They will stack there Listeners, like a normal fragment does. So Unless declare in their XML not to stack there listeners, there onclick will be called if they are position on the same location.
After you Commit add this.
You would need to add another Fragment Transaction, why? Because the Commit() that you have made might not be finished, even though this is a line by line execution. Trust me, most of the time stack calls is fighting over one another.
To avoid this,
FragmentTransaction transaction2 = fm.beginTransaction();
fm.hide(MainSubBFragment.class.getName());
Summing things up.
Fragment mainSubAFragment, mainSubBFragment;
FragmentManager fm = getFragmentManger();
FragmentTransaction ft = fm.beginTransaction();
FragmentTransaction transaction2;
ft.add(R.id.contentContainer,mainSubAFragment,MainSubAFragment.class.getName());
ft.add(R.id.contentContainer,mainSubBFragment,MainSubBFragment.class.getName());
ft.commit();
transaction2 = fm.beginTransaction();
transaction2.hide(MainSubBFragment.class.getName());
transaction2.commit();
Make Sure that the Fragment Transactions and FragmentManagers is Global Variable. So they can be used by other Methods, instead of Creating a new Instance.
I have the following issue. My application has 2 fragments, the first one that is executed show a list of items, when you tap on an item, shows a detail fragment.
I also have a NavigationDrawerActivity, with the following methods to handle the stack of fragments.
private void changeFragment(int pos, Fragment fragment, boolean addToBackStack) {
Fragment f = getSupportFragmentManager().findFragmentByTag(String.valueOf(pos));
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction ft = fragmentManager.beginTransaction();
mCacheServices.setmFragmentTag(String.valueOf(pos));
ft.replace(R.id.container, fragment, String.valueOf(pos));
if ((addToBackStack)) {
ft.addToBackStack(STACK_KEY);
}
ft.commit();
}
The problem i am facing is when i am in the DetailFragment, and i rotete the device, it goes to the ItemList fragment, which is the firs fragment excuted by the app. What can i do to mantain on the DetailFragment when i rotate the device?
Thanks!
you can save the state of the fragment and then re-create it. Go through this for more details
http://developer.android.com/guide/topics/resources/runtime-changes.html
hope this helps.