@act262
2016-11-21T16:23:51.000000Z
字数 4524
阅读 1191
Android
FragmentTabHost extends TabHost
基本功能继承了TabHost,Content使用Fragment展示。实现底部、顶部Tab,其他区域展示内容的效果。
// ContentView
private FrameLayout mRealTabContent;
private Context mContext;
private FragmentManager mFragmentManager;
// ContentView的ID
private int mContainerId;
private TabHost.OnTabChangeListener mOnTabChangeListener;
// 上次切换的Tab信息
private TabInfo mLastTab;
private boolean mAttached;
实现了TabHost.TabContentFactory
Factory
static class DummyTabFactory implements TabHost.TabContentFactory {
private final Context mContext;
public DummyTabFactory(Context context) {
mContext = context;
}
@Override
public View createTabContent(String tag) {
// 这个貌似没有用到,真实的ContentView是mRealTabContent,其ID就是mContainerId
View 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);
// 指定inflatedId
mContainerId = a.getResourceId(0, 0);
a.recycle();
super.setOnTabChangedListener(this);
}
确保有TabWidget和ConentView
// 自动生成ViewTree,上面是TabWidget,下面是Content
private 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));
}
}
// 确保必须有指定的mContainerId
private 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);
}
}
@Nullable
private 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">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:id="@android:id/tabcontent"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<TabWidget
android: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 中设置的inflatedId
tabHost.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);