@act262
2016-11-21T16:23:51.000000Z
字数 4524
阅读 1317
Android
FragmentTabHost extends TabHost
基本功能继承了TabHost,Content使用Fragment展示。实现底部、顶部Tab,其他区域展示内容的效果。
// ContentViewprivate FrameLayout mRealTabContent;private Context mContext;private FragmentManager mFragmentManager;// ContentView的IDprivate int mContainerId;private TabHost.OnTabChangeListener mOnTabChangeListener;// 上次切换的Tab信息private TabInfo mLastTab;private boolean mAttached;
实现了TabHost.TabContentFactoryFactory
static class DummyTabFactory implements TabHost.TabContentFactory {private final Context mContext;public DummyTabFactory(Context context) {mContext = context;}@Overridepublic View createTabContent(String tag) {// 这个貌似没有用到,真实的ContentView是mRealTabContent,其ID就是mContainerIdView v = new View(mContext);v.setMinimumWidth(0);v.setMinimumHeight(0);return v;}}
初始化
private void initFragmentTabHost(Context context, AttributeSet attrs) {final TypedArray a = context.obtainStyledAttributes(attrs,new int[] { android.R.attr.inflatedId }, 0, 0);// 指定inflatedIdmContainerId = a.getResourceId(0, 0);a.recycle();super.setOnTabChangedListener(this);}
确保有TabWidget和ConentView
// 自动生成ViewTree,上面是TabWidget,下面是Contentprivate void ensureHierarchy(Context context) {// If owner hasn't made its own view hierarchy, then as a convenience// we will construct a standard one here.if (findViewById(android.R.id.tabs) == null) {LinearLayout ll = new LinearLayout(context);ll.setOrientation(LinearLayout.VERTICAL);addView(ll, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT));TabWidget tw = new TabWidget(context);tw.setId(android.R.id.tabs);tw.setOrientation(TabWidget.HORIZONTAL);ll.addView(tw, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_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.MATCH_PARENT, 0, 1));}}// 确保必须有指定的mContainerIdprivate void ensureContent() {if (mRealTabContent == null) {mRealTabContent = (FrameLayout)findViewById(mContainerId);if (mRealTabContent == null) {throw new IllegalStateException("No tab content FrameLayout found for id " + mContainerId);}}}
如果布局中的FragmentTabHost有指定android:inflatedId则使用这个方法初始化,一般是使用布局设置的情况。
setup(Context context, FragmentManager manager)
或者指定一个containerId,一般用在java代码中写布局的情况。
setup(Context context, FragmentManager manager, int containerId)
切换Tab,通过FragmentManager的事物来管理的
public void onTabChanged(String tabId) {if (mAttached) {final FragmentTransaction ft = doTabChanged(tabId, null);if (ft != null) {ft.commit();}}if (mOnTabChangeListener != null) {mOnTabChangeListener.onTabChanged(tabId);}}@Nullableprivate FragmentTransaction doTabChanged(@Nullable String tag,@Nullable FragmentTransaction ft) {final TabInfo newTab = getTabInfoForTag(tag);if (mLastTab != newTab) {if (ft == null) {ft = mFragmentManager.beginTransaction();}if (mLastTab != null) {if (mLastTab.fragment != null) {ft.detach(mLastTab.fragment);}}if (newTab != null) {if (newTab.fragment == null) {newTab.fragment = Fragment.instantiate(mContext,newTab.clss.getName(), newTab.args);ft.add(mContainerId, newTab.fragment, newTab.tag);} else {ft.attach(newTab.fragment);}}mLastTab = newTab;}return ft;}
每次新旧的切换是使用detach和attach,所以会造成Fragment的生命周期(onCreatedView)重复执行,导致每次都会重新加载布局等问题。
解决方案有2个:
1. 重写doTabChanged方法,使用FragmentTransaction的show/hide来切换显示、隐藏。
2. 在Fragment的onCreateView中判断这个rootView是否已经存在,不存在才加载View。
布局中使用,基本上和TabHost一样,只是把TabHost控件换成了FragmentTabHost
<?xml version="1.0" encoding="utf-8"?><android.support.v4.app.FragmentTabHost xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><FrameLayoutandroid:id="@android:id/tabcontent"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1" /><TabWidgetandroid:id="@android:id/tabs"android:layout_width="match_parent"android:layout_height="48dp" /></LinearLayout></android.support.v4.app.FragmentTabHost>
在Java代码中设置
FragmentTabHost tabHost = (FragmentTabHost) findViewById(android.R.id.tabhost);// 指定content id// tabHost.setup(this, getSupportFragmentManager(), android.R.id.tabcontent);// content id = xml 中设置的inflatedIdtabHost.setup(this, getSupportFragmentManager());TabHost.TabSpec tabSpec1 = tabHost.newTabSpec("TabA").setIndicator("TabA");TabHost.TabSpec tabSpec2 = tabHost.newTabSpec("TabB").setIndicator("TabB");tabHost.addTab(tabSpec1, Tab1Fragment.class, null);tabHost.addTab(tabSpec2, Tab2Fragment.class, null);