以下内容为原创,转载请注明:
如新浪微博下面的标签切换功能,我以前也写过一篇博文(),可以实现,用的是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.com13 * Date: 13-6-1414 * Time: 下午2:3915 */16 public class TabAFm extends Fragment{17 @Override18 public void onAttach(Activity activity) {19 super.onAttach(activity);20 System.out.println("AAAAAAAAAA____onAttach");21 }22 23 @Override24 public void onCreate(Bundle savedInstanceState) {25 super.onCreate(savedInstanceState);26 System.out.println("AAAAAAAAAA____onCreate");27 }28 29 @Override30 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 @Override36 public void onActivityCreated(Bundle savedInstanceState) {37 super.onActivityCreated(savedInstanceState);38 System.out.println("AAAAAAAAAA____onActivityCreated");39 }40 41 @Override42 public void onStart() {43 super.onStart();44 System.out.println("AAAAAAAAAA____onStart");45 }46 47 @Override48 public void onResume() {49 super.onResume();50 System.out.println("AAAAAAAAAA____onResume");51 }52 53 @Override54 public void onPause() {55 super.onPause();56 System.out.println("AAAAAAAAAA____onPause");57 }58 59 @Override60 public void onStop() {61 super.onStop();62 System.out.println("AAAAAAAAAA____onStop");63 }64 65 @Override66 public void onDestroyView() {67 super.onDestroyView();68 System.out.println("AAAAAAAAAA____onDestroyView");69 }70 71 @Override72 public void onDestroy() {73 super.onDestroy();74 System.out.println("AAAAAAAAAA____onDestroy");75 }76 77 @Override78 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 Listfragments = new ArrayList ();17 18 public String hello = "hello ";19 20 @Override21 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 @Override37 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 28 13 14 10521 22 31 10446 60 74 88 102 103
现在回到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 Listfragments; // 一个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 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____onCreate10-10 11:55:41.168: INFO/System.out(18368): AAAAAAAAAA____onCreateView10-10 11:55:41.175: INFO/System.out(18368): AAAAAAAAAA____onActivityCreated10-10 11:55:41.179: INFO/System.out(18368): AAAAAAAAAA____onStart10-10 11:55:41.179: INFO/System.out(18368): AAAAAAAAAA____onResume10-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____onAttach10-10 11:55:44.996: INFO/System.out(18368): BBBBBBBBBBB____onCreate10-10 11:55:44.996: INFO/System.out(18368): BBBBBBBBBBB____onCreateView10-10 11:55:45.004: INFO/System.out(18368): BBBBBBBBBBB____onActivityCreated10-10 11:55:45.004: INFO/System.out(18368): BBBBBBBBBBB____onStart10-10 11:55:45.004: INFO/System.out(18368): BBBBBBBBBBB____onResume10-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____onAttach10-10 11:55:52.082: INFO/System.out(18368): CCCCCCCCCC____onCreate10-10 11:55:52.086: INFO/System.out(18368): CCCCCCCCCC____onCreateView10-10 11:55:52.090: INFO/System.out(18368): CCCCCCCCCC____onActivityCreated10-10 11:55:52.090: INFO/System.out(18368): CCCCCCCCCC____onStart10-10 11:55:52.090: INFO/System.out(18368): CCCCCCCCCC____onResume10-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 }
// 获得绑定的FragmentActivityMainActivity activity = ((MainActivity)getActivity());
通过getActivity()即可得到Fragment所在的FragmentActivity
最终效果图:
demo下载地址: