I'm pretty new to Android and wanted some help. My app has two tabs (as fragments) in a ViewPager. One of the tabs shows a list of items. On clicking on an item, the app should display the details of the item.
Now how do I this? Do I add the details in a third fragment? If so, how should I add the fragment? I do not want the details fragment to come as a tab inside ViewPager.
I tried adding the details fragment as a separate fragment to the ViewPager. On clicking an item, the fragment is being called (the log statements inside are displayed in console). However, I just get a blank screen.
This is my activity.xml:
<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</android.support.v4.view.ViewPager>
This is the xml for the details fragment:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="600dp"
android:layout_height="700dp"
android:background="#FFFFFF"
android:orientation="vertical" >
...
</LinearLayout>
This is how I'm currently adding the details fragment from the Activity:
DetailsFragment details = new DetailsFragment();
android.support.v4.app.FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.pager, details);
transaction.addToBackStack(null);
transaction.commit();
Thanks in advance.
You should open a new activity to display the details. This new activity will simply be a container for your DetailsFragment.
You need to pass the details to display to the Activity in its Intent, then the Activity will pass the details to the Fragment using the arguments Bundle (Fragment.setArguments()). Most of the time, this will be a simple object implementing Parcelable.
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.
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).
Trying to create Facebook / Gmail style Sliding Navigation Drawer. All I want is to create separate Fragments in XML, and show them when user clicks one of the list items from Drawer Menu. Each Fragment hooked up to one item in the list.
NavigationDrawer is great example app to start with, but it only demos loading fragment dynamically. I want even simpler, just loading those statically. How should (Code snippet please) I be instantiating Fragments within my activity on List menu item click? How would MainActivity XML look like ?
Please read the docs http://developer.android.com/guide/components/fragments.html
XML
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="+#id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>
Java
FragmentManager fragmentManager = getFragmentManager()
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
ExampleFragment fragment = new ExampleFragment();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();
I want to do something like that. An picture is easier than words:
And when the user clicks on a button on the Fragment B, the fragment B changed but not the A.
I've made two different layout (A portrait and a land one). The first one has a layout like
<?xml version="1.0" encoding="utf-8"?>
<fragment
xmlns:android="http://schemas.android.com/apk/res/android"
android:name="com.my.app.ContactsFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/contacts_fragment" />
I've a button in my fragment layout with a simple activity call:
Intent intent = new Intent(getActivity(), NextActivity.class);
startActivity(intent);
And the land one is like that:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
xmlns:android="http://schemas.android.com/apk/res/android"
android:name="com.my.app.ContactsFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/contacts_fragment" />
<fragment
xmlns:android="http://schemas.android.com/apk/res/android"
android:name="com.my.app.HomeFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/fragment_container" />
</LinearLayout>
I change the inner fragment using the following code:
FragmentTransaction ft = getActivity().getFragmentManager().beginTransaction();
NextFragment nextFrag = new NextFragment();
ft.replace(R.id.fragment_container, nextFrag);
ft.addToBackStack(null);
ft.commit();
This part works well.
I have two questions now:
How to put these two ways to change the content in the main activity? I mean, in the main activity, a fragment should call the second way, but in a normal activity, I need to call the first.
If I click on an item in the fragment A, and then I click on the button in fragment B that changes the fragment to NextFragment. If I click on another item and i do the same. I can go back to the first user. Is there a way to dump the stack when clicking on a new item ?
Thanks for your help.
Ps: I'm using the native fragment lib not the support v4.
I'm struggling to understand the specifics of your 2 questions, as they are vague and do not give enough detail.
However, since you want your Fragments to be changed out at runtime, you should not put <fragment/> in your layout files. Your current architecture makes it impossible to change out the Fragments in your layouts, which is not what you want.
Note: When you add a fragment to an activity layout by defining the fragment in the layout XML file, you cannot remove the fragment at runtime. If you plan to swap your fragments in and out during user interaction, you must add the fragment to the activity when the activity first starts.
You should be using FrameLayout containers for your Fragments in your layout files, and have a single Activity add Fragments to those FrameLayout containers depending on if they are there. This will allow the app to create 1 Fragment in portrait and 2 Fragments in landscape (given you have have a layout for each orientation). This will also allow you to be able to swap out Fragments as your please, and add them to the back stack.
And example of this Google recommended approach can be found here.
You can find a good solution from here.
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.