Getting reference to nested fragment from FragmentTabHost - android

In my application, I use an Activity which holds one Fragment with FragmentTabHost and hence all its tabs are nested Fragments.
Inside an Activity which holds a Fragment with its nested Fragment, we may get a reference to attached one using onAttachedFragment().
But how to get a reference to nested Fragment from FragmentTabHost?

Well, exploring the source code of FragmentTabHost I've found that when it adds a fragment tab, it assignes a tag of TabSpec to nested Fragment.
So to get the reference to this Fragment we should call
getChildFragmentManager().findFragmentByTag(tabSpecTag)

I was trying this for a while, but I was getting null returned from the FragmentManager because I was trying to access the manager in onCreateView() immediately after adding.
Here is a good explanation on what happened
It's also important to note that Fragment tabs that have not yet been selected don't exist yet in the FragmentManager, and so will return null as well. I got around this by calling mTabHost.setCurrentTab(index) before trying get to the Fragment with the FragmentManager. It's not very clean, but it works.

Above solutions are also working but I have one more easy solution,
#Override
public void onTabChanged(final String tabId) {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
mFragment = getChildFragmentManager().findFragmentByTag("Tagname");
}
},1000);
}
Here you have to implement FragmentTabHost.onTabChangeListener
We have kept a second delay in fetching fragment from the childFragmentManager.
Note : You need to cast mFragment which fragment you have used.

I found a solution that I like a little better because it doesn't involving executing code with a delay (which is always iffy given android hardware fragmentation and different processor speeds).
In your onTabChanged() method, before you try to find the fragment, call executePendingTransactions() on the fragment manager associated with your tabHost. It seems there are some places in the FragmentTabHost source code where they should be calling executePendingTransactions() but fail to do so.
This works every time the tab changes with one exception... the first tab that is selected still comes back null... In my specific case, I was able to handle this exception differently anyway, by putting some code in onResume.
Hope this helps.

Related

getFragmentManager() returns null sometimes

android.app.Fragment android.app.FragmentManager.findFragmentByTag(java.lang.String)'
on a null object reference
I had found numerous posts with this issue here on SO, but none of them helped me.
It happens relatively rarely (1/100). The place where I call the getFragmentManager() is on the main thread, when a button is clicked :
My flow is simple , first I have fragment A, then add on it several fragments until I get to fragment X.
Once a button is clicked in a custom view class that is instantiated and held in the fragment X, I call :
fragment A.getInstance().showLoadBar();
in fragment A the showLoadBar calls a fragment just for displaying loading, and it uses the getFragmentManager() there.
Most of the time it works great, and I am not able to recreate this crash. But I can see that this crash does happen sometimes.

How do I access elements in multiple nested fragment from an activity in android

I am working on a "guided tour" feature for my android application and need to dim and brighten certain elements on the current screen. I am using fragments for each screen and when I try to access elements in those fragments from the activity they are always null. I have been through countless questions and none seem to help me.
The structure of the fragments is as follows:
I'm using a Fragment TabHost that has only two tabs.
Each tab widget has a container fragment that I use to switch between a fragment A and a fragment B within one tab.
I'm going to start the tour from Tab one, fragment b but everytime I try to get the elements they are null even if I create a new fragment the line before let alone try to drill down into nested fragments.
I am trying to find the fragment using getSupportFragmentMangager() but since I have to get elements 3 fragments deep I end up trying to do an operation on an element that is null. I hope I am not missing something simple here. I am unsure where to start.
Why not store a static reference to the variables you need global access to in the Activity, then access them like MyActivity.class.myVariable and use getSupportFragmentManager#replace() to swap out the fragments dynamically?
You can use getChildFragmentManager() instead of getSupportFragmentMangager()
I discovered my problem was that the view being created by the fragment was simply not finished and therefore not displayed when I called my code to get elements from the fragment layout. I needed to wait until it was done being created and then I could find any element including elements in nested fragments. The nesting of views didn't matter.
This code fixed my problem:
import android.os.Handler;
//After doing replace transaction
final FragmentManager fragmentManager = getSupportFragmentManager();
final Handler handler = new Handler();
//Wait for fragment view to be created
handler.postDelayed(new Runnable() {
#Override
public void run(){
//Do stuff after 100 miliseconds
TabHostParent_Fragment myFrag = (TabHostParent_Fragment) fragmentManager.findFragmentByTag("tabhost_frag");
Switch auto_switch = (Switch) findViewById(R.id.auto_switch);
auto_switch.performClick();
}
}, 100);

Cannot get Fragment data because of Fragment Life cycle

Using Tabs means using fragments, and for some reason fragments have new steps in their life cycle, like onAttach(Activity).
My fragment fills up some maps from the resources, and it is done on onAttach() instead of the fragment constructor; because in the constructor getResources() throws an exception due to the lack of Activity yet.
The fragment is created on MainActivity like this:
#Override
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
switch( tab.getPosition() ) {
case 0:
if( fragmentTab0 == null ) {
fragmentTab0 = new MyFragment();
setTabText(0, ((MyFragment)fragmentTab0).getMyName());
}
fragmentTransaction.add(R.id.fragmentContent, fragmentTab0, "TAB0");
break;
Here lies my problem, in the call to the fragment method getMyName() which uses the maps I mentioned before to get a string. The call to getMyName() is executed before the fragment's onAttach() and the maps are not ready yet.
I am sure I can find a convoluted way to get the name (actually I tried already to pass the activity to the fragment's constructor and built the maps there, and it works, but it goes against the fragment philosophy).
I would have thought that the activity should be visible during Fragment constructor, since the fragments are created from the activity they're going to be eventually attached, so there is no point in delaying the activity attachment.
I also would have thought that the call to new MyFragment() should return after onAttach() is done. But it returns right after the constructor is done.
Therefore I feel not comfortable with the situation and I wonder if I am using fragments the wrong way, if so, the question is, how am I supposed to do it right to be able to call getMyName() there.
Note: From the fragment life cycle diagram it is clear that onAttach() and onDetach() are indistiguishable from onCreate() and onDestroy() respectively, so I question if they are really necessary.
onAttach() is not invoked until the fragment transaction is committed. Until then, a fragment has no reference to the creating Activity unless you pass it such a reference. Passing that reference is probably the cleanest way to implement what you're trying to do.
Generally in Android the title is dictated by the containing activity, not the fragment. For example, a PreferenceActivity's xml headers file lists titles and their associated fragments; those titles do not appear in the preference xml files used by the PreferenceFragment.

Nested fragment with wrong activity reference after configuration change

I'm finally looking into the new nested fragments APIs in the support library revision 11.
Everything It worked pretty well till I tried to use the activity reference held by the nested fragments.
After a configuration change the childFragment doesn't seem to get detached and re-attached to the new activity.
Basically after an orientation change my childFragment is in an inconsistent state from which I can't get the correct activity instance with getActivity().
I manged to get the correct one using getParentFragment().getActivity() and it works, but I don't think that's the right way to go.
here is the code I use to add the fragment in the parentFragment the first time, after that the fragment is automatically added back to the parentFragment:
public void addChildFragment() {
Fragment f = getFragment().getChildFragmentManager().findFragmentByTag( FRAGMENT_CHILD_TAG );
if (f == null) {
FragmentTransaction ft = getFragment().getChildFragmentManager().beginTransaction();
f = new TrackBrowserFragment();
f.setArguments( getFragment().getArguments() );
ft.add( R.id.fragment_album_detail_child_fragment_layout, f , FRAGMENT_CHILD_TAG );
ft.commit();
}
}
This inconsistent in the activity instance obviously lead to multiple problem with my fragment ( binds with services, broadcast receivers and so on ).
I'm probably doing something wrong cause I don't think that this is the correct behavior of a nested fragment.
so:
Am I doing something wrong with the code?
Is this the expected behavior of a nested fragment?
Am I missing something?
Should I detach/attach it by myself?
Thanks
I found out wich was the problem, i was using setRetainInstance(true) in the parent fragment and that kept the child fragment to be detached.
After I Removed that line everything works as expected

FragmentStatePagerAdapter IllegalStateException: <MyFragment> is not currently in the FragmentManager

I'm getting this on some cases, in onResume(), of an activity which uses a FragmentStatePagerAdapter. When using device's back button. Not always. Not reproducible.
I'm using support package v4, last revision (8).
Already searched with google, no success finding a useful answer.
Looking in the source, it's thrown here: FragmentManager.java
#Override
public void putFragment(Bundle bundle, String key, Fragment fragment) {
if (fragment.mIndex < 0) {
throw new IllegalStateException("Fragment " + fragment
+ " is not currently in the FragmentManager");
}
bundle.putInt(key, fragment.mIndex);
}
But why is the index of fragment < 0 there?
The code instantiating the fragments:
#Override
public Fragment getItem(int position) {
Fragment fragment = null;
switch(position) {
case 0:
fragment = MyFragment.newInstance(param1);
break;
case 1:
fragment = MyFragment2.newInstance(param2, param3);
break;
}
return fragment;
}
#Override
public int getCount() {
return 2;
}
If your ViewPager is layouted inside a fragment (not an activty) :
mViewPager.setAdapter(new MyFragmentStatePagerAdapter(getChildFragmentManager()));
I had the same error here, but for another reason.
In my case I had override getItemPosition in my FragmentStatePagerAdapter. My ideia was to return the position, if the item exists, or a POSITION_NONE, if it doesn't exists anymore.
Well, my problem was the fact that when my collection got empty I returned POSITION_NONE. And that broke everything.
My fix was to return POSITION_UNCHANGED when I had an empty collection.
Hope it helps someone else.
The two key things to understand the bug are:
It happens sometimes.
It happens in onResume().
Given this information, it's likely that the ViewPager is not retaining the state of your Fragments. If you are manipulating the Fragments directly from the Activity, it could be the case that the off-page Fragment is getting destroyed and your Activity is trying to manipulate a null fragment. To retain the Fragment's state even when it is not in the current screen, the fix is pretty simple:
private static final int NUM_ITEMS = 2;
ViewPager mPager = /** instantiate viewpager **/;
mPager.setOffscreenPageLimit(NUM_ITEMS-1);
You can read about it here:
ViewPager Fragments getting destroyed over time?
Got it, the reason was, that I'm intantiating the Adapter each time in onResume().
If I instantiate the adapter only once, in the life cycle of the activity, this does not happen anymore.
This exceptions means that you are trying to attach a fragment to an activity which is no longer correct state to attach the fragments. What it means is, whenever we try to attach fragments (especially through an asynchronous call), there is a small probability that someone has pressed the back button and activity is in merge of getting destroyed while you are attaching the fragment. This is not always reproducible as its just a race condition, and might not always occur..
There are two ways to fix this:
1) This happens when you the onSaveInstanceState of your activity has been called and post to that you are trying to attach the fragment, since android wont be able to save the state of your fragment now, it will throw an exception. To overcome this and if you are not saving the state of your fragment, try using
commitAllowingStateLoss(), while committing the transaction.
2) To be very safe, check whether your activity is in correct state or not before attaching the fragment, use the following code in onPause:
boolean isInCorrectState;
public void onCreate{
super.onCreate();
isInCorrectState = true;
}
public void onPause() {
super.onPause();
if(isFinishing()){
isInCorrectState = false;
}
}
Now use this flag to check if your activity is in correct state or not before attaching the fragment.. Meaning attach the fragment iff isInCorrectState == true.
For me the reason was something else.
In my Loader.onLoaderReset() I cleared the data from the adapter. When I was leaving the app, onDestroy() caused the loader to reset, which cleared the FragmentStatePagerAdapter. I think it caused the adapter to clear all references to it's Fragments, but somehow, the FragmentManager didn't notice and threw the Exception. Doesn't seem very logical to me.
Note that for me it happened Activity.onDestroy().
Hope it helps someone.
Fragments in the ViewPager are fixed, instead of trying to replace the fragments in the adapter, try to give a different set of fragments and notifyDataSet changed, or take the advantage of FrameLayout to show another fragment over the view pager tab's current fragment.
There is my solution that works:
Swipe Gesture applied at Fragment level along with ViewPager with it's default swipe disabled

Categories

Resources