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).
Related
The attached picture shows a ViewPager with 3 pages. Each page is implemented as a Fragment. The Toolbar has a + icon in black for adding a new patient. When the + icon is pressed, a nested fragment should be created inside the Patients page which allows the creation of a new patient.
In the Logcat, I can see that the fragment lifecycle callbacks for the nested fragment are called right upto onResume(), but the view for the nested fragment is not rendered.
I am doing this to create the nested fragment and insert it into the parent fragment view hierarchy:
FrPatientDetails frChild = new FrPatientDetails();
FragmentTransaction transaction = getChildFragmentManager().beginTransaction()
.replace(R.id.flPatientsPage, frChild);
transaction.addToBackStack(null)
.commit();
R.id.flPatientsPage is a FrameLayout in the Patients page into which I am trying to insert the nested fragment.
I am getting a feeling that the way to insert a child fragment into a parent fragment hosted in a ViewPager might be different, but I am not clear what to do.
Would someone be able to help here.
Update: Added the xml for the Patients page (parent)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/patients_page_linear_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<ListView
android:id="#+id/lvHomeViewPatientsPage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:divider="#color/colorPrimary"
android:dividerHeight="1dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="8dp"
android:textColor="#android:color/black" />
<TextView
android:id="#+id/tvEmpty"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:textColor="#android:color/black"
android:text="#string/patients_page_empty"
android:fontFamily="sans-serif"
android:gravity="center"
android:textSize="16sp"
/>
<FrameLayout
android:id="#+id/flPatientsPage"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
Update: This is the screenshot after following what #Filo suggested:
To summarize, here is the problem that I was facing:
There is an Activity A containing a TabLayout and ViewPager(V). V contains 3 pages (fragments). One of the fragments(F1) contains a child fragment C. I was able to display F1. I was unable to display C when the user clicked a button in F1.
Solution: V used a FragmentPagerAdapter to setup the fragments. Inside FragmentPagerAdapter.getItem(), do not launch F1 directly. Instead, launch a a fragment called F1Wrapper which contains a FrameLayout. Inside F1Wrapper.onCreateView(), create a FragmentTransaction adding F1 to the backstack. Use getChildFragmentManager to create the transaction. Pass the FrameLayout of F1Wrapper as the view container for F1.
When the user clicks the button in F1 to launch fragment C, create a FragmentTransaction.replace() in F1Wrapper which adds C to the backstack. Use getChildFragmentManager to create the transaction. Again, pass the FrameLayout of F1Wrapper as the view container for C. C will now replace F1 in the backstack and will be displayed.
Note: You cannot pass the ViewPager as the view container for C. That is the crux of the problem and the main reason why we need to use a wrapper fragment. If you pass V as the view container for C, C.onCreateView() will crash.
My activity declares all of its GUI fragments in a single XML layout. It only needs to display a few of the fragments at launch time; the rest get shown as the user interacts with the app. A portion of the layout is as follows:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<fragment
android:id="#+id/map_panel"
android:name="com.example.MapPanel"
android:layout_width="match_parent"
android:layout_height="#dimen/map_panel_height" />
<fragment
android:id="#+id/list_panel"
android:name="com.example.ListPanel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/map_panel" />
<fragment
android:id="#+id/detail_panel"
android:name="com.example.DetailPanel"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#+id/map_panel"
android:visibility="gone" />
My intention is that the list_panel fragment is visible at startup, and the detail_panel fragment is hidden until the user selects something from the list.
By default, a fragment starts out with the isHidden attribute as false. That means my activity has to iterate through the loaded fragments and manually call isHidden(true) on fragments like detail_panel at startup time.
I would prefer to declare the isHidden status in the XML layout. However, setting android:visibility="gone" in a <fragment> declaration does not change the isHidden status, and I can't find any documentation on another attribute that would do the trick.
Is it possible to set an XML attribute on a <fragment> to cause it to be hidden?
Note: I'm not concerned with view visibility, I'm concerned with the fragment.isHidden() value. That affects how FragmentManager manipulates the back stack and performs animations. If you call transaction.show(fragment) on a fragment whose view is invisible or gone, but the fragment.isHidden() value is false, then the FragmentManager will not make the view visible. See http://developer.android.com/reference/android/app/Fragment.html#isHidden() for reference.
I faced a similar situation, where I had to hide a fragment.
I simply included the fragment inside a LinearLayout and marked the layout to be visible/gone.
<LinearLayout
android:id="#+id/map_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="visible" >
<fragment
android:id="#+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.google.android.gms.maps.MapFragment" />
</LinearLayout>
Based off Jyo's post, use this:
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.hide(mFragment);
fragmentTransaction.commit();
This has worked for me on API Level 23. mFragment is the fragment that you want to hide.
This answer is a tad late thought it may be helpful for future reference. Visibility is part of the View class - Fragment extends object though not having access to the visibility values. A possibility is making the Fragment a child of a FrameLayout and calling invisible or gone on the layout. This will cause the fragment to appear to be hidden.
Hope it helps!
public void showHideFrgament(final Fragment fragment){
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.setCustomAnimations(android.R.animator.fade_in,
android.R.animator.fade_out);
if (fragment.isHidden()) {
ft.show(fragment);
Log.d("hidden","Show");
} else {
ft.hide(fragment);
Log.d("Shown","Hide");
}
ft.commit();
}
we have isVisible Method for fragment
seeting visibilty to Gone does not take any space
Where as Invisble takes the actual view space.
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.
I have several fragments within one Activity.
I am trying to make my app compatible with both small and large screens.
I have created a main layout with a LinearLayout as the root. This LinearLayout contains two FrameLayouts. One FrameLayout is used to store Fragments which will store lists or any other side details. I only want this to be in view when specific buttons are pressed.
The other FrameLayout is used to display the main part of the app (a map) which is in its own fragment.
To begin with I add my main map fragment using:
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.add(R.id.viewer, viewModeFragment);
ft.commit();
When I want the side panel to appear with a list fragment I call something like this:
FrameLayout fl = (FrameLayout)findViewById(R.id.list);
fl.setVisibility(View.VISIBLE);
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.add(R.id.list, editOsmInfoFragment);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.addToBackStack(null);
ft.show(editOsmInfoFragment);
ft.commit();
Here is my XML file for the main Activity layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/myFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<FrameLayout
android:id="#+id/list"
android:name="com.srose.cyclopathed.view.LoadRoutesFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:visibility="gone"/>
<FrameLayout
android:id="#+id/viewer"
android:name="com.srose.cyclopathed.view.ViewModeFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2" />
</LinearLayout>
This seems to work some what ok but the main problem is that if I use the app on the tablet and the side bar appears with a list fragment contained within it, if the back button is pressed the fragment vanishes as expected but the blank list FrameLayout remains on screen because it was not part of the transaction.
I guess I am not using this properly but I have no idea how to implement it so that the whole side bar whole slide to the left in the back button is pressed.
Can anyone please help?
Thanks
Do not explicitly set the visibility on R.id.list.
In your layout XML, remove the android:visiblility attribute on R.id.list FrameLayout to make it visible. Since this FrameLayout is initially empty, it will not show up on screen. When the side panel is added to it programmatically via the FragmentTransaction, you will see it, and when it is removed (via the back button), it will go away. You must call FrameLayout# setConsiderGoneChildrenWhenMeasuring() in order for the layout to collapse when the Fragment is removed.
Good day, trying to clarify this: if i have this layout for a portrait layout
main.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<fragment
android:id="#+id/configFragment_id"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.example.settingsFragment">
</fragment>
and this for landscape(mainly for tablets), main.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<fragment
android:id="#+id/configFragment_id"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.example.SettingsFragment" ></fragment>
<fragment
android:id="#+id/detailFragment_id"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.Example.DetailFragment" >
<!-- Preview: layout=#layout/details -->
</fragment>
</LinearLayout>
now within my SettingFragment, i implement a button to go to the DetailFragment:
DetailFragment fragment = (DetailFragment)getActivity().getSupportFragmentManager().findFragmentById(R.id.detailFragment_id);
FragmentTransaction fragmentTransaction = getActivity().getSupportFragmentManager().beginTransaction();
if (fragment != null && fragment.isInLayout()) {
DetailFragment detailfragment = new WallpaperFragment();
fragmentTransaction.add(R.id.detailFragment_id, detailfragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
}else{
Intent intent = new Intent(getActivity().getApplicationContext(),DetailActivity.class);
startActivity(intent);
}
Am i now implementing a dynamic fragment or am i still using static fragment?.. or do i only use dynamic fragment when i use a viewgroup like FrameLayout in place of the fragment tag element?
Idea is to have a single pane for phones and multi-pane for tablets.I know its a bad idea to mix static and dynamic fragments, but its kind of confusing. Thank you.
You know, I might be wrong here (so feel free to correct me) but it looks like you're using both static AND dynamic fragments. In your XML layouts you've added the fragments then you are re-instancing them via a fragment transaction in your activity. Alternatively, if you had declared a container (FrameLayout for instance) instead of a fragment in your XML you would have to use a FragmentTransaction to add the fragment at run time.
In your example you are just stepping on the fragment that you all ready have. Basically, the OS sees your fragment in the XML when inflating (I think that's when it calls the fragment code?) and executes the code associated with it via the tag in the fragment. Your app then adds that same fragment over the top of itself. A simple way to show this is to add a LogCat call in your Fragment class's onCreateView method. If you see it more than once, then your stepping on the previous fragment with the same fragment (which I'm 99% sure you are). Hope this helps to answer your question!
Static fragments are defined in the layout and are not generally added or removed at runtime. They are referenced by their id in your code. They are generally put as a child element of a layout like below. Once they are defined here, android will know to make a fragment, that's all you have to do.
<LinearLayout 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"
android:orientation="vertical"
android:weightSum="1">
<fragment
android:id="#+id/fooFragment"
android:name="com.example.myapplication.TestFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
Dynamic fragments are defined in your code and can be manipulated, added, removed, etc during runtime. They look like this:
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.add(R.id.your_placeholder, new TestFragment());
ft.commit();