1. 程式人生 > >使用Fragment完成Tab選項卡-Android Fragment應用實戰

使用Fragment完成Tab選項卡-Android Fragment應用實戰

先看一下QQ、新浪微博支付寶錢包這三個非常有名的應用,都有一個底部導航欄,我們一般稱之‘選項卡’。google官方會叫他們為fixed tab,不過國內好像很好這麼叫的。其實,在anroid 4.x時代,google官方更希望應用的導航放在頂部,通過滑屏和點選標籤來切換介面。但是隨著ios的的跟風以及使用者習慣的養成,這種設計風格的形成也就變成歷史遺留問題。在這裡我們不討論哪一個風格好,哪一個風格不好。做為開發人員,我們可能更關注這種司空見慣的介面設計是怎麼實現的呢?


在android 2.x時代,我們可能會地使用ActivityGroup來實現這種,但是隨著jelly bean的市場份額超過50%,我們會發現有一種新的組建出現了,它叫Fragment(

http://developer.android.com/reference/android/app/Fragment.html)。而這種底部選項卡的風格介面的實現也由ActivityGroup轉向了Fragment。先了,費話不多說了,下面我會一步一步教您怎麼實現這個介面。在動手之前,我可能需要把我做好的樣式圖給你看一下,以遍讓您有一個心裡預期。



其實,我們這個介面的實現,基本沒有什麼java 程式碼。不信,你看下面就是主介面的程式碼:

  1. publicclass MainActivity extends Activity {  
  2.     private FragmentManager fragmentManager;  
  3.     private RadioGroup radioGroup;  
  4.     @TargetApi(Build.VERSION_CODES.HONEYCOMB)  
  5.     @Override
  6.     protectedvoid onCreate(Bundle savedInstanceState) {  
  7.         super.onCreate(savedInstanceState);  
  8.         requestWindowFeature(Window.FEATURE_NO_TITLE);  
  9.         setContentView(R.layout.weibo_tab);  
  10.         fragmentManager = getFragmentManager();  
  11.         radioGroup = (RadioGroup) findViewById(R.id.rg_tab);  
  12.         radioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {  
  13.             @Override
  14.             publicvoid onCheckedChanged(RadioGroup group, int checkedId) {  
  15.                 FragmentTransaction transaction = fragmentManager.beginTransaction();  
  16.                 Fragment fragment = FragmentFactory.getInstanceByIndex(checkedId);  
  17.                 transaction.replace(R.id.content, fragment);  
  18.                 transaction.commit();  
  19.             }  
  20.         });  
  21.     }  
  22. }  

所以哦,親們,我們把重點放在xml介面的編寫上咯。

1.我們可能需要一背景圖片像這樣的:

2.我們要準備這五個選項卡圖片。包括正常和被按下兩種狀態。所以共十張圖片。

3.你要為這五個按鈕分別編寫selector,檔名為weibolist_attention_selector.xml

  1. <?xmlversion="1.0"encoding="utf-8"?><!-- 微博列表介面tab欄關注 按鈕 -->
  2. <selectorxmlns:android="http://schemas.android.com/apk/res/android">
  3.     <itemandroid:drawable="@drawable/weibo_listab_attention_on"android:state_pressed="true"/>
  4.     <itemandroid:drawable="@drawable/weibo_listab_attention_on"android:state_checked="true"/>
  5.     <itemandroid:drawable="@drawable/weibo_listab_attention_off"/>
  6. </selector>
這個xml檔案挺簡單的啊,但是細心的同學可能會地現,你這裡為什麼要用android:state_checked的啊?為什麼不是其他的,比如android:state_selected哦

我想說,其實我這個底部用的是RadioGroup,我怎麼判斷當前選中的是哪個呢,不選中的是哪個呢。這裡最好的選擇就是使用RadioGroup,它的最大好處就是彼此的RadioButton具有互斥性,這樣就費掉用java程式碼判斷和處理的麻煩了。

4.下面,就要考慮為整個介面佈局了。檔名為activity.xml

  1. <?xmlversion="1.0"encoding="utf-8"?>
  2. <LinearLayout
  3.     android:layout_width="match_parent"
  4.     android:layout_height="match_parent"
  5.     android:orientation="vertical"
  6.     xmlns:android="http://schemas.android.com/apk/res/android">
  7.     <FrameLayout
  8.         android:id="@+id/content"
  9.         android:layout_width="match_parent"
  10.         android:layout_height="match_parent"
  11.         android:layout_weight="1"/>
  12.     <RadioGroup
  13.         android:id="@+id/rg_tab"
  14.         android:orientation="horizontal"
  15.         android:layout_width="match_parent"
  16.         android:layout_height="wrap_content"
  17.         android:background="@drawable/bg_weibo_listab">
  18.         <RadioButton
  19.             style="@style/weibo_tab"
  20.             android:drawableTop="@drawable/weibolist_attention_selector"
  21.             android:text="微博"/>
  22.         <RadioButton
  23.             style="@style/weibo_tab"
  24.             android:drawableTop="@drawable/weibolist_atme_selector"
  25.             android:text="\@我"/>
  26.         <RadioButton
  27.             style="@style/weibo_tab"
  28.             android:drawableTop="@drawable/weibolist_comment_selector"
  29.             android:text="評論"/>
  30.         <RadioButton
  31.             style="@style/weibo_tab"
  32.             android:drawableTop="@drawable/weibolist_mylist_selector"
  33.             android:text="我的"/>
  34.         <RadioButton
  35.             style="@style/weibo_tab"
  36.             android:drawableTop="@drawable/weibolist_global_selector"
  37.             android:text="全站"/>
  38.     </RadioGroup>
  39. </LinearLayout>
這裡可能就有幾個問題啦

(1) FrameLayout是幹什麼的,可能用別的父控制元件嗎?

     這個是一個預留的內容控制元件,以後會用它去呈現Fragment的內容。理論上是可以用別的父控制元件的,沒試過。

(2)android:weight=1,怎麼只有一個呢,這樣做有對LinearLayout有意義嗎?

     首先對FragLayout一定要寫,不寫就會變成這樣。但是第二個RadioGroup的weight一定不要寫,不然會很難調的。這個我猜測應該會有一個很好weight預設值自適應的。

     我只是猜測啦,具體原因求解釋。

(3)weibo_tab樣式裡面什麼內容?

    什麼內容,如下就可以啦:

  1. <stylename="weibo_tab">
  2.     <itemname="android:layout_height">wrap_content</item>
  3.     <itemname="android:layout_width">match_parent</item>
  4.     <itemname="android:layout_weight">1</item>
  5.     <itemname="android:gravity">center</item>
  6.     <itemname="android:textSize">14sp</item>
  7.     <itemname="android:paddingTop">8dip</item>
  8.     <itemname="android:paddingBottom">4dip</item>
  9.     <itemname="android:background">@drawable/weibolist_bottombar_itembg_selector</item>
  10.     <itemname="android:textColor">@color/weibolist_bottombar_textcolor_selector</item>
  11.     <itemname="android:button">@null</item>
  12. </style>
    這個style的目地是為了把button相同的設定提取出來,進行很好地重構。
    這裡可能要解釋幾個屬性weight,其實RadioGroup是繼承LinearLayout,設定weight的目的就是把這個底部檢視五等分。

    textColor,這是為是控制底部顏色變化的。button,這個一定要設定,否則就會這樣


   做到這裡,我建議你就跑起來,試試效果。雖然沒有用到Fragment,但是底部選項已經可以任意切換啦。

   你是不是已經把它很好的搞定了呢,先恭喜你咯。大笑

5.下一步我來內容填充的介面檢視,檔名為fragment.xml

  1. <?xmlversion="1.0"encoding="utf-8"?>
  2. <RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"
  3.     android:layout_width="match_parent"
  4.     android:layout_height="match_parent">
  5.     <TextView
  6.         android:id="@+id/txt_content"
  7.         android:layout_width="match_parent"
  8.         android:layout_height="match_parent"
  9.         android:gravity="center"
  10.         android:padding="10dp"
  11.         android:text=""
  12.         android:textSize="20sp"/>
  13. </RelativeLayout>
這個沒有什麼解釋的,略過。

6.下面我們做五個Fragment內容的呈現,如下:

先有一個BaseFragment抽象類,以被具體的子類繼承和實現。

  1. publicabstractclass BaseFragment extends Fragment {  
  2.     @Override
  3.     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {  
  4.         View view = inflater.inflate(R.layout.fragment, null);  
  5.         TextView textView = (TextView) view.findViewById(R.id.txt_content);  
  6.         textView.setText(initContent());  
  7.         return view;  
  8.     }  
  9.     publicabstract String initContent();  
  10. }  

(1)關注介面

  1. publicclass AttentionFragment extends BaseFragment {  
  2.     @Override
  3.     public String initContent() {  
  4.         return"這是關注我介面";  
  5.     }  
  6. }  
(2)@介面
  1. publicclass AtmeFragment extends BaseFragment {  
  2.     @Override
  3.     public String initContent() {  
  4.         return"這是@我介面";  
  5.     }  
  6. }  
(3)評論介面
  1. publicclass CommentFragment extends BaseFragment {  
  2.     @Override
  3.     public String initContent() {  
  4.         return"這是評論我介面";  
  5.     }  
  6. }  
(4)我的介面
  1. publicclass MyListFragment extends BaseFragment {  
  2.     @Override
  3.     public String initContent() {  
  4.         return"這是我的列表介面";  
  5.     }  
  6. }  
(5)全部介面
  1. publicclass GlobalFragment extends BaseFragment {  
  2.     @Override
  3.     public String initContent() {  
  4.         return"這是全站介面";  
  5.     }  
  6. }  

7.我們還需要一個工廠模式,來實現根據下標的位置返回相應的Fragment。像這樣
  1. publicclass FragmentFactory {  
  2.     publicstatic Fragment getInstanceByIndex(int index) {  
  3.         Fragment fragment = null;  
  4.         switch (index) {  
  5.             case1:  
  6.                 fragment = new AttentionFragment();  
  7.                 break;  
  8.             case2:  
  9.                 fragment = new AtmeFragment();  
  10.                 break;  
  11.             case3:  
  12.                 fragment = new CommentFragment();  
  13.                 break;  
  14.             case4:  
  15.                 fragment = new MyListFragment();  
  16.                 break;  
  17.             case5:  
  18.                 fragment = new GlobalFragment();  
  19.                 break;  
  20.         }  
  21.         return fragment;  
  22.     }  
  23. }  

8.好了,萬事具備只欠東風啦。請回到我們剛開始放出去那段程式碼,那你就已經完成它了。

9.哦,我們還是放上配置檔案,貢大家參考。

  1. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  2.     package="com.mydream.fragment"
  3.     android:versionCode="1"
  4.     android:versionName="1.0">  
  5.     <uses-sdk  
  6.         android:minSdkVersion="14"
  7.         android:targetSdkVersion="19" />  
  8.     <application  
  9.         android:allowBackup="true"
  10.         android:label="@string/app_name"
  11.         android:icon="@drawable/ic_launcher">  
  12.         <activity  
  13.             android:name=".MainActivity"
  14.             android:label="@string/app_name">  
  15.             <intent-filter>  
  16.                 <action android:name="android.intent.action.MAIN" />  
  17.                 <category android:name="android.intent.category.LAUNCHER" />  
  18.             </intent-filter>  
  19.         </activity>  
  20.     </application>  
  21. </manifest>  

原始碼下載