天天看點

【Android開發】 Android Fragment應用實戰(轉)

轉載請注明出處:http://blog.csdn.net/guolin_blog/article/details/13171191

現在fragment的應用真的是越來越廣泛了,之前android在3.0版本加入fragment的時候,主要是為了解決android pad螢幕比較大,空間不能充分利用的問題,但現在即使隻是在手機上,也有很多的場景可以運用到fragment了,今天我們就來學習其中一個特别棒的應用技巧。

很多手機應用都會有一個非常類似的功能,即螢幕的下方顯示一行tab标簽選項,點選不同的标簽就可以切換到不同的界面,如以下幾個應用所示:

【Android開發】 Android Fragment應用實戰(轉)
【Android開發】 Android Fragment應用實戰(轉)
【Android開發】 Android Fragment應用實戰(轉)

上面三個應用從左到右分别是qq、新浪微網誌和支付寶錢包,可見,這種底部标簽式的布局政策真的非常常見。

那麼話說回來,這種效果到底是如何的呢?熟悉android的朋友一定都會知道,很簡單嘛,使用tabhost就ok了!但是殊不知,tabhost并非是那麼的簡單,它的可擴充性非常的差,不能随意地定制tab項顯示的内容,而且運作還要依賴于activitygroup。activitygroup原本主要是用于為每一個tabhost的子項管理一個單獨的activity,但目前已經被廢棄了。為什麼呢?當然就是因為fragment的出現了!檢視android官方文檔中activitygroup的描述,如下所示:

【Android開發】 Android Fragment應用實戰(轉)

可以看到,在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

【Android開發】 Android Fragment應用實戰(轉)
【Android開發】 Android Fragment應用實戰(轉)

<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作為消息界面的布局,代碼如下所示:

【Android開發】 Android Fragment應用實戰(轉)
【Android開發】 Android Fragment應用實戰(轉)

<?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

【Android開發】 Android Fragment應用實戰(轉)
【Android開發】 Android Fragment應用實戰(轉)

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開發】 Android Fragment應用實戰(轉)
【Android開發】 Android Fragment應用實戰(轉)

            android:src="@drawable/contacts_selected" />  

            android:text="這是聯系人界面"  

再建立contactsfragment繼承自fragment,代碼如下所示:

【Android開發】 Android Fragment應用實戰(轉)
【Android開發】 Android Fragment應用實戰(轉)

public class contactsfragment extends fragment {  

    @override  

        view contactslayout = inflater.inflate(r.layout.contacts_layout,  

                container, false);  

        return contactslayout;  

然後建立news_layout.xml作為動态界面的布局,代碼如下所示:

【Android開發】 Android Fragment應用實戰(轉)
【Android開發】 Android Fragment應用實戰(轉)

            android:src="@drawable/news_selected" />  

            android:text="這是動态界面"  

再建立newsfragment繼承自fragment,代碼如下所示:

【Android開發】 Android Fragment應用實戰(轉)
【Android開發】 Android Fragment應用實戰(轉)

public class newsfragment extends fragment {  

        view newslayout = inflater.inflate(r.layout.news_layout, container,  

                false);  

        return newslayout;  

最後建立setting_layout.xml作為設定界面的布局,代碼如下所示:

【Android開發】 Android Fragment應用實戰(轉)
【Android開發】 Android Fragment應用實戰(轉)

            android:src="@drawable/setting_selected" />  

            android:text="這是設定界面"  

再建立settingfragment繼承自fragment,代碼如下所示:

【Android開發】 Android Fragment應用實戰(轉)
【Android開發】 Android Fragment應用實戰(轉)

public class settingfragment extends fragment {  

        view settinglayout = inflater.inflate(r.layout.setting_layout,  

        return settinglayout;  

這樣我們就把每一個fragment,以及它們所對應的布局檔案都建立好了。接下來也就是最關鍵的步驟了,打開或建立mainactivity作為主activity,代碼如下所示:

【Android開發】 Android Fragment應用實戰(轉)
【Android開發】 Android Fragment應用實戰(轉)

/** 

 * 項目的主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來切換界面,如下圖所示:

【Android開發】 Android Fragment應用實戰(轉)

另外,這個tab界面即使在橫屏的情況下也有不錯的适用性哦,如下圖所示:

【Android開發】 Android Fragment應用實戰(轉)

這樣,我們就成功使用fragment編寫出了和tabhost一樣的效果。每個界面的具體邏輯就可以寫在相應的fragment裡,效果和之前寫在activity裡是差不多的。如此一來,我們終于可以和那個被廢棄的activitygroup說再見了!

繼續閱讀