FragmentTabHost.java revision 0adacc1aa313d757ae1c517152cef838e0f35c13
1/* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.support.v4.app; 18 19import java.util.HashMap; 20 21import android.content.Context; 22import android.content.res.TypedArray; 23import android.os.Bundle; 24import android.util.AttributeSet; 25import android.view.View; 26import android.view.ViewGroup; 27import android.widget.FrameLayout; 28import android.widget.LinearLayout; 29import android.widget.TabHost; 30import android.widget.TabWidget; 31 32/** 33 * Special TabHost that allows the use of {@link Fragment} objects for 34 * its tab content. When placing this in a view hierarchy, after inflating 35 * the hierarchy you must call {@link #setup(Context, FragmentManager, int)} 36 * to complete the initialization of the tab host. 37 */ 38public class FragmentTabHost extends TabHost 39 implements TabHost.OnTabChangeListener { 40 private final HashMap<String, TabInfo> mTabs = new HashMap<String, TabInfo>(); 41 private FrameLayout mRealTabContent; 42 private Context mContext; 43 private FragmentManager mFragmentManager; 44 private int mContainerId; 45 private TabHost.OnTabChangeListener mOnTabChangeListener; 46 private TabInfo mLastTab; 47 48 static final class TabInfo { 49 private final String tag; 50 private final Class<?> clss; 51 private final Bundle args; 52 private Fragment fragment; 53 54 TabInfo(String _tag, Class<?> _class, Bundle _args) { 55 tag = _tag; 56 clss = _class; 57 args = _args; 58 } 59 } 60 61 static class DummyTabFactory implements TabHost.TabContentFactory { 62 private final Context mContext; 63 64 public DummyTabFactory(Context context) { 65 mContext = context; 66 } 67 68 @Override 69 public View createTabContent(String tag) { 70 View v = new View(mContext); 71 v.setMinimumWidth(0); 72 v.setMinimumHeight(0); 73 return v; 74 } 75 } 76 77 public FragmentTabHost(Context context) { 78 // Note that we call through to the version that takes an AttributeSet, 79 // because the simple Context construct can result in a broken object! 80 super(context, null); 81 initFragmentTabHost(context, null); 82 } 83 84 public FragmentTabHost(Context context, AttributeSet attrs) { 85 super(context, attrs); 86 initFragmentTabHost(context, attrs); 87 } 88 89 private void initFragmentTabHost(Context context, AttributeSet attrs) { 90 TypedArray a = context.obtainStyledAttributes(attrs, 91 new int[] { android.R.attr.inflatedId }, 0, 0); 92 mContainerId = a.getResourceId(0, 0); 93 a.recycle(); 94 95 super.setOnTabChangedListener(this); 96 97 // If owner hasn't made its own view hierarchy, then as a convenience 98 // we will construct a standard one here. 99 if (findViewById(android.R.id.tabs) == null) { 100 LinearLayout ll = new LinearLayout(context); 101 ll.setOrientation(LinearLayout.VERTICAL); 102 addView(ll, new FrameLayout.LayoutParams( 103 ViewGroup.LayoutParams.FILL_PARENT, 104 ViewGroup.LayoutParams.FILL_PARENT)); 105 106 TabWidget tw = new TabWidget(context); 107 tw.setId(android.R.id.tabs); 108 tw.setOrientation(TabWidget.HORIZONTAL); 109 ll.addView(tw, new LinearLayout.LayoutParams( 110 ViewGroup.LayoutParams.FILL_PARENT, 111 ViewGroup.LayoutParams.WRAP_CONTENT, 0)); 112 113 FrameLayout fl = new FrameLayout(context); 114 fl.setId(android.R.id.tabcontent); 115 ll.addView(fl, new LinearLayout.LayoutParams(0, 0, 0)); 116 117 mRealTabContent = fl = new FrameLayout(context); 118 mRealTabContent.setId(mContainerId); 119 ll.addView(fl, new LinearLayout.LayoutParams( 120 LinearLayout.LayoutParams.FILL_PARENT, 0, 1)); 121 } 122 } 123 124 /** 125 * @deprecated Don't call the original TabHost setup, you must instead 126 * call {@link #setup(Context, FragmentManager)} or 127 * {@link #setup(Context, FragmentManager, int)}. 128 */ 129 @Override @Deprecated 130 public void setup() { 131 throw new IllegalStateException( 132 "Must call setup() that takes a Context and FragmentManager"); 133 } 134 135 public void setup(Context context, FragmentManager manager) { 136 super.setup(); 137 mContext = context; 138 mFragmentManager = manager; 139 ensureContent(); 140 } 141 142 public void setup(Context context, FragmentManager manager, int containerId) { 143 super.setup(); 144 mContext = context; 145 mFragmentManager = manager; 146 mContainerId = containerId; 147 ensureContent(); 148 mRealTabContent.setId(containerId); 149 } 150 151 private void ensureContent() { 152 if (mRealTabContent == null) { 153 mRealTabContent = (FrameLayout)findViewById(mContainerId); 154 if (mRealTabContent == null) { 155 throw new IllegalStateException( 156 "No tab content FrameLayout found for id " + mContainerId); 157 } 158 } 159 } 160 161 @Override 162 public void setOnTabChangedListener(OnTabChangeListener l) { 163 mOnTabChangeListener = l; 164 } 165 166 public void addTab(TabHost.TabSpec tabSpec, Class<?> clss, Bundle args) { 167 tabSpec.setContent(new DummyTabFactory(mContext)); 168 String tag = tabSpec.getTag(); 169 170 TabInfo info = new TabInfo(tag, clss, args); 171 172 // Check to see if we already have a fragment for this tab, probably 173 // from a previously saved state. If so, deactivate it, because our 174 // initial state is that a tab isn't shown. 175 info.fragment = mFragmentManager.findFragmentByTag(tag); 176 if (info.fragment != null && !info.fragment.isDetached()) { 177 FragmentTransaction ft = mFragmentManager.beginTransaction(); 178 ft.detach(info.fragment); 179 ft.commit(); 180 } 181 182 mTabs.put(tag, info); 183 addTab(tabSpec); 184 } 185 186 @Override 187 public void onTabChanged(String tabId) { 188 TabInfo newTab = mTabs.get(tabId); 189 if (mLastTab != newTab) { 190 FragmentTransaction ft = mFragmentManager.beginTransaction(); 191 if (mLastTab != null) { 192 if (mLastTab.fragment != null) { 193 ft.detach(mLastTab.fragment); 194 } 195 } 196 if (newTab != null) { 197 if (newTab.fragment == null) { 198 newTab.fragment = Fragment.instantiate(mContext, 199 newTab.clss.getName(), newTab.args); 200 ft.add(mContainerId, newTab.fragment, newTab.tag); 201 } else { 202 ft.attach(newTab.fragment); 203 } 204 } 205 206 mLastTab = newTab; 207 ft.commit(); 208 mFragmentManager.executePendingTransactions(); 209 } 210 if (mOnTabChangeListener != null) { 211 mOnTabChangeListener.onTabChanged(tabId); 212 } 213 } 214} 215