Fragment是3.0以後才出現的類別庫, 因此要下載官方的support library.
如果要下載官方的Support library, 你要先打開eclipse->window->Android SDK Manager
接著你就會看到以下畫面-> 選擇Extra的Android Support下載
然後在你的android sdk資料夾之下,看到多出一個extras的資料夾, 以我的資料夾為例子 \android-sdk-windows\extras\android\support\v4\android-support-v4.jar, 這個就是我們所需要的support library.
當我們建立一個專案的時候, 只要在專案下面在建立一個lib, 然後把android-support-v4.jar加進去,
接著點選專案右鍵, 選擇properties->Libraries->Add Library 將lib資料夾內的android-support-v4.jar加進去。
記得要到Order and Export那邊將android-support-v4.jar打勾。
如此一來就可以使用這個library的所有類別了。
官方說明如下
我們可以看到這個類別庫大致上有以下這幾類
Fragment
FragmentManager
FragmentTransaction
ListFragment
DialogFragment
LoaderManager
Loader
AsyncTaskLoader
CursorLoader
另外這邊有一個重點就是, 為了讓之前Android版本能夠跑我們的程式,
可以在AndroidManifest.xml上面進行設定。
<uses-sdk android:minSdkVersion="4" />
這樣一來, 無論手機上的android版本多舊, 我們仍然可以跑我們要的程式。
接下來進入我們的重點了, 如何使用TabActivity,
其實在我們下載android-support-v4.jar裡面, 就已經存在很多範例了,
但是為了簡化他的範例, 因此我用官網所提供的範例進行簡化。
首先新增一個fragment_tabs.xml
<TabHostxmlns:android="http://schemas.android.com/apk/res/android"android:id="@android:id/tabhost"t" android:layout_height="match_parandroid:layout_width="match_pare nent"> <LinearLayout android:orientation="vertical"t_height="match_parent"> <FrameLayandroid:layout_width="match_parent" android:layo uout android:id="@android:id/tabcontent" android:layout_width="0dp"ameLayout android:id="@+andandroid:layout_height="0dp" android:layout_weight="0"/> <F rroid:id/realtabcontent" android:layout_width="match_parent" android:layout_height="0dp"ight="wrap_content" androidandroid:layout_weight="1"/> <HorizontalScrollView android:layout_h e:layout_width="fill_parent" android:scrollbars="none" android:id="@+id/scroller"> <TabWidgetnt" android:layout_height="wrandroid:id="@android:id/tabs" android:orientation="horizontal" android:layout_width="match_par eap_content" android:layout_weight="0"/> </HorizontalScrollView> </LinearLayout> </TabHost>
這邊我們建立一個TabHost的元件, 然後我想讓TabWidget能夠呈現在下方,
因此用LinearLayout包起來,
讓上方為一個FrameLayout(橙色部分), 下方才是我們的TabWidget(藍色部分)。
首先要建立一個FragmentActivity的子類別,
public class FragmentTabs extends FragmentActivity{@Overridevoid onCreate(Bundle savedInstanceState) { superprotected .onCreate(savedInstanceState); } }
基本上跟一般的Activity沒什麼不一樣, 也是先建立一個onCreate的方法。
接著我們就把TabWidget元件從xml取出, 並且初始化。
public class FragmentTabs extends FragmentActivity{private TabHost mTabHost; @OverrideanceState) { super.onCreate(savedInstanceState);protected void onCreate(Bundle savedIns t setContentView(R.layout.fragment_tabs);.id.tabhost); mTabHost.setup(); } }mTabHost = (TabHost)findViewById(android.R
在這邊要注意一下, 在之前我們的類別是繼承TabActivity,
因此我們常常只需要getTabHost(); 就可以取得TabHost的物件了,
原因出在我們的TabActivity裡面就對TabHost元件初始化了,
但是現在是從xml取出物件, 因此要多加上setup()這個方法,
才能夠進行add的動作。
官網說法
Call setup() before adding tabs if loading TabHost using findViewById(). However: You do not need to call setup() after getTabHost() in TabActivity.
可以參考看看
接著我們必須宣告一個TabManager來處理當切換分頁的時候, 必須進行的一些動作。
類別長這樣:
public class TabManager implements TabHost.OnTabChangeListener {private final FragmentActivity mActivity;e final int mContainerId; privaprivate final TabHost mTabHost; priva tte final HashMap<String, TabInfo> mTabs = new HashMap<String, TabInfo>();al String tag;TabInfo mLastTab; static final class TabInfo { private fi n private final Class<?> clss; private final Bundle args;ass<?> _class, Bundle _args) {private Fragment fragment; TabInfo(String _tag, C l tag = _tag; clss = _class; args = _args; } }l Context mContext; public DummyTabFactory(Context context) {static class DummyTabFactory implements TabHost.TabContentFactory { private fin a mContext = context; } @Override public View createTabContent(String tag) { View v = new View(mContext);y, TabHost tabHost, int containerv.setMinimumWidth(0); v.setMinimumHeight(0); return v; } } public TabManager(FragmentActivity activi tId) { mActivity = activity; mTabHost = tabHost; mContainerId = containerId; mTabHost.setOnTabChangedListener(this); }= tabSpec.getTag(); TabInfo info = new TabInfo(tag, clss, args);public void addTab(TabHost.TabSpec tabSpec, Class<?> clss, Bundle args) { tabSpec.setContent(new DummyTabFactory(mActivity)); String ta g info.fragment = mActivity.getSupportFragmentManager().findFragmentByTag(tag); if (info.fragment != null && !info.fragment.isDetached()) {(); } mTabs.put(tag, info); mTabHost.addTab(tabSpec); } @OveFragmentTransaction ft = mActivity.getSupportFragmentManager().beginTransaction(); ft.detach(info.fragment); ft.commi trride public void onTabChanged(String tabId) { TabInfo newTab = mTabs.get(tabId); if (mLastTab != newTab) { FragmentTransaction ft = mActivity.getSupportFragmentManager().beginTransaction();ab.fragment = Fragment.instantiate(if (mLastTab != null) { if (mLastTab.fragment != null) { ft.detach(mLastTab.fragment); } } if (newTab != null) { new TmActivity, newTab.clss.getName(), newTab.args); ft.add(mContainerId, newTab.fragment, newTab.tag); if (newTab.fragment == null) { ft.detach(mLastTab.fragment);}} else { mActivity.getSupportFragmentManager().popBackStack(); ft.replace(mContainerId, newTab.fragment); ft.attach(newTab.fragment); } mLastTab = newTab; ft.commit(); mActivity.getSupportFragmentManager().executePendingTransactions(); } } }
超複雜嗎? 你也可以不用管這個類別直接拿過用即可, 新建一個TabManager.java, 然後把上面內容複製貼上就可以直接使用了!
那麼以下內容你可以直接跳過
******************** 跳過 ******************************
基本上是照著官網上所寫的來進行,
你會發現在這個類別當中, 存在兩個靜態類別, 分別是TabInfo以及DummyTabFactory。
static final class TabInfo {private final String tag;private final Class<?> clss;private final Bundle args;TabInfo(String _tag, Clasprivate Fragment fragment; s<?> _class, Bundle _args) {rgs; } }tag = _tag; clss = _class; args = _a
TabInfo就是每一個分頁當中存在的資訊, 例如該分頁的名稱、傳到哪一個類別以及傳過去的時候, 所帶的Bundle。
static class DummyTabFactory implements TabHost.TabContentFactory {private final Context mContext;ontext) { mContext = context; }public DummyTabFactory(Context c @Override public View createTabContent(String tag) {(0); v.setMinimumHeight(0);View v = new View(mContext); v.setMinimumWidt h return v; } }
DummyTabFactory目的是用來創造一個分頁所形成的View, 當你按下任一分頁, 它就會根據對應的View來進行切換。
public TabManager(FragmentActivity activity, TabHost tabHost, int containerId) {mActivity = activity; mTabHost = tabHost;TabChangedListener(this); }mContainerId = containerId; mTabHost.setOn
一開始從建構子傳入的就是該FragmentActivity, 再來就是TabHost物件, 最後是一個容器的id,
這個容器就是用來裝我們切換分頁的view, 還記得fragment_tabs.xml當中, 我們用了兩個FrameLayout嗎? 這個就是放在上面的那個, 你可以對照一下後面我們會傳入的id, 就可以清楚的明白這個layout的功用。
public void addTab(TabHost.TabSpec tabSpec, Class<?> clss, Bundle args) {tabSpec.setContent(new DummyTabFactory(mActivity));ew TabInfo(tag, clss, args);String tag = tabSpec.getTag(); TabInfo info = ninfo.fragment = mActivity.getSupportFragmentManager().findFragmentByTag(tag);tTransaction ft = mActivity.getSupportFragmentManager().beginTrif (info.fragment != null && !info.fragment.isDetached()) { Fragme nansaction(); ft.detach(info.fragment); ft.commit(); } mTabs.put(tag, info); mTabHost.addTab(tabSpec); }
這邊就是要新增一個分頁, 就必須傳入TabHost.TabSpec物件, 以及你要轉跳的java檔案, 到時候會傳到那個Fragment, 最後是如果你有需要將此Activity的一些內容(如String, int..等等)的資料傳到指定的分頁, 就可以在最後一個參數利用Bundle物件夾帶, 如果沒有, 則可以指定null。
那麼一開始他就將TabSpec的物件帶入new出來的DummyTabFactory, 方便這個分頁的內容呈現一個View。
接著又把傳入的參數丟進去TabInfo物件, 完成初始化。
當我們要進行切換fragment的時候, 就會利用FragmentTransaction的物件來進行調度,而當commit出去的時候, 就可以進行變更。
當我們進行detach動作的時候, 就表示指定該fragment會出現在某一個View當中。
當我們進行detach動作的時候, 就表示指定該fragment會從該View當中分離。
當我們進行replace動作的時候, 才是表示將某一個fragment轉換成另外一個fragment。
官網寫法
@Overridepublic void onTabChanged(String tabId) {TabInfo newTab = mTabs.get(tabId);ragmentTransaction ft = mActiif (mLastTab != newTab) { Fvity.getSupportFragmentManager().beginTransaction();b.fragment != null) {if (mLastTab != null) { if (mLastT a ft.detach(mLastTab.fragment); } }nt == null) {if (newTab != null) { if (newTab.fragm e newTab.fragment = Fragment.instantiate(mActivity,gs); ft.add(mContainerId, newTab.fragmennewTab.clss.getName(), newTab.a rt, newTab.tag); } else { ft.attach(newTab.fragment); } }gTransactions(); } }mLastTab = newTab; ft.commit(); mActivity.getSupportFragmentManager().executePendin
一開始先判斷按下的分頁是不是前一個分頁, 如果不是, 則進行處理,
那一開始我們就會把前一個分頁內容做檢查, 看是不是空的,
如果不是空的, 則把前一個分頁從分頁管理者的內容中移除,
再來就是檢查我們新的分頁是否有內容, 如果沒有,
我們就new一個空間讓他建立新的分頁,
然後加入到分頁管理者的儲存器當中,
如果有內容, 則直接指派該分頁是我們目前要呈現的分頁,
最後確定以後, 就把新的分頁跟舊的分頁設定為同一個分頁,
接著送交, 進行更新, 接著執行分頁轉換。
但是! 官網的做法讓我產生一些問題, 因此我做了修改,
大致上如下:
@Overridepublic void onTabChanged(String tabId) {TabInfo newTab = mTabs.get(tabId);FragmentTransaction ft = mif (mLastTab != newTab) { Activity.getSupportFragmentManager().beginTransaction();astTab.fragment != null) {if (mLastTab != null) { if (m L ft.detach(mLastTab.fragment); } }.instantiate(mActivity,if (newTab != null) { newTab.fragment = Fragmen t newTab.clss.getName(), newTab.args);newTab.tag); if (newTab.fragment == null) {ft.add(mContainerId, newTab.fragment , ft.detach(mLastTab.fragment); } else {ft.attach(newTab.fragment); }ft.replace(mContainerId, newTab.fragment); } mLastTab = newTab; ft.commit();} }mActivity.getSupportFragmentManager().executePendingTransactions();
這樣就可以成功解決我的問題。
如果這邊讓你覺得看不太懂, 沒關係, 直接Copy&Paste過去就好XD
********************************* 到這邊繼續 *******************************
接著我們在onCreate就可以使用這個類別來操作我們的TabHost物件了。
public class FragmentTabs extends FragmentActivity{private TabHost mTabHost;ager; @Override protected vprivate TabManager mTabMa noid onCreate(Bundle savedInstanceState) {setContentView(R.layout.fragment_tsuper.onCreate(savedInstanceState) ;abs); mTabHost = (TabHost)findViewById(android.R.id.tabhost);TabHost, R.id.realtabcontmTabHost.setup(); mTabManager = new TabManager(this, ment); } }
這樣基本TabHost的物件就初始化完成了。
讓我們利用TabManager類別的物件來操作這個TabHost吧!
public class FragmentTabs extends FragmentActivity{private TabHost mTabHost;ager; @Override protected vprivate TabManager mTabMa noid onCreate(Bundle savedInstanceState) {setContentView(R.layout.fragment_tsuper.onCreate(savedInstanceState) ;abs); mTabHost = (TabHost)findViewById(android.R.id.tabhost);TabHost, R.id.realtabcontmTabHost.setup(); mTabManager = new TabManager(this, ment); mTabHost.setCurrentTab(0);//設定一開始就跳到第一個分頁 mTabManager.addTab(ragment1.class, null); mTabManager.addTab( mTabHostmTabHost.newTabSpec("Fragment1").setIndicator("Fragment1"), F.newTabSpec("Fragment2").setIndicator("Fragment2"), Fragment2.class, null); mTabManager.addTab(TabManager.addTab( mTabHost.newTabSpec("Fragment4").setIndimTabHost.newTabSpec("Fragment3").setIndicator("Fragment3"), Fragment3.class, null); mcator("Fragment4"), Fragment4.class, null); } }
我們加入了四個子類別, 分別是Fragment1~Fragment4,
這樣代表在切換分頁的時候, 會根據指定的分別跳置不同的Fragment。
在這邊我們必須再新增4個java檔案, 檔名當然就是Fragment1~Fragment4。
在eclipse點右鍵,
選擇sourece->override/overload 就可以找到onCreate&onCreateView這兩個方法了!
public class Fragment1 extends Fragment {@OverrideonCreate(Bundle savedInstanceState) {public void // TODO Auto-generated method stubsuper.onCreate(savedInstanceState);} @OverrideateView(LayoutInflater inflater, ViewGroup container, Bundle savedpublic View onCr eInstanceState) {// TODO Auto-generated method stubreturn super.onCreateView(inflater, container, savedInstanceState);} }
其他的四個類別也相同。
那麼跑看看模擬器, 就會出現這樣。
怎麼會擠在一團咧XD
沒關係, 做一下調整。
public class FragmentTabs extends FragmentActivity{private TabHost mTabHost;ager; @Override protected vprivate TabManager mTabMa noid onCreate(Bundle savedInstanceState) {setContentView(R.layout.fragment_tsuper.onCreate(savedInstanceState) ;abs); mTabHost = (TabHost)findViewById(android.R.id.tabhost);TabHost, R.id.realtabcontmTabHost.setup(); mTabManager = new TabManager(this, ment); mTabHost.setCurrentTab(0); mTabManager.addTab(, Fragment1.class, null); mTabManager.addTab(mTabHost.newTabSpec("Fragment1").setIndicator("Fragment1" ) mTabHost.newTabSpec("Fragment2").setIndicator("Fragment2"), Fragment2.class, null); mTabManager.addTab(ab( mTabHost.newTabSpec("Fragment4").setIndicator("FragmentmTabHost.newTabSpec("Fragment3").setIndicator("Fragment3"), Fragment3.class, null); mTabManager.add T4"), Fragment4.class, null); DisplayMetrics dm = new DisplayMetrics();//先取得螢幕解析度getWindowManager().getDefaultDisplay().getMetrics(dm);int screenWidth = dm.widthPixels; //取得螢幕的寬dget tabWidget = mTabHost.getTabWidget(); //取得tab的物件TabW iint count = tabWidget.getChildCount(); //取得tab的分頁有幾個if (count > 3) {for (int i = 0; i < count; i++) {tabWidget.getChildTabViewAt(i)idth)/3); //設定每一個分頁最小的寬度.setMinimumWidth((screen W}} } }
一開始就先取得螢幕的解析度, 接著由於我們是x軸要加寬, 所以只要取得螢幕的寬度即可,
再來算出我們一共用了幾個分頁, 假設你只想要呈現3個分頁, 因此就把螢幕的寬度除以3個分頁, 這樣一來, 畫面就會把第4個分頁放在螢幕外面, 當你需要第4個分頁的時候, 只需要拉動某一個分頁, 就會出現其他被隱藏的分頁了。
你看是不是好多了?
只要往左拉, 第4個分頁就出現了。
可是總覺得少了那麼一點東西, 加個icon好了。
public class FragmentTabs extends FragmentActivity{private TabHost mTabHost;ager; @Override protected vprivate TabManager mTabMa noid onCreate(Bundle savedInstanceState) {setContentView(R.layout.fragment_tsuper.onCreate(savedInstanceState) ;abs); mTabHost = (TabHost)findViewById(android.R.id.tabhost);TabHost, R.id.realtabcontmTabHost.setup(); mTabManager = new TabManager(this, ment); mTabHost.setCurrentTab(0); mTabManager.addTab(this.getResources().getDrawable( android.mTabHost.newTabSpec("Fragment1").setIndicator("Fragment1" ,R.drawable.ic_dialog_alert)),Fragment1.class, null); mTabManager.addTab(this.getResources().getDrawable( android.R.drawable.imTabHost.newTabSpec("Fragment2").setIndicator("Fragment2", c_lock_lock)),Fragment2.class, null); mTabManager.addTab( mTabHost.newTabSpec("Fragment3").setIndicator("Fragment3",.addTab( mTabHost.newTabSpec("Frthis.getResources().getDrawable( android.R.drawable.ic_input_add)),Fragment3.class, null); mTabManage ragment4").setIndicator("Fragment4", this.getResources().getDrawable( android.R.drawable.ic_delete)),Fragment4.class, null); DisplayMetrics dm = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(dm);//先取得螢幕解析度int screenWidth = dm.widthPixels; //取得螢幕的寬idget tabWidget = mTabHost.getTabWidget(); //取得tab的物件Tab Wint count = tabWidget.getChildCount(); //取得tab的分頁有幾個if (count > 3) {for (int i = 0; i < count; i++) {tabWidget.getChildTabViewAt(i)idth)/3); //設定每一個分頁最小的寬度.setMinimumWidth((screen W}} } }
圖是我從android內建的icon找的, 你可以加入你喜歡的圖示。
看起來越來越有模樣了XD