一、需求
关于fragment的问题,一直想写一篇博客了,应该当初自己也是对这玩意一点都不熟悉到现在也大概知道个日常的使用的地步。
一个侧滑的导航栏,内有4个条目,每个选项点击进入对应的界面,每一个界面是一个fragment,各界面之间自由切换,且可以保存之前的状态,也就是说,切换的过程并不会产生新的对象,不会重新去new 一个fragment对象,不需要每次点击重新加载数据,这里就涉及了一个很重要的问题,fragment状态的保存,在这篇文章里,我尽量用实例把这个问题说清楚,毕竟当初也是查了不少资料,摸索了不少地方才解决的。
还有一个问题,是最近发现的,就是从一个activity界面跳到其中的一个fragment,这个在实际的应用中,也会涉及到,因为我们的项目就涉及到这里了,知道最后还是想办法把这个问题解决了,下面来看看详细的介绍吧。
二、实际效果图
三、实现过程
1、准备工作
先导入slidingmenu库文件,这个相信大家都会把.导入slidingmenu的库文件后会报错,因为它是依赖另一个库文件的,这个库文件就是ActionBarSherlock,将它的库文件也导入eclipse,这个时候要注意几点,首先,要将actionbarsherlock作为slidingmenu的库文件,然后将slidingmenu作为程序的库文件。
同时,要将actionbarsherlock中的android-support-v4包分别拷到slidingmenu库的libs中覆盖原来的android-support-v4包,同理也要覆盖程序的support-v4包,否则就会出现The hierarchy of the type MainActivity is inconsistent这个错误。
导入了相应的库文件后,将主Activity继承SlidingFragmentActivity 就可以使用sliding menu了
2.导航栏界面实现,打开slidingmenu后看到的界面
先做一个侧滑导航的fragment,这个fragment就是应用程序的侧边导航栏,侧滑打开后,点击其中的某一项,就会跳到相应的fragment页面。
我们将这个fragment命名为navifragment,先看navifragment的布局文件
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/navi_list" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#cccccc" > <LinearLayout android:layout_width="fill_parent" android:layout_height="400dip" android:layout_centerInParent="true" android:gravity="center" android:orientation="vertical" > <TextView android:id="@+id/tv_navi_wechat" style="@style/navitext" android:drawableLeft="@drawable/wechat" android:text="微信" /> <TextView android:id="@+id/tv_navi_contacts" style="@style/navitext" android:drawableLeft="@drawable/contacts" android:text="通讯录" /> <TextView android:id="@+id/tv_navi_search" style="@style/navitext" android:drawableLeft="@drawable/search" android:text="发现" /> <TextView android:id="@+id/tv_navi_my" style="@style/navitext" android:drawableLeft="@drawable/my" android:text="我的" /> </LinearLayout> </RelativeLayout>效果如下图:
NaviFragment中的代码如下:
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (rootView == null) { rootView = inflater.inflate(R.layout.fragment_navi, null); } fragmentManager = getFragmentManager(); init(); return rootView; } @Override public void onAttach(Activity activity) { mActivity = (MainActivity) activity; super.onAttach(activity); } /** * 初始化,设置点击事件 */ private void init() { navi_wechat = (TextView) rootView.findViewById(R.id.tv_navi_wechat); navi_contacts = (TextView) rootView.findViewById(R.id.tv_navi_contacts); navi_search = (TextView) rootView.findViewById(R.id.tv_navi_search); navi_my = (TextView) rootView.findViewById(R.id.tv_navi_my); navi_wechat.setSelected(true);// 默认选中菜单 navi_wechat.setOnClickListener(this); navi_contacts.setOnClickListener(this); navi_search.setOnClickListener(this); navi_my.setOnClickListener(this); } /** * 点击导航栏切换 同时更改标题 */ @Override public void onClick(View view) { switch (view.getId()) { case R.id.tv_navi_wechat:// 打开微信界面 navi_wechat.setSelected(true);// 微信设置为被选中状态,其余设置为非选中状态 navi_contacts.setSelected(false); navi_my.setSelected(false); navi_search.setSelected(false); OnTabSelected(WECHATFRAGMENT); break; case R.id.tv_navi_contacts:// 打开通讯录界面 navi_wechat.setSelected(false); navi_contacts.setSelected(true); navi_my.setSelected(false); navi_search.setSelected(false); OnTabSelected(CONTACTSFRAGMENT); break; case R.id.tv_navi_search:// 打开发现界面 navi_wechat.setSelected(false); navi_contacts.setSelected(false); navi_my.setSelected(false); navi_search.setSelected(true); OnTabSelected(SEARCHFRAGMENT); break; case R.id.tv_navi_my:// 打开我的界面 navi_wechat.setSelected(false); navi_about.setSelected(false); navi_contacts.setSelected(false); navi_my.setSelected(true); navi_search.setSelected(false); navi_yuefan.setSelected(false); OnTabSelected(MYFRAGMENT); break; } mActivity.getSlidingMenu().toggle(); } //选中导航中对应的tab选项 private void OnTabSelected(int index) { FragmentTransaction transaction = fragmentManager.beginTransaction(); hideFragments(transaction); switch (index) { case WECHATFRAGMENT://微信 if (null == wechatFragment) { wechatFragment = new WechatFragment(); transaction.add(R.id.center_frame, wechatFragment); } else { transaction.show(wechatFragment); } break; case CONTACTSFRAGMENT: //通讯录 if (null == contactsFragment) { contactsFragment = new ContactsFragment(); transaction.add(R.id.center_frame, contactsFragment); } else { transaction.show(contactsFragment); // String phone = SharedPrefsUtil.getString(getActivity(), // AppConst.USERPHONE); // if (phone != null && !"".equals(phone)) { // contactsFragment.et_phone.setText(phone); // } } break; case SEARCHFRAGMENT://发现 if (null == serchFragment) { serchFragment = new SerchFragment(); transaction.add(R.id.center_frame, serchFragment); } else { transaction.show(serchFragment); } break; case MYFRAGMENT://我的 if (null == myFragment) { myFragment = new MyFragment(); transaction.add(R.id.center_frame, myFragment); } else { transaction.show(myFragment); } break; } transaction.commit(); } /** * 将所有fragment都置为隐藏状态 * * @param transaction * 用于对Fragment执行操作的事务 */ private void hideFragments(FragmentTransaction transaction) { if (wechatFragment != null) { transaction.hide(wechatFragment); } if (contactsFragment != null) { transaction.hide(contactsFragment); } if (serchFragment != null) { transaction.hide(serchFragment); } if (myFragment != null) { transaction.hide(myFragment); } }上面有两个重要的方法,涉及到fragment状态的保存
transcation.hide() transction.show()
前一个是fragment的隐藏,将fragment隐藏到后面去,当然,这个fragment并没有被destroy,一直保存着隐藏前的状态,当执行show()方法后,该fragment将再次显示,之前的所有状态都继续存在。这样做的好处就是不用每次点击某一个item就new 一个fragment的对象,避免了频繁的创建fragment,也避免了每次加载相同的数据,尤其是在实际应用中,可能加载一个主页的fragment会消耗大量的网络资源,不进行fragment的保存,用户体验也会差很多,同样的内容,尤其是一些不是经常变化的数据,每次加载,很让人难接受。
看上面的代码,当点击一个item条目的时候,首先是将之前所有的fragment都进行隐藏,然后进行判断是否存在该fragment,如果存在,就直接将该fragment显示,如果不存在,则new一个该fragment的对象,并将该fragment对象加入transaction事务控制中去。
FragmentManager fragmentManager = getFragmentManager(); FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();一般通过以上两句代码获得fragmenttransaction对象,通过该对象对fragment进行管理。
现在,导航栏的界面NaviFragment已经准备好了,接下来是slidingmenu,通过slidingmenu打开导航栏
3.slidingmenu的实现
slidingmenu对象是通过下面的方法获取到的
SlidingMenu mSlidingMenu = getSlidingMenu();然后,可以设置slidingmenu的一些参数和属性,这里举几个最常用的属性设置
mSlidingMenu.setMode(SlidingMenu.LEFT);// 设置slidingmeni从哪侧出现 mSlidingMenu.setTouchModeAbove(SlidingMenu.TOUCHMODE_MARGIN);// 打开模式 有全屏,仅边界 mSlidingMenu.setBehindOffsetRes(R.dimen.slidingmenu_offset);// 侧滑距离右边界的偏移量
<p style="margin-top: 0px; margin-bottom: 0px; font-size: 11px; font-family: Monaco;"> <span style="white-space:pre"> </span> //mSlidingMenu.setTouchModeAbove(SlidingMenu.<span style="color: #0326cc">TOUCHMODE_FULLSCREEN</span>);<span style="color: #4e9072">//全屏都可以打开</span></p><p style="margin-top: 0px; margin-bottom: 0px; font-size: 11px; font-family: Monaco;"><span style="white-space:pre"> </span> //mSlidingMenu.setTouchModeAbove(SlidingMenu.<span style="color: #0326cc">TOUCHMODE_NONE</span>);<span style="color: #4e9072">//不可以通过滑动打开</span></p> mSlidingMenu.setFadeEnabled(true);//设置侧滑开关时是否需要淡入淡出的效果 mSlidingMenu.setFadeDegree(0.5f);//设置淡入淡出的程序 mSlidingMenu.setMenu(R.layout.frame_navi);
setBehindContentView(R.layout.frame_navi); // 给滑出的slidingmenu的fragment制定layout
public class MainActivity extends SlidingFragmentActivity { private WechatFragment wechatFragment; private NaviFragment naviFragment; @Override public void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); initFragment(); } private void initFragment() { SlidingMenu mSlidingMenu = getSlidingMenu(); setBehindContentView(R.layout.frame_navi); // 给滑出的slidingmenu的fragment制定layout naviFragment = new NaviFragment(); getSupportFragmentManager().beginTransaction() .replace(R.id.frame_navi, naviFragment).commit(); // 设置slidingmenu的属性 mSlidingMenu.setMode(SlidingMenu.LEFT);// 设置slidingmeni从哪侧出现 mSlidingMenu.setTouchModeAbove(SlidingMenu.TOUCHMODE_FULLSCREEN);//全屏都可以打开 mSlidingMenu.setBehindOffsetRes(R.dimen.slidingmenu_offset);// 偏移量 mSlidingMenu.setFadeEnabled(true); mSlidingMenu.setFadeDegree(0.5f); mSlidingMenu.setMenu(R.layout.frame_navi); Bundle mBundle = null; // 导航打开监听事件 mSlidingMenu.setOnOpenListener(new OnOpenListener() { @Override public void onOpen() { } }); // 导航关闭监听事件 mSlidingMenu.setOnClosedListener(new OnClosedListener() { @Override public void onClosed() { } }); } }
4.接下来是几个Fragment界面的实现,这几个界面就是实际开发中最主要的几个界面。
fragment的生命周期不同于activity,但是非常类似,有兴趣的同学可以去研究一下,这里就不列出了。其中的oncreatview是用来加载界面的,返回值view就是要显示的view。
例如我们的首页,wechatfragment
public class WechatFragment extends Fragment { @Override public void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // TODO Auto-generated method stub return inflater.inflat(R.layout.wechat_fargment,null); } }
其他几个fragment的页面跟这个页面类似。
最后,运行程序,就可以得到一个带slidingmenu的fragment应用,点击导航栏中的不同条目,跳转到不同的fragment。
效果图如下所示:
下面,我们来看一种奇怪的现象,fragment出现了重叠的情况,看图:
后面的fragment与首页的fragment出现了重叠的现象,这个在开发过程中是个非常大的bug,你点击通讯录界面的按钮,可能就把微信界面的按钮也给点击了,这个在实际开发中是不允许出现的。
我之前就犯了这个大错误,每次在后面的fragment点击的时候,前面fragment界面也对点击事件进行了响应了,头疼了好久,最后终于找到原因,就是在MainAcitity里面加了这么一句代码:
wechatFragment = new WechatFragment(); getSupportFragmentManager().beginTransaction().replace(R.id.center_frame, wechatFragment).commit();导致了这个问题的产生。
当然,这个问题又可以带来一个应用,比如有好几个fragment标题栏有同一个控件,这个时候,就可以利用这个特性来合理利用了,这个我也用过。不过现在找到这个bug的原因了,就不再这么干了。
fragment还有各种应用,会出现一些意想不到的问题,还有很多值得探索的地方,实际开发中很多问题都涉及到,这里就不细细说明了。
slidingmenu+fragment的简单应用就是这样了。
放上demo的下载地址:http://download.csdn.net/detail/yanglfree/7594673
slidingmenu+fragment实现常用的侧滑效果(包括Fragment状态的保存),布布扣,bubuko.com
slidingmenu+fragment实现常用的侧滑效果(包括Fragment状态的保存)
原文:http://blog.csdn.net/csr_yang/article/details/33502141