On the tablet we have two fragments (two different views of the same data) that sit next to each other. On mobile devices we'd like to switch between these two fragments at the push of a button. The mobile layout looks something like this:
<RelativeLayout>
<fragment id="container" name="fragA"/>
<ImageButton onClick="swapFragments" />
</RelativeLayout>
In the activity's swapFragments(View) method, I'm attempting to use the FragmentManager to replace fragA with fragB:
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.container, new FragB());
fragmentTransaction.commit();
...but I can always see fragA through the transparent parts of fragB, leading me to believe that it's just putting fragB on top of fragA, not replacing it.
I'm starting down the path of using hide(Fragment) and similar methods in the transaction, but that doesn't look like the right way to do it.
Any tips on how to swap these fragments the right way?
Edit: I saw the answer to this question. This confuses me, though, as I need to be able to specify a different layout for tablet and phone. If I have to programmatically add fragments, how do I avoid code specific to each layout in the activity (i.e.
if(TABLET) {
addFragmentA();
addFragmentB();
} else {
addFragmentA();
}
Don't mix Fragments created in XML and in Code - bad things will happen. Keep a container view in the layout, then add / replace fragments into it (don't have the first fragment inside it).
Looks OK to me, I'm doing something similar albeit I'm adding my initial fragment from the activity using add rather than having it loaded by referencing it in the layout. There might be a bug there. FYI I'm using the compatibility library.
Might be worth trying:
1) Add the transaction to the back stack to see if that makes a difference, it seems like you may want that functionality anyway.
2) Give your fragment in the layout an id or tag, then use that to perform a remove and add fragB instead.
3) Try loading your fragA from code instead and see if that makes a difference.
well, first of all you can try using the newInstance() factory method for instantiating the fragment B and not just new FragB().
However, I think this is not the problem.
Can you just try not to use in you XML layout the fragment tag?
Just do something like this:
<RelativeLayout>
<Linear/FrameLayout id="container" name="fragA"/>
<ImageButton onClick="swapFragments" />
</RelativeLayout>
So use like a Frame or LinearLayout as container for you fragment and inflate it in the onCreateView callback from the Fragment. Maybe it helps, let me know.
Cheers!
As far as I can see you are not hiding / detaching the previous fragment so both will be displayed. You can implement something like this:
if (mFragment != null) {
ft = mActivity.getSupportFragmentManager().beginTransaction();
ft.hide(mFragment);
ft.detach(mFragment);
ft.commitAllowingStateLoss();
}
Related
Recently delve into fragments and from what I understand to create a fragment you need a java class and the fragments layout. This makes sense. However what I cant seem to wrap my head around is what "container", or layout do I use to store/insert the fragment? In android studio you can use this to insert fragments, or you can use any other of the layouts. But which one is ideal to use?
Also I saw in a reddit post that I shouldn't be using fragments at all and that its preferred to instead use Frame layouts and play around with your views visibility for the desired effects. Is this true?
You're slightly over-complicating the concept of Fragments.
Fragments, like Activities, don't actually need a dedicated fragments layout xml file. If you choose to do so, you can create the entire layout through Java code, not that I will understand why you'll choose to do so.
So for Fragments, you don't need a Java class and a fragment's layout file. The only requirement is the Java class, and the layouts file is just the preferred approach to inflating a layout, similar to how it is for Activities.
As for your question about the container of the fragments, it's really a matter of your app's design.
You can add a Fragment to your Activity or other Fragments, through the FragmentManager in code or through the <fragment> tag in your layout.xml files.
Neither of those are the best way or the preferred way, since it really depends on what your app needs.
Using the <fragment> tag will cause that fragment to always be added whenever the layout is inflated. This is actually VERY bad if your Activity requires dynamically switching Fragments due to your use of things like ViewPagers, Tabs, Drawer Navigation, or etc. However, it's GREAT if there's no need to dynamically switch fragments and for that specific Activity or parent Fragment, this fragment is a fragment that's always loaded.
For example, let's say you designed a flexible AddNew Fragment that's used in a Dialog and an AddNewActivity. Due to reusing the same screen and code, you decide to make this part of your code a fragment so you can insert it inside a DialogFragment or into another Activity. But, for those DialogFragments and Activities, the only Fragment it'll have is the AddNewFragment, so it'll make sense to just insert that fragment into the Dialog layout and Activity layout through the <fragment> tag.
As for the option with Java code, the preferred approach is to use a FrameLayout. But there's no need to play around with any View visibilities!
The common approach is to just use:
<FrameLayout
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
A FrameLayout is used because it's going to be the container of the Fragment. In other words, the Fragment will be stored inside of this layout.
So in Java code, you can simply use this code to replace the Fragment inside the container with your new one:
getSupportFragmentManager().beginTransaction().replace(R.id.container, AddNewFragment.newInstance()).commit();
Optionally, you can use add() instead of replace() if you want the fragment to be placed ontop of another fragment within the FrameLayout container.
So yes, to give a decisive answer to your question, there's no ideal way to add a Fragment to an Activity or another Fragment. Each option has it's benefits and drawbacks, with some working better for certain situations and others working better for others.
In the end, it really depends on what your App needs. If you need your Fragments to be flexible, so you can switch Fragments, then this must be done through Java code, because fragments added through the <fragment> tag can't be removed at runtime. However, if you don't need your Fragment to be replaced and it's definitely always going to be showing the same Fragment, then using the <fragment> tag removes the need to write extra Java code to load the dedicated Fragment.
One thing I really do need to point out is... that reddit page you read about is wrong. The 'preferred' way to use Fragments is not to use FrameLayouts and play around with View visibilities. I actually have no idea why there's even a need to change View visibilities.
You can use the new androidx.fragment.app.FragmentContainerView to get a better performance than FrameLayout.
Here more information.
You could use the Fragment layout, but this isn't very flexible. If you're just showing one Fragment, it should work, but using a FrameLayout and inserting your Fragment into it works better as this allows you to change them on the fly.
You may see a FrameLayout with the id R.id.container or something similar, and what this is used for is the Transaction.
For example, if you want to insert FragmentOne into your layout, you can just do this and it'll put it into R.id.container.
Fragment fragment = new FragmentOne();
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.container, fragment);
transaction.commit();
I have a viewPager in my app which has several tabs. In some of the tabs, on clicking an item a new fragment is shown. I want this fragment to cover the tabs. Doing this is possible but the approaches don't look good to me.
1.) one way is that I add the newly created fragment to the activity using getSupportFragmentManager(). This solves the problem but doesn't look like a good idea as it will create problems when using back button etc.
2.) Other way is to hide the tabs manually using Visibilty.GONE but problem with this approach is that this hiding of the tabs is visible, I mean the animation could be seen and looks bad.
Is there a better approach to do this problem?
This is my code. "sub_fragment_container" is present in the activity xml, so I get an error java.lang.IllegalArgumentException: No view found for id 0x7f0e00ff (com.my.app:id/sub_fragment_container) for fragment DetailFragment{
FragmentTransaction fragmentTransaction = getChildFragmentManager()
.beginTransaction();
Fragment profileFragment = new DetailFragment();
profileFragment.setArguments(bundle);
fragmentTransaction
.add(R.id.sub_fragment_container, profileFragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
Use android:animateLayoutChanges="true"
Or, to fine tune it further, here's a tested method that I've personally used in my app Newslet to solve the exact same problem.
Wrap your Toolbar and TabLayout within an AppBarLayout. This should be easy if you're aware of the Design Support Library. If not, this should get you started.
Add this piece of code in your onCreate():
LayoutTransition layoutTransition = new LayoutTransition();
layoutTransition.setDuration(200);
layoutTransition.setStartDelay(LayoutTransition.CHANGE_APPEARING, 0);
layoutTransition.setStartDelay(LayoutTransition.APPEARING, 0);
layoutTransition.setStartDelay(LayoutTransition.DISAPPEARING, 0);
layoutTransition.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING, 0);
appBarLayout.setLayoutTransition(layoutTransition);
After this, when you remove the Tabs or add it back, the layout change will animate smoothly.
Hope this helped you!
I'm all new to Android Development but already in big trouble. I've read several threads both here and on other sites regarding a similar or even the same problem. But all of the answers I've found don't fit my needs or aren't working for me at all.
So here's the thing. I'm having a single Activity with a self-made "menu" layer including 3 buttons at the bottom of the activity and a ScrollView above. Now on clicking one of those buttons I simply inflate a fragment into the ScrollView using
svContent.removeAllViews();
getLayoutInflater().inflate(R.layout.fragment_xyz, (ScrollView) findViewById(R.id.svContent));
which is inflated perfectly fine.
Now the inflated fragment includes another fragment for Google Maps (API 2), which looks like this.
<fragment
android:id="#+id/fMap"
android:name="com.google.android.gms.maps.MapFragment"
android:layout_width="fill_parent"
android:layout_height="150dp" />
This fragment is also populated just the way I want on its parent fragment's inflation and the map is working fine.
Now I click on another menu button to have another fragment being inflated. But right when I click the button for the first fragment again, I get the following error:
Duplicate id xxxxx, tag null, or parent id xxxxx with another fragment for com.google.android.gms.maps.MapFragment
Like I said, I've tried several solution I found online including
moving from extending Fragment to FragmentActivity
using SupportMapFragment instead of Fragment
inflating the sub-fragment (the Google Maps one) from code instead of XML
All of these had several disadvantages I cannot accept at the moment (at least I think) or "didn't work" for me or in my constellation
Any help is greatly appreciated. And please me know if you need more information.
Regards,
D.
To replace the fragment in your ScrollView :
SupportMapFragment mapFragment = MapFragment.newInstance();
FragmentTransaction ft = getChildFragmentManager().beginTransaction();
ft.replace(R.id.svContent, _mapFragment );
ft.commit();
If you want to go back to the previous frgament with the back button, add ft.addToBackStack(null); before the commit.
And that's it.
By the way, you should probably not have a ScrollView as the container of your fragments. If the content must be scrollable, put the scroll in your fragment and put the fragment in a LinearLayout/FrameLayout/RelativeLayout.
To handle fragments, you should use the FragmentManager. This means that you should not inflate a fragment directly. Rather, you should allow the FragmentManager to do it for you.
you need to make map to null when user navigates to another screen.
destroy google map instance when onDestroy method.
#Override
protected void onDestroyView() {
Fragment fragment = getFragmentManager().findFragmentById(R.id.fMap);
getFragmentManager().beginTransaction().remove(fragment);
gMap.clear();
gMap = null;
super.onDestroy();
}
I am learning fragments but I am failing to understand the significance behind why fragments requires a Container.
The way I understand Fragments work is as follows :
FragmentActivity setContentview refers to a xml file which
defines where fragments would be located.
FragmentActivity creates instance of the fragments
Then assigns fragment to container.
FragmentManager then displays them.
The actual Fragment class then inflates a layout, and it is this layout which
contains all of the applications UI components.
(please correct me if I miss something here because I am only learning at the moment).
So why is the purpose of the Container why do we even need since in all the examples I have seen it is just a blank relative layout xml document.
Can different fragments share the same Container (since its just a RelativeLayout xml file)?
So in the example provided by google http://developer.android.com/training/basics/fragments/creating.html
They have a ListFragment and when item is selected through the use of the CallBack interface we eventually get back to this line of code :
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack so the user can navigate back
transaction.replace(R.id.fragment_container, newFragment);
My other question is:
1) Why does this line of code not replace the ListFragment (left side Fragment) with the article fragment. Since when it was initialised we see:
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, firstFragment).commit();
Instead ... the ListFragment remains on the left and the right Fragment is updated.
But the container fragment_container belongs to firstFragment this is the ListFragment.
And this is not the one that gets updated.
Do you see why I have the question ? This is not explained in the tutorial.
Here: http://marakana.com/s/post/1250/android_fragments_tutorial
And here: http://developer.android.com/guide/components/fragments.html
Read this and all will be clear:)
Fragment is a portion of Activity and can exist only inside an Activity. So you need a special type of activity that can handle fragment - it's FragmentActivity.
FragmentActivity without Fragments is almost like a normal Activity. But it has a FragmentManager to manage (add,remove,replace) fragments. When you want to add a Fragment to a FragmetnActivity you should specify where it should be placed (because fragment does not need to be fullscreen, just like GooglePlay-there are multiple small fragments). So this is why you need a container.
Can different fragments share the same Container (since its just a RelativeLayout xml file)?
Yes they can, you can replace one fragment with another within the same container.
I have looked around the internet and I have found only examples of Android fragmens, where they are placed on place (like two of them, one as a list, second is diplaying details of a selected item from list). My goal is to have classic android activity and I want to be able to add to it dynamically pre-prepared fragments with particular GUI. I want to add there dome group of EditText elements (editTextFragment), or several radiobuttons (radionbuttonFragment). Is there any example of how to do that? Making stable fragments is not usable for me.
Thanks
This answer is for those who all searching the answer for same question.
The answer is achieved by FragmentManager and FragmentTransaction with the following syntax.
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
then you need to add your fragment to this fragmentTransaction with the following syntax.
fragmentTransaction.replace(R.id.detailFragment, layout1);
And finally you MUST commit your transaction. Otherwise changes will not persist.
fragmentTransaction.commit();
Fragment and views are very similar so think similar... Also look at google api demoes for fragments. If I remember correctly they use a framelayout and adds fragments to it.
You can add and remove fragments to a FrameLayout in an activity's UI programmatically but your activity must extend FragmentActivty. Once you have created fragment classes and corresponding layouts, instantiated fragments can be added and removed via the FragmentManger and FragmentTransaction. See the section "Performing Fragment Transactions" in the Fragment documentation.