Fragment layout not fully inflating upon FragmentTransaction.add/replace - android

I am attempting to replace a nested fragment inside a tabLayout fragment.
My app structure follows this pattern.
Here is the XML for the Tab Fragment 2
<FrameLayout
android:id="#+id/scene_root"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".Home"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<FrameLayout
android:id="#+id/shows_fragment_list"
android:tag="list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<FrameLayout
android:id="#+id/shows_fragment_details"
android:tag="details"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<FrameLayout
android:id="#+id/shows_fragment_info"
android:tag="info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</FrameLayout>
I am using this code to add the NestedFragment to the TabFragment FrameLayout
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View root = inflater.inflate(R.layout.shows_fragment_layout, container, false);
ShowDetailsFragment newNestedFragment = new ShowDetailsFragment();
android.support.v4.app.FragmentManager fragmentManager = getChildFragmentManager();
android.support.v4.app.FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction
.add(R.id.show_fragment_details, newNestedFragment)
.addToBackStack("null")
.commit();
return root;
}
But when I add the Nested Fragment to the Tab Fragment, only the last part of my layout appears, in this case it is a list view (highlighted in red), but it always seems to be the which ever element is declared last when the layout is inflated by the corresponding Java file.
When I inflate these layouts as there own activity, there are no inflation problems, so it must relate to my implementation of fragments, any ideas where I am going wrong?

A good idea is always to use the Android Device Monitor to debug your layout.
I would also add some logging on the onCreateView of your fragments.

Ok so my problem was that in my XML layout for my ShowDetailsFragment (The Nested Fragment) I had a FrameLayout as the root, but there were two separate RelativeLayouts as children, meaning only the last element was being displayed, the problem was solved by nesting these two Relative Layouts in another single Relative Layout.
Props go to ctarabusi for pointing me in the right direction, although I am still unsure why this only caused a problem when inflating as a Fragment and not as an Activity.

Related

What happens when you call FragmentTransaction.replace() on a fragment container?

My app has 1 activity and multiple fragments.
The container is a FrameLayout in my activity, and after the initial fragment is added to that container, replace() is then used to change fragments.
Initial add: transaction.add(R.id.fragment_container, firstFragment, "FirstFragment")
After initial add: transaction.replace(R.id.fragment_container, newFragment, "FragmentTag")
activity_main.xml
...
<FrameLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:id="#+id/fragment_layout"
app:layout_constraintTop_toBottomOf="#id/toolbar"
app:layout_constraintBottom_toTopOf="#id/bottomNav">
</FrameLayout>
...
My question is, what exactly happens when I add and replace a fragment to R.id.fragment_layout?
Say my fragments are all ConstraintLayout - is the ConstraintLayout nested inside the FrameLayout?
Yes, your ConstrainLayout will be nested in FrameLayout, just open the Layout Inspector(Tools -> Layout Inspector) and you will see the following:

What can be the difference of using a fragment and frameLayout in android? Can both be used interchangeably?

I have seen an approach where frameLayout is used in case of fragments. The ultimate goal was to have multiple fragments.
For showing a single Fragment immediately on the screen, yes, you can use fragment or FrameLayout interchangeably.
Single Fragment, Method 1
Showing the Fragment via the fragment tag would look like this in XML:
<fragment class="com.example.ExampleFragment"
android:id="#+id/details" android:layout_weight="1"
android:layout_width="0px" android:layout_height="match_parent" />
Single Fragment, Method 2
Showing the Fragment via FrameLayout would look like this in XML:
<FrameLayout android:id="#+id/details" android:layout_weight="1"
android:layout_width="0px" android:layout_height="match_parent" />
Followed by Java code like this:
Fragment newFragment = new ExampleFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.details, newFragment);
transaction.addToBackStack(null);
transaction.commit();
Multiple Fragments
Method 2 then supports changing what fragment you are showing later by running more Java code to change what Fragment is there afterwards:
Fragment secondFragment = new SecondExampleFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.details, secondFragment);
transaction.addToBackStack(null);
transaction.commit();
So FrameLayout gives you the extra ability to do that over using the fragment tag.
A framelayout, Relative View and a few others represents a view in android and is extended from viewgroup.
A Fragment is a an an Object that is used to represent a portion of a user interface and is usually hosted in an activity.
A fragment has a viewgroup which you can assign an XML layout. In the XML you can specify a viewgroup which can be a framelayout if you wish to represent the layout of the viewgroup within the fragment.
Fragments and framelayouts cannot be used interchangeably.
Having said that, you can create a Android application without the use of fragments, and just use viewgroups.

Multiple view with same resource id in android

I have only one listview in a fragment with other views.
As shown in the view dump below, for some reason, there are two listviews in the hierarchy (with same resource id).
The unpopulated listview (top one) here I think, masks my populated listview.
What is this listview (that is selected in the screenshot) and how can I remove it/find its origin.
My code for this fragment looks like:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#color/dark"
android:orientation="vertical">
<include layout="#layout/empty_view"/>
<include layout="#layout/progress_bar"/>
<com.application.custom.CustomListView
android:id="#+id/main_list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
UPDATE:
The activity simply loads the fragments:
HomeFrag homeFragment = new HomeFrag();
getSupportFragmentManager().beginTransaction()
.add(R.id.homelist_fragment_container, homeFragment, "home_fragment")
.commit();
Here we are using add to add the fragment, without checking if the fragment exists. This resulted in multiple view hierarchies with same resource ids.
Adding the following check to see if the fragment already exists, around the add fragment code fixes this:
if (getSupportFragmentManager().findFragmentByTag("my_frag_tag") == null) {
//add fragment with tag "my_frag_tag"
HomeFrag homeFragment = new HomeFrag();
getSupportFragmentManager().beginTransaction()
.add(R.id.homelist_fragment_container, homeFragment, "my_frag_tag")
.commit();
}
This also ensures that the fragment isn't created when there is no need to create it (unlike replace).

Android overlapping layouts

I has a Sliding ViewPager with about 5 pages. Each layout is inflated like this:
public static class SectionFragment extends Fragment {
...
#Override
public View onCreateView(LayoutInflater inflater, ...) {
...
rootView = inflater.inflate(R.layout.gridpage1,container,false);
...
}
Now I want to check if a condition is true and if Yes, I want to inflate the gridpage1 layout first, then another layout on top of it.
How can i do this? All I need is the help on inflating two views one on top of the other.
Inflating a view basically just means creating it from an XML file and returning it.
In your specific case, you just need to return your fragment content view from the onCreateView function. This must be a single view, so if your condition is true and you want 2 views do the following:
Create a FrameLayout view yourself programmatically
Something like: FrameLayout frameLayout = new FrameLayout(context);
Add the 1st view to your FrameLayout after inflating it
Either do frameLayout.addView(inflater.inflate(R.layout.gridpage1,frameLayout,false));
or even inflater.inflate(R.layout.gridpage1,frameLayout,true); is enough because true tells it to add the view to the container.
Add the 2nd view to your FrameLayout after inflating it
Return the FrameLayout from your onCreateView
Addition:
How to save the reference to each view:
Option 1:
View v1 = inflater.inflate(R.layout.gridpage1,frameLayout,false);
this.v1Reference = v1;
frameLayout.addView(v1);
Option 2:
inflater.inflate(R.layout.gridpage1,frameLayout,true);
this.v1Reference = frameLayout.findViewById(...);
You can use the <include /> tag in your main layout and then hide/show the views you want with setVisibility(View.GONE/VISIBLE).
For example:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/hello_world" />
<include android:id="#+id/gridpage1_layout" layout="#layout/gridpage1"/>
<include android:id="#+id/gridpage2_layout" layout="#layout/gridpage2"/>
...
</RelativeLayout>
And in your Fragment, you can inflate only the root layout and find the other views by ID.

Android fragments overlap previous view and button listeners

I have an activity A with a fragment A inside.
Activity A uses layout X, and fragment A uses layout A.
code of layout X:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<fragment
android:id="#+id/fragment1"
android:name="android.app.DialogFragment"
android:layout_width="wrap_content"
android:layout_height="500dp"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="113dp"
class="com.example.fragtester.FragA" />
</RelativeLayout>
Layout A is just textview + linearlayout.
I set up another fragment B that uses layout B.
Now that I use the following code in activity A to change the fragments:
Fragment f = new FragB();
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.fragment1, f);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
ft.addToBackStack(null);
ft.commit();
I end up having layout B displaying under layout A.
So I use a FrameLayout to wrap the fragment in layout X and use
ft.replace(R.id.FrameLayout1, f);
Now the view is working nicely. Though, another problem arises.
Although layout B covers layout A, but the buttons are still active.
That means when I am viewing layout B, I can still click buttons on layout A, even if I am not seeing it.
And even when I add fragment C/D/E..... (layouts C/D/E....), the buttons on layout A is still active.
Can anybody explain why is that? Am I using fragments wrongly? Thanks!
A way to get through is to make layout A blank, and use other layout to cover it. But it doesn't seems to be the "right" way??
Remove the fragment and add a FrameLayout
<FrameLayout
android:id="#+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffffff" >
</FrameLayout>
then add fragments programmatically.
In android fragment button click pass through the fragments (i dont know if the fragments are suppose to work like that). what I used to do in such a situation is to make the layout of the fragment clickable. so the clicks wont pass through.
Instead of having fragment in your xml, try to create empty container for a fragments. For example empty frame layout. And then programmatically put your fragments in there.
Add the following attribute to the XML root layout of the fragment that goes on top.
android:clickable="true"
This will ensure that touch events will not propagate further than the top layer.

Categories

Resources