I stumbled into a crazy little 'bug', or i'm just doing something wrong. I am trying to get the reference to a fragment that is inside a fragment. So we have ParentFragment, that is a child of MainActivity. I can get a reference to this ParentFragment without a problem, because the ParentFragment is added to the MainActivity via code:
ParentFragment fragment = new ParentFragment();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragmentPlaceholder, fragment);
transaction.commit();
I have added a ChildFragment to the ParentFragment via XML like this:
parent_fragment.xml
<RelativeLayout ... etc>
.. some other views, findViewById('') works great on these.
<fragment
android:layout_width="70dp"
android:layout_height="70dp"
android:id="#+id/childFragment1"
android:tag="1"
class="com.my.package.ChildFragment"
android:name="com.my.package.ChildFragment"
tools:layout="#layout/child_fragment" />
</RelativeLayout>
All works fine, the ChildFragment(s) show up in the ParentFragment. The only thing is; i cannot find the fragmentById.
I have tried multiple ways of achieving this, how it should be done (?) :
getChildFragmentManager().findFragmentById(R.id.childFragment1)
Didn't work. Also, getting the Activity and using that fragment manager doesn't work (.. of course).
I have also tried getting all fragments in the ChildFragmentManager, iterating over them etc. But this always returns null:
getChildFragmentManager().getFragments()
Do you have any idea why this is happening? Can't i use the ChildFragrentManager if the fragments are not added via code?
Thanks!
You must add your child fragment dynamically. See here:
Note: You cannot inflate a layout into a fragment when that layout
includes a <fragment>. Nested fragments are only supported when added
to a fragment dynamically.
ps. if you are planning to use nested fragments then be prepared for various strange behaviours, you can find them in bug reports to android platform:
https://code.google.com/p/android/issues/list?can=2&q=nested+fragment&sort=-opened&colspec=ID+Status+Priority+Owner+Summary+Stars+Reporter+Opened&cells=tiles
If you want to use nested Fragments and be able to get a reference to them via findFragmentById you cannot use the support package.
I changed all getSupportFragmentManager()'s to getFragmentManager() and replaced all FragmentActivity's with regular Activity's.
Now it works as expected, just call getChildFragmentManager().findFragmentById().
Related
I know this question has been asked before in other threads like this one :
Trying to replace one fragment with another
But, in these cases the layout file would have a node with an id. In my case, I created a custom SettingsFragment and this SettingsFragment consists of many items. When I click on one item, it will start another fragment CreatePasswordFragment.
Now, my SettingsFragment does not have an id (or does it? I am a newbie to android....), so I don't know how to use .replace(R.id.fragment_container,CreatePasswordFragment.newInstance())
So far, I have used the following method, and while it works I am wondering if there is a better way to do it?
getActivity()
.getSupportFragmentManager()
.beginTransaction()
.remove(SettingsFragment.this)
.replace((android.R.id.content, CreatePasswordFragment.newInstance(userProfile.email))
.addToBackStack(null)
.commit();
The id in the replace() method is the container id, not your fragment id. So it would be the id of whatever layout in your activity is displaying the fragment. So you are doing it right. One thing to note, however, is that you do not need to call remove(). You can just call replace() and the FragmentManager will automatically call remove() for you.
I added some Fragment into a TableLayout and I want to manage them from my container Activity, so I used this:
Fragment fragment = (Fragment) tableLayout.getChildAt(i);
but getChildAt(int) returns a View and a View could NOT cast to Fragment
I don't understand why people are down-voting your question. Fragments can be very confusing at times, especially for beginners. To understand your problem, you must learn what is a Fragment and how they are used.
To start with, a View is something that has an existence on the screen. Examples include: TextView, EditText, Button, etc. They are placed inside "layouts" written in Xml or Java/Kotlin. These layouts are shown using an Activity.
Now, a Fragment is not a View. It does not have any existence on the screen at all. Instead, it's a class that simply manages a "layout" — kinda similar to an Activity. If you need the View returned by your Fragment's onCreateView(), you can directly use findViewById() within your Activity.
If you need a reference to your Fragment, there are two possible ways of doing this:
1) If you added the Fragment programmatically like this
getFragmentManager()
.beginTransaction()
.replace(R.id.fragment_container_viewgroup, myFragment, FRAGMENT_TAG)
.commit();
You can use:
MyFragment myFragment = (MyFragment) getFragmentManager().findFragmentByTag(FRAGMENT_TAG);
2) If you added the Fragment inside an XML layout like this:
<fragment android:name="com.example.android.fragments.HeadlinesFragment"
android:id="#+id/fragmentContainer"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent" />
You can use this:
getFragmentManager().findFragmentById(R.id.fragmentContainer);
Basically, each Activity has a FragmentManager class that maintains all the active Fragments, and there are two ways of finding them: Using a unique TAG that you pass while showing a fragment, or passing the container view-ID where the fragment was added.
For people looking how to actually get a reference to the Fragment object from a View there is now a method in FragmentManager called findFragment(View) (reference)
//in Java
FragmentManager.findFragment(view)
//in Kotlin there is an extension function
view.findFragment()
Be careful - it will throw an IllegalStateException if the view was not added via a fragments onCreateView.
You can not get a fragment like this. You will have to add fragment with a tag and retrieve it by that tag.
to add a fragment do following:
getFragmentManager().beginTransaction().add(R.id.container, fragment, "tagTofindFragment");
to get fragment:
fragment = getFragmentManager().findFragmentByTag("tagTofindFragment");
Here tagTofindFragment is that tag that should be unique among your fragments.
So I basically have a button in 'DemosFragment' and when I click it, I want it to open another fragment (SettingsFragment), I understand now that I need an activity to fix this issue, as the button currently has an onClick method using intent/startActivity, so how would I go about creating an activity that just holds my fragment? I know that may sound weird they way I wrote it, I just started Android development, but basically I have a fragment and because I want a fragment to have a button to open another fragment, I figure I need an activity for the fragment I am trying to open, so how do I create that activity and what do I need to put in it? Thanks.
You need an activity with the following code:
public class ShowFragmentActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_show_fragment);
}
}
You also have to create a layout xml file called activity_show_fragment.xml in your res/layout folder:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment class="com.example.yourFragmentsClassName"
android:id="#+id/fragment_id"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
This should work for just displaying your fragment.
To launch the activity, paste this code in your button's onClick method:
Intent i = new Intent(this, ShowFragmentActivity.class);
startActivity(i);
It's always a good decision to look at the official docs: http://developer.android.com/reference/android/app/Fragment.html.
Hope that helps!
Wow! Your question requires a long answer, however is a good practice (and madatory too) that Fragments cannot communicates between each others, but they can be hosted by an Activity; in that case an Activity can manage the communication flow between them (fragments) and can be developed in several ways, Bundle, Intent and the Handler. Have a look to the ufficial Android documentation here:
http://developer.android.com/training/basics/fragments/index.html
The android docs section on building a flexible UI is a good example of how to start/load a Fragment from an Activity. In the example you will see that a FrameLayout in the Activity XML is used a the fragment container. This will be the View in which all of your fragments are displayed.
When you load your fragment with a FragmentTransaction the contents of your fragments layout will be displayed in the container View. In the above referenced example this takes place with SupportFragmentManager a class included with the android support library, for facilitating fragment transactions in earlier version of the operating system. SupportFramgnetManager requires that you extend FramentActivity and not just Activity. If you're not worried about backwards compatibility and are extending activity, not fragment activity, you can simply use getFragmentManager() instead.
getFragmentManager().beginTransaction()
.add(R.id.fragment_container, firstFragment).commit();
After the initial add transaction you can switch between fragments using the replace method for your fragment transaction. Replace does exactly what it sounds like, it swaps one fragment for another. To accomplish this from within your firstframgnet use
SecondFragment secondFragment = new SecondFragment();
getActivity().getFragmentManager().beginTransaction()
.replace(R.id.fragment_container, secondFragment).commit();
Notice that from within the fragment I used getActivity(). This allows you to reference the context of the host activity to access the fragment manager. When you are within the activity you do not need to use getactivity because the fragment manager is already accessible from that context.
Well, i got a simple <FrameLayout>:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/FragmentContainer"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
Then in my code, i added a Fragment to it:
FragClass aFrag = new FragClass();
getSupportFragmentManager().beginTransaction()
.replace(R.id.FragmentContainer, aFrag).commit();
And somewhere else in my code, i want to get that FragClass (extends Fragment) object from the ID R.id.FragmentContainer.
i have tried
((ViewGroup) findViewById(R.id.FragmentContainer)).getChildAt(0)
or
((FrameLayout) findViewById(R.id.FragmentContainer)).getChildAt(0)
but they are returning the View, instead of the Fragment attached to it.
i know i can keep the variable aFrag somewhere, so i do not need to find it again. But i believe there should be a way to retieve it.
Let me wrap it up by a full answer :)
In this case, the dynamically added Fragment uses the ID of the container View (ViewGroup).
ref: http://developer.android.com/guide/components/fragments.html#Adding
Note: Each fragment requires a unique identifier that the system can use to restore the fragment if the activity is restarted (and which you can use to capture the fragment to perform transactions, such as remove it). There are three ways to provide an ID for a fragment:
Supply the android:id attribute with a unique ID.
Supply the android:tag attribute with a unique string.
If you provide neither of the previous two, the system uses the ID of the container view.
It is because it's a Fragment afterall. We have to use getSupportFragmentManager().findFragmentById() to retrieve it, which returns a Fragment, instead of findViewById() which returns a View.
So the answer to this problem would be:
((aFrag) getSupportFragmentManager().findFragmentById(R.id.FragmentContainer))
Thanks to #Luksprog.
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