Strange fragment behaviour - empty container in view hierarchy after screen rotation - android

I encountered very strange problem with fragments. In my case I have fragment which is placed inside activity. It has two type of layout:
layout - with Frame Layout with list_container id
layout-land - with two Layout: list_container and detail_container.
When user enters activity in portrait mode he sees TestListFragment. When he clicks one of the list element TestDetailFragment is added to backstack.
Next, when user rotates screen TestDetailFragment is removed from backstack and placed inside layout with id detail_container. How strange part appears, when inspecting view hierarchy there are two list_containers (one is empty and is over the rest of the content causing view overlap). onActivityCreated in TestFragment is triggered twice.
I'm including hierarchy view and screens:
http://i.imgur.com/cw37VX4.png
http://i.imgur.com/xQYalCJ.png
http://i.imgur.com/SObusj0.png
http://i.imgur.com/2sVhlIP.png
http://i.imgur.com/BCXrQ1K.png
Source code:
Activity with fragments: http://pastebin.com/2vAqQYgw
Activity layout: http://pastebin.com/iEawVBCx
TestFragment layout: http://pastebin.com/z5MZ5eR9
TestFragment layout-land: http://pastebin.com/RJHYRinM
Is this normal behavior? How to get rid of this layout?

When you nest fragments (i.e., have a fragment host a fragment), the child fragments need to be set up either by:
having the parent fragment inflate a layout containing <fragment> tags
having the parent fragment use getChildFragmentManager() when executing a FragmentTransaction to add the child fragments
In this case, the code in the question is using getActivity().getSupportFragmentManager(), not getChildFragmentManager().
To be honest, I cannot completely understand how this resulted in the cited symptoms, but according to the comments on the question, it helped.

When Android detect rotation it destroyes the running activity and create a new one, so if you have a behaviour on onCreate() and onDestroy() of the activity, keep in mind that. Take care also for Fragment that has a further event to manage and is related on the attached Activity onAttach()

Related

Show nested fragment on screen rotation without showing parent fragment

When rotating the screen my nested fragment is shown but for some brief moments, the parent fragment is also shown.
I have my MainActivity that has a FrameLayout with ID activity_base_container.
I'm doing this when my activity starts:
Fragment initialFragment = getInitialFragment();
mFragmentManager.beginTransaction()
.add(R.id.activity_base_container, initialFragment, initialFragment.getClass().getSimpleName())
.commit();
That initialFragment initial fragment is responsible to check some conditions and depending them will launch one of two possible fragments:
fragmentManager.beginTransaction().replace(R.id.activity_base_container, fragment, fragment.getClass().getSimpleName()).commit();
Lets assume it launches FragmentF (whit a root FrameLayout with id fragment_f_root). This fragments layout has a set of options. When the user clicks one of those options, the corresponding fragment is created and is launched like this:
//The example here is an option that displays a google map.
fragment = FragmentMapMultipleActivity.newInstance();
fragmentManager.beginTransaction()
.replace(R.id.fragment_f_root, fragment)
.addToBackStack(fragment.getClass().getSimpleName())
.commit();
At this point all is working as expected. The problem is when I rotate the screen. FragmentF appears briefly and then immediately FragmentMapMultipleActivity, the nested fragment, appears.
Is it possible after rotating the screen show only the nested fragment or I should change my "architecture" to something else?
should change my "architecture" to something else?
Probably, you should.
The brightest Android-minds from Square are even advocating to avoid simple fragments everywhere it's possible: Advocating Against Android Fragments
Nested fragemnts, in its turn, increase complexity exponentially. The only good pattern of using them I've seen so far is ViewPager with it's FragmentPagerAdapter. In majority of other cases, consider using Custom Views instead.
It keeps your app's lifecycle cleaner and more predictable.
I don't think you can do much with this blinking you see, apart from:
setRetainInstance(true) and avoid full re-creation of the Fragment in Activity, so you keep you fragment's data during change of the configuration (and then pass same retained fragment to the fragment manager)
keeping layouts as lightweight as possible
avoid re-creation of already initialized variables
keep onViewCreate() as lightweight as possible
Good luck!

Re-inflate different layout View in same Fragment in onResume

I have a Fragment, MainFragment, which can contain two, three, or four nested fragments. The specific fragments that will be shown can be changed by the user in the settings.
There is a different layout for each number of fragments. For instance, layout_3 is used when the user chooses three nested fragments.
What I need to do is dynamically update MainFragment's layout, and which fragments will be nested within that layout, in onResume() (i.e. Once the user comes back from the settings). There are about 10 fragments the user can choose from, and I need to be able to swap them in and out of MainFragment dynamically.
I'm having trouble doing this. The only way to update the layout/view once I return from the settings is to leave MainFragment and then come back (which calls onCreateView()).
Here is an example of what I do in onCreateView() to initialize the layouts (two nested fragments is the default):
mView = mInflater.inflate(R.layout.layout_2, mParent, false);
getChildFragmentManager().beginTransaction().add(R.id.fragmentContainer1, fragment1).commit();
getChildFragmentManager().beginTransaction().add(R.id.fragmentContainer2, fragment2).commit();
return view;
Suppose the user then goes to the settings and chooses to have three nested fragments. This is what I've tried in onResume(), to no effect:
mView = mInflater.inflate(R.layout.layout_3, mParent, false);
getChildFragmentManager().beginTransaction().add(R.id.fragmentContainer1, fragment1)).commit();
getChildFragmentManager().beginTransaction().add(R.id.fragmentContainer2, fragment2).commit();
getChildFragmentManager().beginTransaction().add(R.id.fragmentContainer3, fragment3).commit();
I'm not sure if I'm doing something wrong. Ideally, I would just force MainFragment to call onCreateView() again, but none of the solutions for that problem seem to work.
Any ideas? Thanks for the help.
Edit: I believe the problem is with inflating the new View, rather than replacing the fragments.
For instance, suppose the default screen is layout_4, with four fragment containers. The user then goes to the settings un-checks all four default fragments, and chooses three new fragments. In onResume(), we try to inflate layout_3, and then add the fragments. I think layout_3 never inflates, but because my fragment containers have the same style id across layouts (i.e. fragmentContainer1 - fragmentContainer4), the first three fragment containers are updated. The fourth one remains as it was, since I assumed we were in layout_3 and did not try to update it.
This behavior is confirmed and results in a crash when the user tries to increase the number of fragments, rather than decrease. Above, when the user switched from four fragments to three fragments, there was no crash because all three fragment containers I tried to update exist in layout_4. But if the user is in layout_2 and then goes to the settings to select a third fragment, we'll try to add a fragment to fragmentContainer3 when we resume. This results in a crash because layout_3 fails to inflate.
java.lang.RuntimeException: Unable to resume activity
java.lang.IllegalArgumentException: No view found for id 0x7f0c005f
Any ideas how to fix this? The call to re-inflate mView in onResume() does not seem to have any effect.
Edit 2: I've tried calling mParent.addView(mView) after inflating, but still experience the same behavior as above, for the most part.
When you return from the settings, onResume() should be called in the MainFragment and subsequently any nested fragments that were already loaded in the MainFragment. Can you not include any update logic in the nested fragments' onResume() instead of only in onCreateView()?
Otherwise you can create a different code path and put update logic there: make public methods in the classes for fragment1, fragment2, fragment3 that include all your update logic, and call those methods from somewhere in MainFragment. (You could create an interface and have the nested fragment classes inherit that interface, if they are different classes and you want to have a cleaner design.)
Be careful about whether or not the nested fragments have been resumed yet—calling methods on View objects when nested fragments' onResume() hasn't been called yet could be problematic.

Mixing layout values each other when use same layout for different fragments in ViewPager

In my Android application there are two fragments called FragmentA and FragmentB. I added both fragments to a ViewPager using a FragmentAdapter So when I run the application I'm getting a unexpected layout (both fragment layouts are mixed together). Whenever I tried changing the currentItem of ViewPager by swiping for each time the layout is mixing each other.
My question: Is there any problem if we use same layout to different fragments in a ViewPager?
I found the answer.
There is no problem if you use getView() function of Fragment to refer the current root view of a fragment. So that you can get exact views in the Fragment. You can use like this(getView().findViewByid(...);
But there is a problem if you use activity reference to refer views in the fragment. Because all views in each fragment which is using same layout xml has same id. So if you change the value of a view in a fragment that will reflect in other fragments which are using the same layout in ViewPager. One more thing, think like this, when the activity created, all the fragments in the ViewPager of that activity will also be created and runs in background. So if you change the value of a view, android will return 1st view which is having the same id. Android knows only id of views. So always refer views of fragment using it root view (getView()).

Fragment LIfecycle in Custom Tab View

I have a custom widget that performs FragmentTransaction.replace when buttons are pressed. Currently, my code is set up such that the first time a fragment is created, it attaches a bunch of stuff to the view that isn't originally part of the xml layout file.
When the app first launches, all my fragments show stuff correctly, however, let's say I start on Fragment A. I can then transition to Fragment B (with B showing up correctly), however, when I transition back to Fragment A, all the stuff I have attached to the view of Fragment A is now gone. I know this happens because onCreateView is called which probably means the Fragment's view is re-generated when FragmentTransaction.replace is called.
Is there a way where I can keep my fragments around instead of having them re-generate their views when FragmentTransaction.replace is called?
Thanks!
Instead of using fragmentTransaction.replace, use fragmentTransaction.show and fragmentTransaction.hide.
That will keep your fragments from being destroyed.

onCreateView called on non existing fragment

I am using fragments in Android. At the beginning the API seemed very easy to use. Now I can say that it is overcomplicated with many problems that are not clearly documented. For example following many tutorials, I have created two layout one for the portrait and one for the landscape. In the portrait I put just one fragment (list) while in landscape I also have the detail fragment.
If I rotate the device, onCreateView of detail fragment gets called (and it is ok). The problem is that, if I rotate again the device in portrait (with no detail fragment in the xml), the onCreateView of Detail fragment is called again! Why this? and how to avoid? I can check for ViewGroup container == null but it is terrible.
UPDATE: making some research, I found that if the fragment is in the layout with the tag this is called just once when the layout is not present while if you replace a FrameLayout present just in one layout with the fragment, then the onCreateView is always called. I was expecting it was not called if attached to a container that is not present
Thanks

Categories

Resources