Add fragment into a fragment (nested fragment) - android

I want to add a youtube fragment into my already existing fragments dynamically. The code I used is below:
// setting the Youtube Player Dynamically
private int setYoutubePlayer(String desc, View view, int prevID,
Bundle input) {
if (desc.indexOf("=") != -1) {
desc = desc.substring(desc.indexOf("=") + "=".length());
} else {
return prevID;
}
final String url = desc;
LinearLayout videoLayout = new LinearLayout(view.getContext());
videoLayout.setOrientation(LinearLayout.VERTICAL);
prevID++;
videoLayout.setId(prevID);
FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager
.beginTransaction();
fragment.setVideoId(url);
LinearLayout itemLayout = (LinearLayout) view.findViewById(R.id.items);
itemLayout.addView(videoLayout);
fragmentTransaction.add(itemLayout.getId(), fragment,
"youtube fargment " + prevID);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
return prevID;
}
I need to get the youtube fragment in the appropriate fragment. As I checked when always a new fragment get loaded (when swipe between fragments), the new inner fragment needs to be the first loaded fragment.
Any help will be gladly accepted.
SOLVED: Thank you Koby You were right. i had to replace "getActivity().getSupportFragmentManager();" with "getChildFragmentManager()". The problem was apparently the Sherlock library came with a old android v4 support library. I had to update the support library in the Sherlock. It worked for me.....

to create a nested fragment inside a fragment, you should use:
http://developer.android.com/reference/android/app/Fragment.html#getChildFragmentManager()
call the getChildFragmentManager() from the parent fragment,
and do the transaction in the parent to nest the child inside.
https://stackoverflow.com/a/13381825/1693674
tell me if you need more help in doing that...

Related

State of nested Fragments is lost when re-attached to their fragment container in bottom tab navigation

I am working on an app that has the following UI structure:
One Activity, called FoodActivity
This Activity implements bottom navigation. Hence, can show three categories of food: fruit, meat and fish
The Activity has a big Fragment container, where I attach the Fragment for fruit, meat and fish as the user interacts with the bottom tabs.
Each outer Fragment (fish, meat and fish) presents navigation between Fragments: it can show a list of fruits, and the the detail for the selected fruit.
Hence, the outer Fragments have another Fragment container in where I attach Fragments for the fruit list or fruit detail.
So, it's one main Activity, which has a big Fragment container where I swap Fragments, which in turn nest other Fragments
To switch from one outer Fragment to another using the tabs (ie: switch from fruit to meat), I perform a Fragment Transaction in the outer Fragment container:
private void switchFragment(Fragment fragment, String fragmentTag) {
final FragmentManager fm = getSupportFragmentManager();
final FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.fragment_outer, fragment, fragmentTag);
ft.commit();
}
The problem is that when switching the first-level Fragments, the state of their ChildFragmentManager is not kept.
For example, imagine that:
- I start in FuitFragment
- The FruitFragment, when created, attaches the nested FruitListFragment
- The user navigates in the nested Fragment container, from FruitListFragment to FruitDetailFragment
- The user switches to another tab
- The user switches back to the fruit tab
- The child `FragmentManager* of the FuitFragment does not automatically put FruitDetailFragment in the nested fragment container.
More specifically, when switching back to the outer Fragment:
onCreate is called, with savedInstance == null
onCreateView is called, with savedInstance == null
Hence, as the outer Fragment onCreate method is called, and I cannot tell whether or not to attach the child Fragment (as one usually does with Activities).
Moreover if I don't attach it (using an instance variable to check if I'm switching back to it) the child Fragment container will be just empty.
Experiments and notes
I have experimented that if I add the outer fragment transaction to the backstack when attaching it, when switching back to it, onCreate method is NOT called and the child FragmentManager will remember and attach the fragment it had before.
However, I cannot add it to the backstack as per Google's specifications on bottom navigation.
Setting setRetainInstace to true does not make any effect.
So, what should I do for properly restoring the state of the child FragmentManager?
Am I doing something wrong, or is it that everything around nested Fragments in Android is not well supported (or a bit broken) and I simply should not provide navigation using nested Fragments?
As Abbas pointed out, the problem was that I was using replace to switch between fragments.
I have changed to code in the Activity that puts the outer Fragment, and it works:
private void showChildFragment(int itemId) {
final FragmentManager fragmentManager = getSupportFragmentManager();
final FragmentTransaction transaction = fragmentManager.beginTransaction();
final Fragment currentFragment = getSupportFragmentManager().findFragmentById(R.id.fragment_outer);
if (currentFragment != null) {
Log.v(TAG, "Detaching item #" + currentFragment);
currentFragment.setMenuVisibility(false);
currentFragment.setUserVisibleHint(false);
transaction.detach(currentFragment);
}
// Do we already have this fragment?
final String tag = makeFragmentTag(container.getId(), itemId);
Fragment fragment = fragmentManager.findFragmentByTag(tag);
if (fragment == null) {
fragment = createFragmentForViewId(itemId);
Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
transaction.add(container.getId(), fragment, tag);
} else {
Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
transaction.attach(fragment);
}
fragment.setMenuVisibility(true);
fragment.setUserVisibleHint(true);
transaction.commitAllowingStateLoss();
fragmentManager.executePendingTransactions();
}
private Fragment createFragmentForViewId(int itemId) {
switch (itemId) {
case FRAGMENT_ID_LIBRARY:
return LibraryNavigationFragment.createInstance();
case FRAGMENT_ID_FEED:
return WebAppFragment.createInstance("feed");
case FRAGMENT_ID_SUGGEST:
return WebAppFragment.createInstance("suggest");
default:
throw new IllegalArgumentException();
}
}
This code is almost copy pasted from android.support.v4.app.FragmentPagerAdapter as ViewPagers using Fragments work like I wanted to.
With getChildFragmentManager() it won't crash.
private void switchFragment(Fragment fragment, String fragmentTag) {
final FragmentManager fm = getChildFragmentManager();
final FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.fragment_outer, fragment, fragmentTag);
ft.commit();
}

Fragment Transaction load empty view but fragment is shown after rotating device

I am building a navigation drawer as designed by the google documentation however I have an issue where the fragment is not being replaced. http://developer.android.com/training/implementing-navigation/nav-drawer.html
When the app first loads, the default fragment is loaded.
Clicking on another item on the drawer list leaves an empty view
However on rotating the device, loads the fragment chosen.
public void selectNavActivty(int position){
// TODO Changing between the different screens selection
fragment = null;
switch (position) {
case 0:
fragment = OverLay.newInstance();
break;
case 1:
fragment = Dummy.newInstance();
break;
}
if(fragment != null) {
// attach added to handle viewpager fragments
FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
trans.replace(R.id.content_frame, fragment).attach(fragment)
.addToBackStack(null);
trans.commit();
getFragmentManager().executePendingTransactions();
} else {
Log.d("Drawer Activity","Error in creating Fragment");
}
}
For navigation menu fragment transactions I use the following approach, this way the fragment will be added and placed on top.
String name = "myFragment";
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.content_frame, fragment, name)
.commit();
Look up the attach() function. It follows a different fragment lifecycle.
Also make sure that your layout files framelayout is visible.
Modify your code as below:
if(fragment != null) {
// attach added to handle viewpager fragments
FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
trans.replace(R.id.content_frame, fragment);
trans.addToBackStack(null);
trans.commit();
} else {
Log.d("Drawer Activity","Error in creating Fragment");
}
If the solution doesn't work for you, share the xml code along with your fragment code.
After adding Fragment it will be added to activity state and its view
will be added to defined Container view. But by attaching nothing will
be displayed if fragment was not already added to UI. It just attaches
to fragment manager. However if view was already added to a container
in UI and detached after that, by attaching it will be displayed again
in its container. Finally you can use attach and detach if you want to
destroy fragment View temporarily and want to display and build its
view on future without losing its state inside activity.
https://stackoverflow.com/a/18979024/3329488
My solution is to tag all the fragment with unique tag on fragment replacement. Make sure you also assign a unique tag to the default fragment during it creation. A more efficient way is to identify the fragment before you recreate the same one.
public void selectNavActivty(int position){
// TODO Changing between the different screens selection
FragmentManager fragmentManager = getSupportFragmentManager();
fragment = fragmentManager.findFragmentById(R.id.content_frame);
String fragmentTag = null;
switch (position) {
case 0:
fragmentTag = "case0Tag"; // please change to better tag name
break;
case 1:
fragmentTag = "case1Tag"; // please change to better tag name
break;
default:
Log.d("Drawer Activity","Error in creating Fragment");
return;
}
if (fragmentTag != null && !fragment.getTag().equals(fragmentTag))
fragmentManager.beginTransaction().replace(R.id.content_fragment, fragment, tag).commit();
}
In my case after rotating a device a blank fragment was shown. I understood that in an Activity.onCreate() I always called creating a blank Fragment and after that a needed one. So I changed it's behaviour to this:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (savedInstanceState == null) {
openEmptyFragment()
openAnotherFragment()
}
}
I recommend to check savedInstanceState != null before adding new fragments, as written in Why won't Fragment retain state when screen is rotated?.

Find Fragment by tag name in Container

I have a relative layout and i am adding fragment in that relative layout.
Like this
HomeFragment mHomeFragment = new HomeFragment();
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction =fragmentManager.beginTransaction();
if(mHomeFragment!=null&& mHomeFragment.isAdded()){
fragmentTransaction.show(mHomeFragment);
}else {
fragmentTransaction.add(R.id.order_container,mHomeFragment,"Search");
}
fragmentTransaction.commit();
I also have a text view where i have to display the name of fragment i added in the relative layout.
Like search in case.
How to get the name of fragment added in my relative layout suppose with
id = R.id.order_container
you can retrieve a Fragment by tag through
Fragment fragment = getFragmentManager().findFragmentByTag("yourstringtag");
then you can check if your fragment is instace of HomeFragment
if (fragment instanceof HomeFragment) {
}
Fragment fragment = getFragmentManager().findFragmentById(R.id.order_container);
String tag = (String) fragment.getTag();
If you want to get the tag of a fragment you can use the getTag() method. This probably isn't going to be the nicest way to achieve what you are looking to do.
http://developer.android.com/reference/android/app/Fragment.html#getTag()
If you have several fragments and you will change them around, consider using an adapter.
You want the name?? You can get your fragment using the ID and doing:
fragment.getClass().getName();
is it what you want?
Or you can use:
fragment.getTag();
it's returns the tag name of the fragment, if specified.
You can use findFragmentById in FragmentManager
getFragmentManager().findFragmentById(R.id.order_container)
This is late but I hope this will be helpful for someone.
To find fragment by tag we can do this:
String tag=getSupportFragmentManager().getFragments().get(index).getTag();
SecondFragment f = (SecondFragment) getSupportFragmentManager().findFragmentByTag(tag);
getSupportFragmentManager().findFragmentById(R.id.container).getClass().getName();

Dynamically replacing a fragment in ViewPager with TabsAdapter

I am using a ViewPager in conjunction with ActionBar tabs, as illustrated here. I'm using ActionBarSherlock for backward compatibility, so the parent activity extends SherlockFragmentActivity, and the children fragments extend SherlockFragment.
The solution works great for tabs with swiping, but now I want to dynamically change a fragment associated with one of the tabs.
I've read through numerous S.O. answers on this subject (for example here and here), but I've not found a clear explanation of how to dynamically change a fragment when using ViewPager + the TabsAdapter above.
Here's what I have now. When the user hits a button on an existing fragment, I try to replace the fragment from the parent activity as follows:
AnotherFragment fragment = new AnotherFragment();
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
int viewId = R.id.pager; // resource id of the ViewPager
int position = Constants.TAB_2; // tab pos of the fragment to be changed
String tag = "android:switcher:" + viewId + ":" + position;
ft.replace(viewId, fragment, tag);
ft.commit();
mTabsAdapter.notifyDataSetChanged();
This doesn't work, so I'm missing something. I also tried doing it with nested fragments using getChildFragmentManager(), but ran into a problem since this function isn't available without API 17, Android 4.2.
Thanks for any help!
I've made a little example that shows a similar behaviour. I hope you can reuse it.
I think the key is to use a fragment as a container and replace your "real" fragment using it.
For example, see how to navigate to another fragment:
btn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
FragmentTransaction trans = getFragmentManager()
.beginTransaction();
/*
* IMPORTANT: We use the "root frame" defined in
* "root_fragment.xml" as the reference to replace fragment
*/
trans.replace(R.id.root_frame, new SecondFragment());
/*
* IMPORTANT: The following lines allow us to add the fragment
* to the stack and return to it later, by pressing back
*/
trans.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
trans.addToBackStack(null);
trans.commit();
}
});
You can check the entire example here:
https://github.com/danilao/fragments-viewpager-example

android sub-fragment create before inflate

I am creating a Fragment FragParent displaying several input-fields, one of which is a sub-Fragment frag, with specialized audio controls.
The layouts of both fragments are loaded from XML in their onCreateView() methods.
Code I am using is below, but I have some questions about it:
A) is it OK to replace() the sub-Fragment if it is already returned by findFragmentByTag(), or is that an unnecessary step?
B) can the sub-Fragment be instantiated and replaced in the layout, before the parent Fragment completes the inflate() call?
<!-- language: lang-java -->
// Load sub-Fragment with audio UI.
String fragStr = "fragment_audio_str";
int fragView = R.id.frag_audio_frame_layout;
FragmentManager fm = getChildFragmentManager();
FragmentTransaction fmt = fm.beginTransaction();
Fragment frag = (Fragment) fm.findFragmentByTag(fragStr);
if (null == frag) {
frag = new Fragment();
}
fmt.replace(fragView, frag, fragStr);
fmt.commit();
return inflater.inflate(R.layout.fragment_parent, container, false);
} // END onCreateView() of FragParent
A) is it OK to replace() the sub-Fragment if it is already returned by findFragmentByTag(), or is that an unnecessary step?
No, it shouldn't be necessary to replace the fragment if it is already there. However you need to replace your child fragment if it isn't there.
B) can the sub-Fragment be instantiated and replaced in the layout, before the parent Fragment completes the inflate() call?
No, you need to inflate the parent Fragment layout and then add your child Fragment to it before returning the parent's View.
Try something like this:
View parentFragLayout = inflater.inflate(R.layout.fragment_parent, container, false);
FrameLayout childFragContainer = (FrameLayout) parentFragLayout.findViewById(R.id.frag_audio_frame_layout);
// Load sub-Fragment with audio UI.
String fragStr = "fragment_audio_str";
FragmentManager fm = getChildFragmentManager();
Fragment frag = fm.findFragmentByTag(fragStr);
// Child fragment isn't there, so add it
if (frag == null) {
frag = new Fragment();
FragmentTransaction fmt = fm.beginTransaction();
fmt.replace(childFragContainer.getId(), frag, fragStr);
fmt.commit();
}
return parentFragLayout;
Unless your Fragment is contained your XML layout (which, since you're adding it in the onCreateView, I'm assuming that it is not contained in your XML layout) you will still need to use FragmentManager to add the Fragment. However, you should use add() instead of replace(). You should only call replace() when you are actually replacing another Fragment.
While I'm not a hundred percent sure about the second question, I'm pretty sure that it's a no.

Categories

Resources