轉載請注明出處:http://blog.csdn.net/guolin_blog/article/details/13171191
現在fragment的應用真的是越來越廣泛了,之前android在3.0版本加入fragment的時候,主要是為了解決android pad螢幕比較大,空間不能充分利用的問題,但現在即使隻是在手機上,也有很多的場景可以運用到fragment了,今天我們就來學習其中一個特别棒的應用技巧。
很多手機應用都會有一個非常類似的功能,即螢幕的下方顯示一行tab标簽選項,點選不同的标簽就可以切換到不同的界面,如以下幾個應用所示:
上面三個應用從左到右分别是qq、新浪微網誌和支付寶錢包,可見,這種底部标簽式的布局政策真的非常常見。
那麼話說回來,這種效果到底是如何的呢?熟悉android的朋友一定都會知道,很簡單嘛,使用tabhost就ok了!但是殊不知,tabhost并非是那麼的簡單,它的可擴充性非常的差,不能随意地定制tab項顯示的内容,而且運作還要依賴于activitygroup。activitygroup原本主要是用于為每一個tabhost的子項管理一個單獨的activity,但目前已經被廢棄了。為什麼呢?當然就是因為fragment的出現了!檢視android官方文檔中activitygroup的描述,如下所示:
可以看到,在api 13的時候android就已經将activitygroup廢棄掉了,并且官方推薦的替代方式就是使用fragment,因為它使用起來更加的靈活。那麼剩下的問題就是如何借助fragment來完成類似于tabhost一般的效果了,是以我們自然要動起手來了。
在開始之前,首先你必須已經了解fragment的用法了,如果你對fragment還比較陌生的話,建議先去閱讀我前面的一篇文章 android
fragment完全解析,關于碎片你所需知道的一切 。
另外,我們還應該準備好程式所需要的資源,比如說每一個tab項中所用到的圖檔。我已經事先從qq裡截好了幾張圖作為這個項目的資源,稍後會連同源碼一起給出。
建立一個項目,起名就叫fragmentdemo,這裡我使用的是4.0的api。
下面開始程式設計工作,這裡我們首先需要去編寫一個類似于qq的主界面,當然隻會去編寫界面最下方的tabhost部分,而不會編寫上面的内容界面部分,因為内容界面是應該寫在fragment的布局裡的。打開或建立activity_main.xml作為程式的主布局檔案,在裡面加入如下代碼:
[html] view
plaincopy
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<framelayout
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" >
</framelayout>
<linearlayout
android:layout_height="60dp"
android:background="@drawable/tab_bg" >
<relativelayout
android:id="@+id/message_layout"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" >
<linearlayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centervertical="true"
android:orientation="vertical" >
<imageview
android:id="@+id/message_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:src="@drawable/message_unselected" />
<textview
android:id="@+id/message_text"
android:text="消息"
android:textcolor="#82858b" />
</linearlayout>
</relativelayout>
android:id="@+id/contacts_layout"
android:id="@+id/contacts_image"
android:src="@drawable/contacts_unselected" />
android:id="@+id/contacts_text"
android:text="聯系人"
android:id="@+id/news_layout"
android:id="@+id/news_image"
android:src="@drawable/news_unselected" />
android:id="@+id/news_text"
android:text="動态"
android:id="@+id/setting_layout"
android:id="@+id/setting_image"
android:src="@drawable/setting_unselected" />
android:id="@+id/setting_text"
android:text="設定"
</linearlayout>
</linearlayout>
這段布局代碼雖然有點長,但其實主要就分為兩部分。第一個部分就是framelayout,這裡隻是給framelayout的id設定成content,并沒有在裡面添加任何具體的内容,因為具體的内容是要在後面動态進行添加的。第二個部分就是framelayout下面的linearlayout,這個linearlayout中包含的就是整個類似于tabhost的布局。可以看到,我們将這個linearlayout又等分成了四份,每一份中都會顯示一個imageview和一個textview。imageview用于顯示目前tab的圖示,textview用于顯示目前tab的标題,這個效果就會和qq非常得類似。
既然是等分成了四分,那接下來我們自然要去分别實作四個fragment和它們的布局了。建立一個message_layout.xml作為消息界面的布局,代碼如下所示:
<?xml version="1.0" encoding="utf-8"?>
<relativelayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent" >
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerinparent="true"
android:orientation="vertical" >
<imageview
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:src="@drawable/message_selected" />
<textview
android:padding="10dp"
android:text="這是消息界面"
android:textsize="20sp" />
</relativelayout>
這個布局就相對簡單多了,隻是在螢幕的正中央顯示一個消息圖示,以及一段文字。
然後要去建立對應這個布局的fragment。建立messagefragment繼承自fragment,代碼如下所示:
[java] view
public class messagefragment extends fragment {
public view oncreateview(layoutinflater inflater, viewgroup container,
bundle savedinstancestate) {
view messagelayout = inflater.inflate(r.layout.message_layout, container, false);
return messagelayout;
}
}
後面就是依葫蘆畫瓢,把其它幾個fragment以及對應的布局建立出來。建立contacts_layout.xml作為聯系人界面的布局,代碼如下所示:
android:src="@drawable/contacts_selected" />
android:text="這是聯系人界面"
再建立contactsfragment繼承自fragment,代碼如下所示:
public class contactsfragment extends fragment {
@override
view contactslayout = inflater.inflate(r.layout.contacts_layout,
container, false);
return contactslayout;
然後建立news_layout.xml作為動态界面的布局,代碼如下所示:
android:src="@drawable/news_selected" />
android:text="這是動态界面"
再建立newsfragment繼承自fragment,代碼如下所示:
public class newsfragment extends fragment {
view newslayout = inflater.inflate(r.layout.news_layout, container,
false);
return newslayout;
最後建立setting_layout.xml作為設定界面的布局,代碼如下所示:
android:src="@drawable/setting_selected" />
android:text="這是設定界面"
再建立settingfragment繼承自fragment,代碼如下所示:
public class settingfragment extends fragment {
view settinglayout = inflater.inflate(r.layout.setting_layout,
return settinglayout;
這樣我們就把每一個fragment,以及它們所對應的布局檔案都建立好了。接下來也就是最關鍵的步驟了,打開或建立mainactivity作為主activity,代碼如下所示:
/**
* 項目的主activity,所有的fragment都嵌入在這裡。
*
* @author guolin
*/
public class mainactivity extends activity implements onclicklistener {
/**
* 用于展示消息的fragment
*/
private messagefragment messagefragment;
* 用于展示聯系人的fragment
private contactsfragment contactsfragment;
* 用于展示動态的fragment
private newsfragment newsfragment;
* 用于展示設定的fragment
private settingfragment settingfragment;
* 消息界面布局
private view messagelayout;
* 聯系人界面布局
private view contactslayout;
* 動态界面布局
private view newslayout;
* 設定界面布局
private view settinglayout;
* 在tab布局上顯示消息圖示的控件
private imageview messageimage;
* 在tab布局上顯示聯系人圖示的控件
private imageview contactsimage;
* 在tab布局上顯示動态圖示的控件
private imageview newsimage;
* 在tab布局上顯示設定圖示的控件
private imageview settingimage;
* 在tab布局上顯示消息标題的控件
private textview messagetext;
* 在tab布局上顯示聯系人标題的控件
private textview contactstext;
* 在tab布局上顯示動态标題的控件
private textview newstext;
* 在tab布局上顯示設定标題的控件
private textview settingtext;
* 用于對fragment進行管理
private fragmentmanager fragmentmanager;
protected void oncreate(bundle savedinstancestate) {
super.oncreate(savedinstancestate);
requestwindowfeature(window.feature_no_title);
setcontentview(r.layout.activity_main);
// 初始化布局元素
initviews();
fragmentmanager = getfragmentmanager();
// 第一次啟動時選中第0個tab
settabselection(0);
* 在這裡擷取到每個需要用到的控件的執行個體,并給它們設定好必要的點選事件。
private void initviews() {
messagelayout = findviewbyid(r.id.message_layout);
contactslayout = findviewbyid(r.id.contacts_layout);
newslayout = findviewbyid(r.id.news_layout);
settinglayout = findviewbyid(r.id.setting_layout);
messageimage = (imageview) findviewbyid(r.id.message_image);
contactsimage = (imageview) findviewbyid(r.id.contacts_image);
newsimage = (imageview) findviewbyid(r.id.news_image);
settingimage = (imageview) findviewbyid(r.id.setting_image);
messagetext = (textview) findviewbyid(r.id.message_text);
contactstext = (textview) findviewbyid(r.id.contacts_text);
newstext = (textview) findviewbyid(r.id.news_text);
settingtext = (textview) findviewbyid(r.id.setting_text);
messagelayout.setonclicklistener(this);
contactslayout.setonclicklistener(this);
newslayout.setonclicklistener(this);
settinglayout.setonclicklistener(this);
public void onclick(view v) {
switch (v.getid()) {
case r.id.message_layout:
// 當點選了消息tab時,選中第1個tab
settabselection(0);
break;
case r.id.contacts_layout:
// 當點選了聯系人tab時,選中第2個tab
settabselection(1);
case r.id.news_layout:
// 當點選了動态tab時,選中第3個tab
settabselection(2);
case r.id.setting_layout:
// 當點選了設定tab時,選中第4個tab
settabselection(3);
default:
}
* 根據傳入的index參數來設定選中的tab頁。
*
* @param index
* 每個tab頁對應的下标。0表示消息,1表示聯系人,2表示動态,3表示設定。
private void settabselection(int index) {
// 每次選中之前先清楚掉上次的選中狀态
clearselection();
// 開啟一個fragment事務
fragmenttransaction transaction = fragmentmanager.begintransaction();
// 先隐藏掉所有的fragment,以防止有多個fragment顯示在界面上的情況
hidefragments(transaction);
switch (index) {
case 0:
// 當點選了消息tab時,改變控件的圖檔和文字顔色
messageimage.setimageresource(r.drawable.message_selected);
messagetext.settextcolor(color.white);
if (messagefragment == null) {
// 如果messagefragment為空,則建立一個并添加到界面上
messagefragment = new messagefragment();
transaction.add(r.id.content, messagefragment);
} else {
// 如果messagefragment不為空,則直接将它顯示出來
transaction.show(messagefragment);
}
case 1:
// 當點選了聯系人tab時,改變控件的圖檔和文字顔色
contactsimage.setimageresource(r.drawable.contacts_selected);
contactstext.settextcolor(color.white);
if (contactsfragment == null) {
// 如果contactsfragment為空,則建立一個并添加到界面上
contactsfragment = new contactsfragment();
transaction.add(r.id.content, contactsfragment);
// 如果contactsfragment不為空,則直接将它顯示出來
transaction.show(contactsfragment);
case 2:
// 當點選了動态tab時,改變控件的圖檔和文字顔色
newsimage.setimageresource(r.drawable.news_selected);
newstext.settextcolor(color.white);
if (newsfragment == null) {
// 如果newsfragment為空,則建立一個并添加到界面上
newsfragment = new newsfragment();
transaction.add(r.id.content, newsfragment);
// 如果newsfragment不為空,則直接将它顯示出來
transaction.show(newsfragment);
case 3:
// 當點選了設定tab時,改變控件的圖檔和文字顔色
settingimage.setimageresource(r.drawable.setting_selected);
settingtext.settextcolor(color.white);
if (settingfragment == null) {
// 如果settingfragment為空,則建立一個并添加到界面上
settingfragment = new settingfragment();
transaction.add(r.id.content, settingfragment);
// 如果settingfragment不為空,則直接将它顯示出來
transaction.show(settingfragment);
transaction.commit();
* 清除掉所有的選中狀态。
private void clearselection() {
messageimage.setimageresource(r.drawable.message_unselected);
messagetext.settextcolor(color.parsecolor("#82858b"));
contactsimage.setimageresource(r.drawable.contacts_unselected);
contactstext.settextcolor(color.parsecolor("#82858b"));
newsimage.setimageresource(r.drawable.news_unselected);
newstext.settextcolor(color.parsecolor("#82858b"));
settingimage.setimageresource(r.drawable.setting_unselected);
settingtext.settextcolor(color.parsecolor("#82858b"));
* 将所有的fragment都置為隐藏狀态。
* @param transaction
* 用于對fragment執行操作的事務
private void hidefragments(fragmenttransaction transaction) {
if (messagefragment != null) {
transaction.hide(messagefragment);
if (contactsfragment != null) {
transaction.hide(contactsfragment);
if (newsfragment != null) {
transaction.hide(newsfragment);
if (settingfragment != null) {
transaction.hide(settingfragment);
這個類中的注釋已經寫得非常詳細了,下面我再帶大家簡單梳理一遍。在oncreate()方法中先是調用了initviews()來擷取每個控件的執行個體,并給相應的控件設定好點選事件,然後調用settabselection()方法設定預設的選中項,這裡傳入的0說明預設選中第1個tab項。
那麼settabselection()方法中又是如何處理的呢?可以看到,首先第一步是調用clearselection()方法來清理掉之前的選中狀态,然後開啟一個fragment事務,并隐藏掉所有的fragment,以防止有多個fragment顯示在界面上。接下來根據傳入的index參數判斷出選中的是哪一個tab項,并改變該tab項的圖示和文字顔色,然後将相應的fragment添加到界面上。這裡注意一個細節,我們添加fragment的時候并沒有使用replace()方法,而是會先判斷一下該fragment是否為空,如果是空的則調用add()方法添加一個進來,如果不是空的則直接調用show()方法顯示出來即可。那麼為什麼沒有使用replace()方法呢?這是因為replace()方法會将被替換掉的那個fragment徹底地移除掉,該fragment的生命周期就結束了。當再次點選剛才那個tab項的時候,就會讓該fragment的生命周期重新開始,oncreate()、oncreateview()等方法都會重新執行一遍。這顯然不是我們想要的,也和activitygroup的工作原理不符,是以最好的解決方案就是使用hide()和show()方法來隐藏和顯示fragment,這就不會讓fragment的生命周期重走一遍了。
設定完預設選中項後,我們當然還可以通過點選tab項來自由地切換界面,這就會進入到onclick()方法中。onclick()方法中的邏輯判斷非常簡單,當點選了消息标簽時就會選中第1個tab項,點選聯系人标簽時就會選中第2個tab項,點選動态标簽時就會選中第3個tab項,點選設定标簽時就會選中第4個tab項。都是通過調用settabselection()方法來完成的,隻是傳入了不同的參數。
好了,這樣我們就将全部的代碼都編寫完成了,下面就來運作一下吧。整個tab的界面有點類似于qq的感覺,并且可以通過點選不同的tab來切換界面,如下圖所示:
另外,這個tab界面即使在橫屏的情況下也有不錯的适用性哦,如下圖所示:
這樣,我們就成功使用fragment編寫出了和tabhost一樣的效果。每個界面的具體邏輯就可以寫在相應的fragment裡,效果和之前寫在activity裡是差不多的。如此一來,我們終于可以和那個被廢棄的activitygroup說再見了!