以下內容為原創,轉載請注明:http://www.cnblogs.com/tiantianbyconan/p/3360938.html
如新浪微博下面的標簽切換功能,我以前也寫過一篇博文(http://www.cnblogs.com/tiantianbyconan/archive/2012/02/24/2366237.html),可以實現,用的是TabHost。但是android發展比較迅速,TabHost這玩意現在已經被棄用了,雖說用現在也能用,但是被棄用的東西還是少用為妙。
官方有個FragmentTabHost這么一個替代品,於是試了一下,發現每次切換tab,都會調用onCreateView()方法,控件被重新加載,也就是說你從tab1切換到別的tab后,再切換回來,tab1的狀態並沒有保存,重新加載了控件。
搞了半天,暫時沒有好的解決辦法(有朋友知道解決辦法的話,希望聯系我,賜教下哈)
於是,怒了,自己實現一個吧- -
先來看看整個demo的結構:
TabAFm到TabEFm都是Fragment,並且每個Fragment對應一個布局文件。
TabAFm.java:

1 package com.wangjie.fragmenttabhost; 2 3 import android.app.Activity; 4 import android.os.Bundle; 5 import android.support.v4.app.Fragment; 6 import android.view.LayoutInflater; 7 import android.view.View; 8 import android.view.ViewGroup; 9 10 /** 11 * Created with IntelliJ IDEA. 12 * Author: wangjie email:tiantian.china.2@gmail.com 13 * Date: 13-6-14 14 * Time: 下午2:39 15 */ 16 public class TabAFm extends Fragment{ 17 @Override 18 public void onAttach(Activity activity) { 19 super.onAttach(activity); 20 System.out.println("AAAAAAAAAA____onAttach"); 21 } 22 23 @Override 24 public void onCreate(Bundle savedInstanceState) { 25 super.onCreate(savedInstanceState); 26 System.out.println("AAAAAAAAAA____onCreate"); 27 } 28 29 @Override 30 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 31 System.out.println("AAAAAAAAAA____onCreateView"); 32 return inflater.inflate(R.layout.tab_a, container, false); 33 } 34 35 @Override 36 public void onActivityCreated(Bundle savedInstanceState) { 37 super.onActivityCreated(savedInstanceState); 38 System.out.println("AAAAAAAAAA____onActivityCreated"); 39 } 40 41 @Override 42 public void onStart() { 43 super.onStart(); 44 System.out.println("AAAAAAAAAA____onStart"); 45 } 46 47 @Override 48 public void onResume() { 49 super.onResume(); 50 System.out.println("AAAAAAAAAA____onResume"); 51 } 52 53 @Override 54 public void onPause() { 55 super.onPause(); 56 System.out.println("AAAAAAAAAA____onPause"); 57 } 58 59 @Override 60 public void onStop() { 61 super.onStop(); 62 System.out.println("AAAAAAAAAA____onStop"); 63 } 64 65 @Override 66 public void onDestroyView() { 67 super.onDestroyView(); 68 System.out.println("AAAAAAAAAA____onDestroyView"); 69 } 70 71 @Override 72 public void onDestroy() { 73 super.onDestroy(); 74 System.out.println("AAAAAAAAAA____onDestroy"); 75 } 76 77 @Override 78 public void onDetach() { 79 super.onDetach(); 80 System.out.println("AAAAAAAAAA____onDetach"); 81 } 82 }
如上述代碼所示,TabAFm是一個Fragment,對應的布局文件是tab_a.xml,並實現了他的所有的生命周期回調函數並打印,便於調試
tab_a.xml布局中有個EditText
其他的Fragment大同小異,這里就不貼出代碼了
現在來看MainActivity:

1 package com.wangjie.fragmenttabhost; 2 3 import android.os.Bundle; 4 import android.support.v4.app.Fragment; 5 import android.support.v4.app.FragmentActivity; 6 import android.widget.RadioGroup; 7 8 import java.util.ArrayList; 9 import java.util.List; 10 11 public class MainActivity extends FragmentActivity { 12 /** 13 * Called when the activity is first created. 14 */ 15 private RadioGroup rgs; 16 public List<Fragment> fragments = new ArrayList<Fragment>(); 17 18 public String hello = "hello "; 19 20 @Override 21 public void onCreate(Bundle savedInstanceState) { 22 super.onCreate(savedInstanceState); 23 setContentView(R.layout.main); 24 25 fragments.add(new TabAFm()); 26 fragments.add(new TabBFm()); 27 fragments.add(new TabCFm()); 28 fragments.add(new TabDFm()); 29 fragments.add(new TabEFm()); 30 31 32 rgs = (RadioGroup) findViewById(R.id.tabs_rg); 33 34 FragmentTabAdapter tabAdapter = new FragmentTabAdapter(this, fragments, R.id.tab_content, rgs); 35 tabAdapter.setOnRgsExtraCheckedChangedListener(new FragmentTabAdapter.OnRgsExtraCheckedChangedListener(){ 36 @Override 37 public void OnRgsExtraCheckedChanged(RadioGroup radioGroup, int checkedId, int index) { 38 System.out.println("Extra---- " + index + " checked!!! "); 39 } 40 }); 41 42 } 43 44 }
MainActivity上述代碼所示
MainActivity是包含Fragment的Activity(也就是這里的5個Fragment)
他繼承了FragmentActivity(因為我這里用的是android-support-v4.jar)
用一個List<Fragment>去維護5個Fragment,也就是5個tab
main布局中有一個id為tab_content的FrameLayout,用來存放要顯示的Fragment。底部有一個RadioGroup,用於tab的切換,如下:

1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:orientation="vertical" 4 android:layout_width="fill_parent" 5 android:layout_height="fill_parent" 6 android:background="@android:color/white" 7 > 8 <LinearLayout 9 android:layout_width="fill_parent" 10 android:layout_height="fill_parent" 11 android:orientation="vertical" 12 > 13 14 <FrameLayout 15 android:id="@+id/tab_content" 16 android:layout_width="fill_parent" 17 android:layout_height="0dp" 18 android:layout_weight="1.0" 19 android:background="#77557799" 20 /> 21 22 <RadioGroup 23 android:id="@+id/tabs_rg" 24 android:layout_width="fill_parent" 25 android:layout_height="wrap_content" 26 android:orientation="horizontal" 27 android:gravity="center" 28 android:paddingTop="7dp" 29 android:paddingBottom="7dp" 30 > 31 <RadioButton 32 android:id="@+id/tab_rb_a" 33 android:layout_width="0dp" 34 android:layout_height="wrap_content" 35 android:drawableTop="@drawable/tablatestalert" 36 android:button="@null" 37 android:text="Tab1" 38 android:textColor="#000000" 39 android:textSize="13sp" 40 android:layout_weight="1.0" 41 android:gravity="center" 42 android:singleLine="true" 43 android:checked="true" 44 android:background="@drawable/selector_tab" 45 /> 46 <RadioButton 47 android:id="@+id/tab_rb_b" 48 android:layout_width="0dp" 49 android:layout_height="wrap_content" 50 android:drawableTop="@drawable/tabsearch" 51 android:button="@null" 52 android:text="Tab2" 53 android:textColor="#000000" 54 android:textSize="13sp" 55 android:layout_weight="1.0" 56 android:gravity="center" 57 android:singleLine="true" 58 android:background="@drawable/selector_tab" 59 /> 60 <RadioButton 61 android:id="@+id/tab_rb_c" 62 android:layout_width="0dp" 63 android:layout_height="wrap_content" 64 android:drawableTop="@drawable/tabrecommd" 65 android:button="@null" 66 android:text="Tab3" 67 android:textColor="#000000" 68 android:textSize="13sp" 69 android:layout_weight="1.0" 70 android:gravity="center" 71 android:singleLine="true" 72 android:background="@drawable/selector_tab" 73 /> 74 <RadioButton 75 android:id="@+id/tab_rb_d" 76 android:layout_width="0dp" 77 android:layout_height="wrap_content" 78 android:drawableTop="@drawable/tabconfigicon" 79 android:button="@null" 80 android:text="Tab4" 81 android:textColor="#000000" 82 android:textSize="13sp" 83 android:layout_weight="1.0" 84 android:gravity="center" 85 android:singleLine="true" 86 android:background="@drawable/selector_tab" 87 /> 88 <RadioButton 89 android:id="@+id/tab_rb_e" 90 android:layout_width="0dp" 91 android:layout_height="wrap_content" 92 android:drawableTop="@drawable/tababoutus" 93 android:button="@null" 94 android:text="Tab5" 95 android:textColor="#000000" 96 android:textSize="13sp" 97 android:layout_weight="1.0" 98 android:gravity="center" 99 android:singleLine="true" 100 android:background="@drawable/selector_tab" 101 /> 102 103 </RadioGroup> 104 </LinearLayout> 105 </LinearLayout>
現在回到MainActivity中,下面這個FragmentTabAdapter類是關鍵,是我自己編寫的用於綁定和處理fragments和RadioGroup之間的邏輯關系
FragmentTabAdapter tabAdapter = new FragmentTabAdapter(this, fragments, R.id.tab_content, rgs);
現在看下FragmentTabAdapter:

1 package com.wangjie.fragmenttabhost; 2 3 import android.support.v4.app.Fragment; 4 import android.support.v4.app.FragmentActivity; 5 import android.support.v4.app.FragmentTransaction; 6 import android.widget.RadioGroup; 7 8 import java.util.List; 9 10 /** 11 * Created with IntelliJ IDEA. 12 * Author: wangjie email:tiantian.china.2@gmail.com 13 * Date: 13-10-10 14 * Time: 上午9:25 15 */ 16 public class FragmentTabAdapter implements RadioGroup.OnCheckedChangeListener{ 17 private List<Fragment> fragments; // 一個tab頁面對應一個Fragment 18 private RadioGroup rgs; // 用於切換tab 19 private FragmentActivity fragmentActivity; // Fragment所屬的Activity 20 private int fragmentContentId; // Activity中所要被替換的區域的id 21 22 private int currentTab; // 當前Tab頁面索引 23 24 private OnRgsExtraCheckedChangedListener onRgsExtraCheckedChangedListener; // 用於讓調用者在切換tab時候增加新的功能 25 26 public FragmentTabAdapter(FragmentActivity fragmentActivity, List<Fragment> fragments, int fragmentContentId, RadioGroup rgs) { 27 this.fragments = fragments; 28 this.rgs = rgs; 29 this.fragmentActivity = fragmentActivity; 30 this.fragmentContentId = fragmentContentId; 31 32 // 默認顯示第一頁 33 FragmentTransaction ft = fragmentActivity.getSupportFragmentManager().beginTransaction(); 34 ft.add(fragmentContentId, fragments.get(0)); 35 ft.commit(); 36 37 rgs.setOnCheckedChangeListener(this); 38 39 40 } 41 42 @Override 43 public void onCheckedChanged(RadioGroup radioGroup, int checkedId) { 44 for(int i = 0; i < rgs.getChildCount(); i++){ 45 if(rgs.getChildAt(i).getId() == checkedId){ 46 Fragment fragment = fragments.get(i); 47 FragmentTransaction ft = obtainFragmentTransaction(i); 48 49 getCurrentFragment().onPause(); // 暫停當前tab 50 // getCurrentFragment().onStop(); // 暫停當前tab 51 52 if(fragment.isAdded()){ 53 // fragment.onStart(); // 啟動目標tab的onStart() 54 fragment.onResume(); // 啟動目標tab的onResume() 55 }else{ 56 ft.add(fragmentContentId, fragment); 57 } 58 showTab(i); // 顯示目標tab 59 ft.commit(); 60 61 // 如果設置了切換tab額外功能功能接口 62 if(null != onRgsExtraCheckedChangedListener){ 63 onRgsExtraCheckedChangedListener.OnRgsExtraCheckedChanged(radioGroup, checkedId, i); 64 } 65 66 } 67 } 68 69 } 70 71 /** 72 * 切換tab 73 * @param idx 74 */ 75 private void showTab(int idx){ 76 for(int i = 0; i < fragments.size(); i++){ 77 Fragment fragment = fragments.get(i); 78 FragmentTransaction ft = obtainFragmentTransaction(idx); 79 80 if(idx == i){ 81 ft.show(fragment); 82 }else{ 83 ft.hide(fragment); 84 } 85 ft.commit(); 86 } 87 currentTab = idx; // 更新目標tab為當前tab 88 } 89 90 /** 91 * 獲取一個帶動畫的FragmentTransaction 92 * @param index 93 * @return 94 */ 95 private FragmentTransaction obtainFragmentTransaction(int index){ 96 FragmentTransaction ft = fragmentActivity.getSupportFragmentManager().beginTransaction(); 97 // 設置切換動畫 98 if(index > currentTab){ 99 ft.setCustomAnimations(R.anim.slide_left_in, R.anim.slide_left_out); 100 }else{ 101 ft.setCustomAnimations(R.anim.slide_right_in, R.anim.slide_right_out); 102 } 103 return ft; 104 } 105 106 public int getCurrentTab() { 107 return currentTab; 108 } 109 110 public Fragment getCurrentFragment(){ 111 return fragments.get(currentTab); 112 } 113 114 public OnRgsExtraCheckedChangedListener getOnRgsExtraCheckedChangedListener() { 115 return onRgsExtraCheckedChangedListener; 116 } 117 118 public void setOnRgsExtraCheckedChangedListener(OnRgsExtraCheckedChangedListener onRgsExtraCheckedChangedListener) { 119 this.onRgsExtraCheckedChangedListener = onRgsExtraCheckedChangedListener; 120 } 121 122 /** 123 * 切換tab額外功能功能接口 124 */ 125 static class OnRgsExtraCheckedChangedListener{ 126 public void OnRgsExtraCheckedChanged(RadioGroup radioGroup, int checkedId, int index){ 127 128 } 129 } 130 131 }
這里解決Fragment切換重新加載布局的辦法,用的是把幾個Fragment全部Add,然后根據要顯示的哪個Fragment設置show或者hide
效果輸出:
10-10 11:55:41.168: INFO/System.out(18368): AAAAAAAAAA____onAttach // 第一次進入,顯示TabA
10-10 11:55:41.168: INFO/System.out(18368): AAAAAAAAAA____onCreate
10-10 11:55:41.168: INFO/System.out(18368): AAAAAAAAAA____onCreateView
10-10 11:55:41.175: INFO/System.out(18368): AAAAAAAAAA____onActivityCreated
10-10 11:55:41.179: INFO/System.out(18368): AAAAAAAAAA____onStart
10-10 11:55:41.179: INFO/System.out(18368): AAAAAAAAAA____onResume
10-10 11:55:44.980: INFO/System.out(18368): AAAAAAAAAA____onPause // 從TabA切換到TabB(TabA調用onPause)
10-10 11:55:44.980: INFO/System.out(18368): Extra---- 1 checked!!!
10-10 11:55:44.996: INFO/System.out(18368): BBBBBBBBBBB____onAttach
10-10 11:55:44.996: INFO/System.out(18368): BBBBBBBBBBB____onCreate
10-10 11:55:44.996: INFO/System.out(18368): BBBBBBBBBBB____onCreateView
10-10 11:55:45.004: INFO/System.out(18368): BBBBBBBBBBB____onActivityCreated
10-10 11:55:45.004: INFO/System.out(18368): BBBBBBBBBBB____onStart
10-10 11:55:45.004: INFO/System.out(18368): BBBBBBBBBBB____onResume
10-10 11:55:52.062: INFO/System.out(18368): BBBBBBBBBBB____onPause // 從TabB切換到TabC(TabB調用onPause)
10-10 11:55:52.062: INFO/System.out(18368): Extra---- 2 checked!!!
10-10 11:55:52.082: INFO/System.out(18368): CCCCCCCCCC____onAttach
10-10 11:55:52.082: INFO/System.out(18368): CCCCCCCCCC____onCreate
10-10 11:55:52.086: INFO/System.out(18368): CCCCCCCCCC____onCreateView
10-10 11:55:52.090: INFO/System.out(18368): CCCCCCCCCC____onActivityCreated
10-10 11:55:52.090: INFO/System.out(18368): CCCCCCCCCC____onStart
10-10 11:55:52.090: INFO/System.out(18368): CCCCCCCCCC____onResume
10-10 11:56:06.535: INFO/System.out(18368): CCCCCCCCCC____onPause // 從TabC切換到TabB(TabC調用onPause)
10-10 11:56:06.535: INFO/System.out(18368): BBBBBBBBBBB____onResume // 從TabC切換到TabB(TabB調用onResume)
10-10 11:56:06.535: INFO/System.out(18368): Extra---- 1 checked!!!
好了,到此為止,我們已經用Fragment實現了類似TabHost的功能了,下面來看下各個Fragment之間的通信
現在的情況是TabAFm中有個EditText,TabBFm中有個Button,MainActivity中有個變量“hello”
要做的是,切換到TabA,輸入“I'm TabA”,切換到B,點擊Button后,Toast顯示“hello I'm TabA”
MainActivity中沒什么好說的,就一個hello變量:
public String hello = "hello ";
TabAFm在布局文件tab_a.xml中加個EditText,設置個id就可以了
TabBFm中:

1 @Override 2 public void onActivityCreated(Bundle savedInstanceState) { 3 super.onActivityCreated(savedInstanceState); 4 System.out.println("BBBBBBBBBBB____onActivityCreated"); 5 this.getView().findViewById(R.id.clickme).setOnClickListener(new View.OnClickListener() { 6 @Override 7 public void onClick(View view) { 8 // 獲得綁定的FragmentActivity 9 MainActivity activity = ((MainActivity)getActivity()); 10 // 獲得TabAFm的控件 11 EditText editText = (EditText) activity.fragments.get(0).getView().findViewById(R.id.edit); 12 13 Toast.makeText(activity, activity.hello + editText.getText(), Toast.LENGTH_SHORT).show(); 14 } 15 }); 16 }
// 獲得綁定的FragmentActivity MainActivity activity = ((MainActivity)getActivity());
通過getActivity()即可得到Fragment所在的FragmentActivity
最終效果圖:
demo下載地址:http://pan.baidu.com/s/1wxsIX