I wanted to see if anyone has had success with customization of tabs using FragmentTabHost that comes with the new Android API level 17.
I was excited to be able to nest a tabHost within my ViewPager SherlockFragments, but I'm having trouble doing simple things like moving the tabs to the bottom or changing the layout of the tabs.
Has anyone seen a good example of using this functionality?
This is the only example I could find in the Android docs, and theres just about nothing that describes its use. It also seems to ignore whatever is defined in the layout for R.id.fragment1
.
My question I suppose would be if anyone has come across a good tutorial re:FragmentTabHost or if they have an idea about how to a) put the nested tabs at the bottom or b) change the layout of said tabs.
I've tried all the usual methods, but since it appears the XML layout file is overridden, I haven't had much luck.
private FragmentTabHost mTabHost;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
setContentView(R.layout.fragment_tabs);
mTabHost = (FragmentTabHost)findViewById(android.R.id.tabhost);
mTabHost.setup(this, getSupportFragmentManager(), R.id.realtabcontent);
mTabHost.addTab(mTabHost.newTabSpec("simple").setIndicator("Simple"),
FragmentStackSupport.CountingFragment.class, null);
mTabHost.addTab(mTabHost.newTabSpec("contacts").setIndicator("Contacts"),
LoaderCursorSupport.CursorLoaderListFragment.class, null);
mTabHost.addTab(mTabHost.newTabSpec("custom").setIndicator("Custom"),
LoaderCustomSupport.AppListFragment.class, null);
mTabHost.addTab(mTabHost.newTabSpec("throttle").setIndicator("Throttle"),
LoaderThrottleSupport.ThrottledLoaderListFragment.class, null);
return mTabHost;
}
After doing some research, it appears there may be a glitch with initializing the FragmentTabHost in the support library. The user here on Google code has provided a suggestion to this:
FragmentTabHost.java
private void initFragmentTabHost(Context context, AttributeSet attrs) {
TypedArray a = context.obtainStyledAttributes(attrs,
new int[] { android.R.attr.inflatedId }, 0, 0);
mContainerId = a.getResourceId(0, 0);
a.recycle();
super.setOnTabChangedListener(this);
// If owner hasn't made its own view hierarchy, then as a convenience
// we will construct a standard one here.
/***** HERE COMMENT CODE BECAUSE findViewById(android.R.id.tabs) EVERY TIME IS NULL WE HAVE OWN LAYOUT ******//
// if (findViewById(android.R.id.tabs) == null) {
// LinearLayout ll = new LinearLayout(context);
// ll.setOrientation(LinearLayout.VERTICAL);
// addView(ll, new FrameLayout.LayoutParams(
// ViewGroup.LayoutParams.FILL_PARENT,
// ViewGroup.LayoutParams.FILL_PARENT));
//
// TabWidget tw = new TabWidget(context);
// tw.setId(android.R.id.tabs);
// tw.setOrientation(TabWidget.HORIZONTAL);
// ll.addView(tw, new LinearLayout.LayoutParams(
// ViewGroup.LayoutParams.FILL_PARENT,
// ViewGroup.LayoutParams.WRAP_CONTENT, 0));
//
// FrameLayout fl = new FrameLayout(context);
// fl.setId(android.R.id.tabcontent);
// ll.addView(fl, new LinearLayout.LayoutParams(0, 0, 0));
//
// mRealTabContent = fl = new FrameLayout(context);
// mRealTabContent.setId(mContainerId);
// ll.addView(fl, new LinearLayout.LayoutParams(
// LinearLayout.LayoutParams.FILL_PARENT, 0, 1));
// }
}
XML Layout for fragment:
<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="@android:id/tabcontent"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="0"/>
<FrameLayout
android:id="@+id/realtabcontent"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
<TabWidget
android:id="@android:id/tabs"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="0"/>
</LinearLayout>
</android.support.v4.app.FragmentTabHost>
I think, it was a mistake to set method
initFragmentTabHost()
to constructor. At that time TabHost don't his children - it happens after.LinearLayout
, for example, work with his children inonMeasure()
method (grepcode).ViewGroup
in constructor just init variables, and setmChildrenCount = 0
(grepcode).All what I can did, it's only costumize
FragmentTabHost
:And costumize
Tabs
(have problems with tab heights, I solve them in code):In code:
I think other customization with
TabWidget
we can do only with programmatically manipulating, like this:IMHO, this is not good.
I finally got to the bottom of this. There is an issue with FragmentTabHost.java which will always create a TabHost element for you, no matter what you define in XML and inflate beforehand.
As such, I commented out that part of code when writing my own version of FragmentTabHost.java.
Make sure to use your new version of this in your XML layout,
<com.example.app.MyFragmentTabHost
And of course inflate it:
Fragment1.java:
MyFragmentTabHost.java:
as far as i tested jamisOn solution is good. It is important to not initialize MyFragmentTabHost with its constructor. At least if the class holding the MyFragmentTabHost is a fragment. I haven`t tested with a FragmentActivity...
I'd like to mention some more issues with FragmentTabHost. I'm using a ViewPager where each page (View) contains a FragmenTabHost and I had to overcome several problems:
1) FragmentTabHost assumes that it's the only FragmentTabHost in its parent FragmentManager (2nd argument to
FragmentTabHost.setup()
). This causes the rest of the problems...2) the "tags" you provide when calling
addTab()
are passed straight through to the FragmentManager, so if you just use hardcoded tags for all your pages (a perfectly reasonable thing to do) your first page will create tab fragments while every other page will reuse those tabs. Yes, page 2 controls page 1...Solution is to generate unique tag names. I appended the page number to the hardcoded strings:
3) All tab fragments get placed in a container identified only by "view id" (the 3rd argument to
FragmentTabHost.setup()
). This means that when the FragmentManager resolves the viewId to a View, it always finds the first instance (from the first page). All your other pages are ignored.Solution to this is to assign unique ids to your "tab content" views, for example:
4) It doesn't remove tab fragments when destroyed. While the ViewPager destroys unused Views as you swipe, the FragmentTabHosts contained within those views "leak" the tab fragments. When the ViewPager re-instantiates a previously seen page (using previously used tags), FragmentTabHost will notice that the fragments for those tabs already exist and simply reattach them. This blows up because the fragments point to views that have been destroyed by the ViewPager.
The solution is to remove fragments when FragmentTabHost is destroyed. You'll want to add this code to
onDetachedFromWindow()
in your local copy of FragmentTabHost.javaYou could probably also work around these issues by using a FragmentPagerAdapter or FragmentStatePagerAdapter (makes Fragments) instead of a standard PagerAdapter (makes Views). Then you'd call
FragmentTabHost.setup( ... fragment.getChildFragmentManager() ... )
.