I have a project I am working on in Android. I have one big fragment that covers the whole of the screen in my main activity. Normally I for each ui screen the user uses I would just do a replace command and swap the fragments for each screen.
In my current one I want to put a tablayout/viewpager combo with three different fragments and depending on what tab is pushed I will show a new fragment. Problem with this is that is a tablayout showing three different fragments inside another fragment. Is this even possible or recommended? and if I have to change it to a new activity. Would the navigation drawer act normally and just shut as I loaded the activity or is there going to be a problem there as well.
Thanks for your help
You can check below official documentation for the same or can use my demo snippet:
https://developer.android.com/reference/android/support/v4/app/FragmentTabHost.html
Try to do this to handle the Tabs in main fragment:
public class MainFragment extends Fragment {
private FragmentTabHost mTabHost;
//Mandatory Constructor
public MainFragment() {
}
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_tabs,container, false);
mTabHost = (FragmentTabHost)rootView.findViewById(android.R.id.tabhost);
mTabHost.setup(getActivity(), getChildFragmentManager(), R.id.realtabcontent);
mTabHost.addTab(mTabHost.newTabSpec("fragmentb").setIndicator("Fragment B"),
FragmentB.class, null);
mTabHost.addTab(mTabHost.newTabSpec("fragmentc").setIndicator("Fragment C"),
FragmentC.class, null);
mTabHost.addTab(mTabHost.newTabSpec("fragmentd").setIndicator("Fragment D"),
FragmentD.class, null);
return rootView;
}
}
With this tabs layout:
<android.support.v4.app.FragmentTabHost
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#android:id/tabhost"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="#+id/realtabcontent"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
</LinearLayout>
Yes, TabLayout with viewPager will maintain it's different fragments in the memory.
Since, there are going to be only 3 tabs in your case it won't affect your memory performance, you can go with pagerAdapter.
You can reuse a single fragment for other tabs, but as i said since there are only 3 tabs, it won't affect your performance.
And Yes you can have tabLayout inside your main fragment, you main fragment is a part of your activity right, it will follow the same life cycle as the hosting activity except few other callbacks.
Related
I have the problem that the tab content does not change when I use tabHost.setCurrentTab(...). One can see, that the corresponding tab is marked as active, the content stays the same. When I click on the tab, the content changes.
To the Setup.
I have different functionalities in one app using fragments. One fragment contains a tabhost. Each tab has its own layout and the fragment should switch between those tabs and show a selection of tabs depending on the state of the app.
I'll try to quote the important code passages.
The main xml file of the fragment:
<TabHost
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#+id/tabHost"
xmlns:android="http://schemas.android.com/apk/res/android">
<TabWidget
android:id="#android:id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="fill"
android:background="#color/colorPrimary"/>
<FrameLayout
android:id="#android:id/tabcontent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<include layout="#layout/fragment_master_emg_start"
android:layout_height="445dp"
android:layout_width="fill_parent"
android:layout_gravity="center|bottom"/>
<include layout="#layout/fragment_master_emg_setup"
android:layout_height="445dp"
android:layout_width="fill_parent"
android:layout_gravity="center|bottom"/>
... some more includes
</FrameLayout>
</TabHost>
The xml files included just contain a relative layout and a bunch of buttons and textviews.
The Fragments onCreate Method:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Get View
View rootView = inflater.inflate(R.layout.fragment_master_emg, container, false);
Button button= (Button)rootView.findViewById(R.id.button);
button.setText("test");
TabHost tabHost=(TabHost)rootView.findViewById(R.id.tabHost);
tabHost.setup();
// Tabs
// Setup - Start Tab
TabHost.TabSpec t=tabHost.newTabSpec("Tab0");
t.setContent(R.id.master_emg_start);
t.setIndicator("EMG Measurement - Setup");
tabHost.addTab(t);
TabHost.TabSpec t0=tabHost.newTabSpec("Tab1");
t0.setContent(R.id.master_emg_setup);
t0.setIndicator("Setup");
tabHost.addTab(t0);
.... more tabs
....
// Set initial visibility
tabHost.getTabWidget().getChildAt(0).setVisibility(View.VISIBLE);
tabHost.getTabWidget().getChildAt(1).setVisibility(View.GONE);
// Buttons to switch tabs
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
TabHost tabHost=(TabHost)getActivity().findViewById(R.id.tabHost);
tabHost.getTabWidget().getChildAt(0).setVisibility(View.GONE);
tabHost.getTabWidget().getChildAt(1).setVisibility(View.VISIBLE);
tabHost.getTabWidget().setCurrentTab(1);
}
});
I've just included two tabs in this excerpt, but this would be the basic procedure.
I've tried to invalidade the tab host in order to have everythin redrawn. This did not change anything.
For anyone landing here for this problem solution:
Use :
tabHost.setCurrentTab(tabNumber);
Instead of :
tabHost.getTabWidget().setCurrentTab(tabNumber);
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.
I can't get my ViewPager not clipping its pages contents.
I'm currently using ViewPager with a FragmentStatePagerAdapter in my application. I have 3 pages, each page is showing a different fragment.
Everything works fine, but now I want to be able to draw outside of my pages bounds, like in this image:
I set clipChildren and clipToPadding to false in my all views parents to the views that needs to draw outside their bounds, but here's what I get : my views get clipped according to pages bounds.
Some additional info : In order to fix that issue, I used hierarchyviewer to check my view hierarchy and see if some "under the hood" views could have a clipping behaviour. And bingo : each of my fragments' root views is added under a NoSaveStateFrameLayout. I suspect this guy to clip its children...
Is my last assumption correct in your opinion? How would you do to get unclipped pages?
There are a few different Views that require disabled clipping. First, the FrameLayout that is at the root of the ViewPager needs to have clipping disabled. You can do this wherever you initialize your view pager. You also need to set the off-screen page limit to 2 to allow for an extra fragment to be loaded during a swipe. Without this, you'll see a pop going from 1->2 as 3 loads and adds it's overflow content to 2 once 2 has finished loading (this loads both 2 and 3 ahead of time).
mViewPager.setClipChildren(false);
mViewPager.setClipToPadding(false);
mViewPager.setOffscreenPageLimit(2);
Second, the XML you inflate in your FragmentStatePagerAdapter needs to include the appropriate tags or it can be done programmatically. Last, you need to force the NoSaveStateFrameLayout to not clip as well. This can be done after the view has been created in onViewCreated. Here's an example fragment:
public static class PlaceholderFragment extends Fragment {
public static PlaceholderFragment newInstance() {
PlaceholderFragment fragment = new PlaceholderFragment();
return fragment;
}
public PlaceholderFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
LinearLayout rootView = (LinearLayout) inflater.inflate(R.layout.fragment_main, container, false);
//This can be done in XML
rootView.setClipChildren(false);
rootView.setClipToPadding(false);
return rootView;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
//This is the NoSaveStateFrameLayout - force it to not clip
FrameLayout frameLayout = (FrameLayout) getView();
frameLayout.setClipChildren(false);
frameLayout.setClipToPadding(false);
}
}
I have tested this solution and am certain it works.
Full activity example:
https://gist.github.com/grantamos/d664f01a30f9ad97ba53
I had the same problem. I fixed this by adding
android:clipChildren="false"
android:clipToPadding="false"
to ViewPager and ViewGroup which is parent of ViewPager
For instance, if you have a layout like that:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:clipChildren="false"
android:clipToPadding="false"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.view.ViewPager
android:clipChildren="false"
android:clipToPadding="false"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
</FrameLayout>
</RelativeLayout>
then only LinearLayout and ViewPager should have
android:clipChildren="false"
android:clipToPadding="false"
Have you tried to use a negative value for the margin between pages (using ViewPager.setPageMargin(int))? You will also have to set offscreen page limit to at least 2 (using ViewPager.setOffscreenPageLimit(int)).
Try this:
#Override
public float getPageWidth(int position) {
return 0.80f;
}
This is working for me perfectly
1. (Parent Layouts).setClipChildren(false);
(Parent Layouts).setClipToPadding(false);
2. `mViewPager.setPagerMargin(dp)`
3. `mViewPager.setOffscreenPageLimit(numberOfPage);`
I have an Activity with a layout that contains 2 Fragments next to each other. In each Fragment, I have a ViewPager. Since I need both Fragments to look the same way, I use the same layout XML for both, which contains a ViewPager.
Now when I test the app, only the ViewPager of the first Fragment seems to work. The second Fragment shows a PagerTitleStrip, and I can flip through it, but it doesn't show the Fragments in the ViewPager.
Why isn't the second ViewPager showing the Fragments too? What is causing this?
Is the problem that they use the same layout? Isn't it a different Object anyway?
The layout that the 2 Fragments in the Activity share looks like this:
<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<android.support.v4.view.PagerTitleStrip
android:id="#+id/search_mapping_pager_title_strip"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:background="#33b5e5"
android:paddingBottom="4dp"
android:paddingTop="4dp"
android:textColor="#fff" />
</android.support.v4.view.ViewPager>
Both Fragments instantiate the ViewPager the same way:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_search_mapping,
container, false);
mSectionsPagerAdapter = new SectionsPagerAdapter(mActivity
.getSupportFragmentManager());
mViewPager = (ViewPager) view.findViewById(R.id.pager);
mViewPager.setAdapter(mSectionsPagerAdapter);
return view;
}
EDIT: More code showing my SectionsPagerAdapter:
public class SectionsPagerAdapter extends FragmentPagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
Fragment fragment = new SuperclassFragment();
Bundle args = new Bundle();
args.putInt(SuperclassFragment.ARG_SECTION_NUMBER, position + 1);
fragment.setArguments(args);
return fragment;
}
...
}
Any tips where I should look for the cause of the bug? I can of course include more code here if necessary.
I solved the problem myself by not putting the ViewPagers into Fragments, but putting them directly into the layout of the Activity.
The XML layout of the Activity then looks like this:
...
<LinearLayout
android:id="#+id/search_fragment_layout"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="horizontal">
<android.support.v4.view.ViewPager
android:id="#+id/objectPager"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="0.5"
android:layout_margin="16dp" >
<android.support.v4.view.PagerTitleStrip
android:id="#+id/object_pager_title_strip"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:background="#33b5e5"
android:paddingBottom="4dp"
android:paddingTop="4dp"
android:textColor="#fff" />
</android.support.v4.view.ViewPager>
<android.support.v4.view.ViewPager
android:id="#+id/actionPager"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="0.5"
android:layout_margin="16dp" >
<android.support.v4.view.PagerTitleStrip
android:id="#+id/action_pager_title_strip"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:background="#33b5e5"
android:paddingBottom="4dp"
android:paddingTop="4dp"
android:textColor="#fff" />
</android.support.v4.view.ViewPager>
</LinearLayout>
...
Using it in the Activity itself actually creates 2 distinct ViewPager objects, and both show their Fragments as expected.
I'm still not sure what caused the bug. If someone comes up with a good explanation, I'm willing to accept that as an answer.
FragmentPagerAdapter relies on a combination of ViewPager id/Page Item id to check if the page item needs to be instantiated (see FragmentPagerAdapter.instantiateItem function). If you place your ViewPager into a fragment, both instances of the View Pager will end up having the same id, and since page position is the default id for the page, pages for the second ViewPager are never instantiated.
The workaround is ether to set ViewPager id programmatically:
viewPager.setId(myID++);
or override getItemId on your adapter so item id's are different for different ViewPager's.
By putting ViewPager inside Fragment's layout, you are trying to add a child fragment inside a fragment. Nested fragment is only supported from 4.2 onwards and now its added in Support Library too.
In order to support View Pager insider a fragment, you need to implement your own version of PagerAdapter. From one of the SO post,
It is perfectly possible to put a ViewPager inside a Fragment, so long
as the contents of the ViewPager do not themselves contain fragments.
Since the concrete implementations of PagerAdapter supplied by the
Android Support package use fragments, you have to roll your own
fragment-less PagerAdapter to put the ViewPager in a fragment.
Below are few post which describe the problem and possible solution
Why it is not possible to use ViewPager within a Fragment? It actually is
ViewPager inside fragment issue
I'am build an app with an ActionBar and two Tabs below.
Everything works fine if the device / emulator isnt rotated. If rotated, tab state switches automaticale to tab1 (normal, because onCreate get called) but the content dont get changed. If I select a tab in the new orientation, the onCreateView() method from the selected Fragment get called but the view dont get updated (stay always the same). Any Tips?
The code.
Main Activity:
ActionBar actionbar = getActionBar();
actionbar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
ActionBar.Tab ATab = actionbar.newTab().setText(R.string.player);
ActionBar.Tab BTab = actionbar.newTab().setText(R.string.stations);
Fragment AFragment = new AFragment();
Fragment BFragment = new BFragment();
PlayerTab.setTabListener(new MyTabsListener(AFragment));
StationsTab.setTabListener(new MyTabsListener(BFragment));
actionbar.addTab(ATab);
actionbar.addTab(BTab);
With identical tabs that display a simple textview. The textview simple say which tab is selected.
Fragments:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.a, container, false);
}
The Fragment layout, mentioned above, only contains a TextView with hardcoded Text. (Only for testing purposes)
The Main layout looks like this.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<LinearLayout
android:id="#+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</LinearLayout>
</LinearLayout>
Solved. I have recreated the fragment everytime, doesnt do that anymore solved it.
Changed in my TabListener and onTabSelected(Tab tab, FragmentTransaction ft), ft.add() to ft.replace()