在之前的TabActivity使用中,
會產生很多問題(例如Back問題、切換activity的時候狀態儲存問題...等等),
因此Android開發了一個新的類別Fragment,
利用這個類別來開發TabActivity,
可以解決掉上述那些問題,
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的所有類別了。
官方說明如下
http://developer.android.com/tools/extras/support-library.html#Downloading
我們可以看到這個類別庫大致上有以下這幾類
Fragment
FragmentManager
FragmentTransaction
ListFragment
DialogFragment
LoaderManager
Loader
AsyncTaskLoader
CursorLoader
另外這邊有一個重點就是,
為了讓之前Android版本能夠跑我們的程式,
可以在AndroidManifest.xml上面進行設定
<uses-sdk android:minSdkVersion="4" />
這樣一來, 無論手機上的android版本多舊, 我們仍然可以跑我們要的程式。
接下來進入我們的重點了,
如何使用TabActivity,
其實在我們下載android-support-v4.jar裡面, 就已經存在很多範例了,
但是為了簡化他的範例,
因此我用官網所提供的範例進行簡化。
http://developer.android.com/reference/android/app/TabActivity.html
首先新增一個fragment_tabs.xml
fragment_tabs.xml
<TabHost 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="@+android:id/realtabcontent" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1"/> <HorizontalScrollView android:layout_height="wrap_content" android:layout_width="fill_parent" android:scrollbars="none" android:id="@+id/scroller"> <TabWidget android:id="@android:id/tabs" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="0"/> </HorizontalScrollView> </LinearLayout> </TabHost>
這邊我們建立一個TabHost的元件, 然後我想讓TabWidget能夠呈現在下方,
因此用LinearLayout包起來,
讓上方為一個FrameLayout(橙色部分),
下方才是我們的TabWidget(藍色部分)。
首先要建立一個FragmentActivity的子類別,
public class FragmentTabs extends FragmentActivity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}
基本上跟一般的Activity沒什麼不一樣, 也是先建立一個onCreate的方法。
接著我們就把TabWidget元件從xml取出,
並且初始化。
public class FragmentTabs extends FragmentActivity{ private TabHost mTabHost; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.fragment_tabs); mTabHost = (TabHost)findViewById(android.R.id.tabhost); mTabHost.setup(); } }
在這邊要注意一下, 在之前我們的類別是繼承TabActivity,
因此我們常常只需要getTabHost();
就可以取得TabHost的物件了,
原因出在我們的TabActivity裡面就對TabHost元件初始化了,
但是現在是從xml取出物件,
因此要多加上setup()這個方法,
才能夠進行add的動作。
可以參考看看
http://developer.android.com/reference/android/widget/TabHost.html#setup()
接著我們必須宣告一個TabManager來處理當切換分頁的時候, 必須進行的一些動作。
類別長這樣:
public class TabManager implements TabHost.OnTabChangeListener { private final FragmentActivity mActivity; private final TabHost mTabHost; private final int mContainerId; private final HashMap<String, TabInfo> mTabs = new HashMap<String, TabInfo>(); TabInfo mLastTab; static final class TabInfo { private final String tag; private final Class<?> clss; private final Bundle args; private Fragment fragment; TabInfo(String _tag, Class<?> _class, Bundle _args) { tag = _tag; clss = _class; args = _args; } } static class DummyTabFactory implements TabHost.TabContentFactory { private final Context mContext; public DummyTabFactory(Context context) { mContext = context; } @Override public View createTabContent(String tag) { View v = new View(mContext); v.setMinimumWidth(0); v.setMinimumHeight(0); return v; } } public TabManager(FragmentActivity activity, TabHost tabHost, int containerId) { mActivity = activity; mTabHost = tabHost; mContainerId = containerId; mTabHost.setOnTabChangedListener(this); } public void addTab(TabHost.TabSpec tabSpec, Class<?> clss, Bundle args) { tabSpec.setContent(new DummyTabFactory(mActivity)); String tag = tabSpec.getTag(); TabInfo info = new TabInfo(tag, clss, args); info.fragment = mActivity.getSupportFragmentManager().findFragmentByTag(tag); if (info.fragment != null && !info.fragment.isDetached()) { FragmentTransaction ft = mActivity.getSupportFragmentManager().beginTransaction(); ft.detach(info.fragment); ft.commit(); } mTabs.put(tag, info); mTabHost.addTab(tabSpec); } @Override public void onTabChanged(String tabId) { TabInfo newTab = mTabs.get(tabId); if (mLastTab != newTab) { FragmentTransaction ft = mActivity.getSupportFragmentManager().beginTransaction(); if (mLastTab != null) { if (mLastTab.fragment != null) { ft.detach(mLastTab.fragment); } } if (newTab != null) { newTab.fragment = Fragment.instantiate(mActivity, 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, 然後把上面內容複製貼上就可以直接使用了!
那麼以下內容你可以直接跳過
******************** 跳過
******************************
基本上是照著官網上所寫的來進行,
http://developer.android.com/reference/android/app/TabActivity.html
你會發現在這個類別當中, 存在兩個靜態類別, 分別是TabInfo以及DummyTabFactory。
static final class TabInfo { private final String tag; private final Class<?> clss; private final Bundle args; private Fragment fragment; TabInfo(String _tag, Class<?> _class, Bundle _args) { tag = _tag; clss = _class; args = _args; } }
TabInfo就是每一個分頁當中存在的資訊, 例如該分頁的名稱、傳到哪一個類別以及傳過去的時候, 所帶的Bundle。
static class DummyTabFactory implements TabHost.TabContentFactory { private final Context mContext; public DummyTabFactory(Context context) { mContext = context; } @Override public View createTabContent(String tag) { View v = new View(mContext); v.setMinimumWidth(0); v.setMinimumHeight(0); return v; } }
DummyTabFactory目的是用來創造一個分頁所形成的View, 當你按下任一分頁, 它就會根據對應的View來進行切換
public TabManager(FragmentActivity activity, TabHost tabHost, int containerId) { mActivity = activity; mTabHost = tabHost; mContainerId = containerId; mTabHost.setOnTabChangedListener(this); }
一開始從建構子傳入的就是該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)); String tag = tabSpec.getTag(); TabInfo info = new TabInfo(tag, clss, args); info.fragment = mActivity.getSupportFragmentManager().findFragmentByTag(tag); if (info.fragment != null && !info.fragment.isDetached()) { FragmentTransaction ft = mActivity.getSupportFragmentManager().beginTransaction(); 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當中分離。
當我們進行replace動作的時候,
才是表示將某一個fragment轉換成另外一個fragment
官網寫法
@Override public void onTabChanged(String tabId) { TabInfo newTab = mTabs.get(tabId); if (mLastTab != newTab) { FragmentTransaction ft = mActivity.getSupportFragmentManager().beginTransaction(); if (mLastTab != null) { if (mLastTab.fragment != null) { ft.detach(mLastTab.fragment); } } if (newTab != null) { if (newTab.fragment == null) { newTab.fragment = Fragment.instantiate(mActivity, newTab.clss.getName(), newTab.args); ft.add(mContainerId, newTab.fragment, newTab.tag); } else { ft.attach(newTab.fragment); } } mLastTab = newTab; ft.commit(); mActivity.getSupportFragmentManager().executePendingTransactions(); } }
一開始先判斷按下的分頁是不是前一個分頁, 如果不是, 則進行處理,
那一開始我們就會把前一個分頁內容做檢查, 看是不是空的,
如果不是空的,
則把前一個分頁從分頁管理者的內容中移除,
再來就是檢查我們新的分頁是否有內容,
如果沒有,
我們就new一個空間讓他建立新的分頁,
然後加入到分頁管理者的儲存器當中,
如果有內容,
則直接指派該分頁是我們目前要呈現的分頁,
最後確定以後, 就把新的分頁跟舊的分頁設定為同一個分頁,
接著送交, 進行更新,
接著執行分頁轉換。
但是! 官網的做法讓我產生一些問題, 因此我做了修改,
大致上如下:
@Override public void onTabChanged(String tabId) { TabInfo newTab = mTabs.get(tabId); if (mLastTab != newTab) { FragmentTransaction ft = mActivity.getSupportFragmentManager().beginTransaction(); if (mLastTab != null) { if (mLastTab.fragment != null) { ft.detach(mLastTab.fragment); } } if (newTab != null) { newTab.fragment = Fragment.instantiate(mActivity, newTab.clss.getName(), newTab.args); ft.add(mContainerId, newTab.fragment, newTab.tag); if (newTab.fragment == null) { ft.detach(mLastTab.fragment); } else { ft.replace(mContainerId, newTab.fragment); ft.attach(newTab.fragment); } } mLastTab = newTab; ft.commit(); mActivity.getSupportFragmentManager().executePendingTransactions(); } }
這樣就可以成功解決我的問題。
如果這邊讓你覺得看不太懂, 沒關係,
直接Copy&Paste過去就好XD
********************************* 到這邊繼續
*******************************
接著我們在onCreate就可以使用這個類別來操作我們的TabHost物件了。
public class FragmentTabs extends FragmentActivity{ private TabHost mTabHost; private TabManager mTabManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.fragment_tabs); mTabHost = (TabHost)findViewById(android.R.id.tabhost); mTabHost.setup(); mTabManager = new TabManager(this, mTabHost, R.id.realtabcontent); } }
這樣基本TabHost的物件就初始化完成了。
讓我們利用TabManager類別的物件來操作這個TabHost吧!
public class FragmentTabs extends FragmentActivity{ private TabHost mTabHost; private TabManager mTabManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.fragment_tabs); mTabHost = (TabHost)findViewById(android.R.id.tabhost); mTabHost.setup(); mTabManager = new TabManager(this, mTabHost, R.id.realtabcontent); mTabHost.setCurrentTab(0);//設定一開始就跳到第一個分頁 mTabManager.addTab( mTabHost.newTabSpec("Fragment1").setIndicator("Fragment1"), Fragment1.class, null); mTabManager.addTab( mTabHost.newTabSpec("Fragment2").setIndicator("Fragment2"), Fragment2.class, null); mTabManager.addTab( mTabHost.newTabSpec("Fragment3").setIndicator("Fragment3"), Fragment3.class, null); mTabManager.addTab( mTabHost.newTabSpec("Fragment4").setIndicator("Fragment4"), Fragment4.class, null); } }
我們加入了四個子類別, 分別是Fragment1~Fragment4,
這樣代表在切換分頁的時候,
會根據指定的分別跳置不同的Fragment。
在這邊我們必須再新增4個java檔案,
檔名當然就是Fragment1~Fragment4。
在eclipse點右鍵,
選擇sourece->override/overload
就可以找到onCreate&onCreateView這兩個方法了!
public class Fragment1 extends Fragment {
@Override
public void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
return super.onCreateView(inflater, container, savedInstanceState);
}
}
其他的四個類別也相同。
那麼跑看看模擬器, 就會出現這樣。
怎麼會擠在一團咧XD
沒關係, 做一下調整。
public class FragmentTabs extends FragmentActivity{ private TabHost mTabHost; private TabManager mTabManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.fragment_tabs); mTabHost = (TabHost)findViewById(android.R.id.tabhost); mTabHost.setup(); mTabManager = new TabManager(this, mTabHost, R.id.realtabcontent); mTabHost.setCurrentTab(0); mTabManager.addTab( mTabHost.newTabSpec("Fragment1").setIndicator("Fragment1"), Fragment1.class, null); mTabManager.addTab( mTabHost.newTabSpec("Fragment2").setIndicator("Fragment2"), Fragment2.class, null); mTabManager.addTab( mTabHost.newTabSpec("Fragment3").setIndicator("Fragment3"), Fragment3.class, null); mTabManager.addTab( mTabHost.newTabSpec("Fragment4").setIndicator("Fragment4"), Fragment4.class, null); DisplayMetrics dm = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(dm); //先取得螢幕解析度 int screenWidth = dm.widthPixels; //取得螢幕的寬 TabWidget tabWidget = mTabHost.getTabWidget(); //取得tab的物件 int count = tabWidget.getChildCount(); //取得tab的分頁有幾個 if (count > 3) { for (int i = 0; i < count; i++) { tabWidget.getChildTabViewAt(i) .setMinimumWidth((screenWidth)/3);//設定每一個分頁最小的寬度 } } } }
一開始就先取得螢幕的解析度, 接著由於我們是x軸要加寬, 所以只要取得螢幕的寬度即可,
再來算出我們一共用了幾個分頁, 假設你只想要呈現3個分頁,
因此就把螢幕的寬度除以3個分頁, 這樣一來, 畫面就會把第4個分頁放在螢幕外面, 當你需要第4個分頁的時候, 只需要拉動某一個分頁,
就會出現其他被隱藏的分頁了。
你看是不是好多了?
只要往左拉, 第4個分頁就出現。
可是總覺得少了那麼一點東西, 加個icon好了。
public class FragmentTabs extends FragmentActivity{ private TabHost mTabHost; private TabManager mTabManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.fragment_tabs); mTabHost = (TabHost)findViewById(android.R.id.tabhost); mTabHost.setup(); mTabManager = new TabManager(this, mTabHost, R.id.realtabcontent); mTabHost.setCurrentTab(0); mTabManager.addTab( mTabHost.newTabSpec("Fragment1").setIndicator("Fragment1", this.getResources().getDrawable( android.R.drawable.ic_dialog_alert)),Fragment1.class, null); mTabManager.addTab( mTabHost.newTabSpec("Fragment2").setIndicator("Fragment2", this.getResources().getDrawable( android.R.drawable.ic_lock_lock)),Fragment2.class, null); mTabManager.addTab( mTabHost.newTabSpec("Fragment3").setIndicator("Fragment3", this.getResources().getDrawable( android.R.drawable.ic_input_add)),Fragment3.class, null); mTabManager.addTab( mTabHost.newTabSpec("Fragment4").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; //取得螢幕的寬 TabWidget tabWidget = mTabHost.getTabWidget(); //取得tab的物件 int count = tabWidget.getChildCount(); //取得tab的分頁有幾個 if (count > 3) { for (int i = 0; i < count; i++) { tabWidget.getChildTabViewAt(i) .setMinimumWidth((screenWidth)/3);//設定每一個分頁最小的寬度 } } } }
圖是我從android內建的icon找的, 你可以加入你喜歡的圖示。
看起來越來越有模樣了XD
原文地址:http://givemepass.blogspot.tw/2012/07/tabactivity.html

浙公网安备 33010602011771号