xmlns:android=“http://schemas.android.com/apk/res/android” xmlns:app=“http://schemas.android.com/apk/res-auto” xmlns:tools=“http://schemas.android.com/tools” android:orientation=“vertical” android:layout_width=“match_parent” android:layout_height=“match_parent”>
2. 在代码中动态添加,比如我们添加到一个LinearLayout中:
Button button = new Button(mActivity); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.WRAP_CONTENT); button.setLayoutParams(params); container.addView(button);
所以Fragment也很简单,就把它当做一个简单的View(但其实更像是“子 Activity”),然后添加方式也是一样。
1. 直接在Layout.xml中添加:
<?xml version="1.0" encoding="utf-8"?>
2. 直接在代码中添加:
Fragment one = new FragmentOne();//自定义的Fragment类 //要先获取FragmentManager对象 FragmentManager fragmentManager = getSupportFragmentManager(); //开启一个FragmentTransaction事务 FragmentTransaction transaction = fragmentManager.beginTransaction(); transaction.add(R.id.framelayout_view,one).commit();
其中添加到R.id.framelayout_view
的这个id
的ViewGourp
可以是<FrameLayout/>
,也可以是其他的比如<LinearLayout/>
等。
2. Fragment的基本操作
看到我们上面的动态代码添加的时候需要获取FragmentTransaction
和FragmentManager
。
2.1 FragmentManager相关
1. getFragmentManager():
获取Fragment父容器的管理器,但是现在该方法在Activity中已经被标记不推荐使用了。
/** • Return the FragmentManager for interacting with fragments associated • with this activity. • • @deprecated Use {@link android.support.v4.app.FragmentActivity#getSupportFragmentManager()} */ @Deprecated public FragmentManager getFragmentManager() { return mFragments.getFragmentManager(); }
2. getSupportFragmentManager():
v4包下的这个方法,与上一个效果一样,不过是Android推荐使用的方法(毕竟可以兼容Android所有版本)
3. getChildFragmentManager():
我们提过,Fragment更像是一个“子Activity”,那你说"子Activity"中能否再添加Fragment,答案当然是可以。那么在Fragment内部中的Fragment的管理器,就需要使用getChildFragmentManager()
来获取了。
2.2 FragmentTransaction相关
我们可以看到有添加删除各种方法操作。
1. attach/detach方法:
- detach(Fragment fragment) : 分离指定Fragment的UI视图
- attach(Fragment fragment) : 重新关联一个Fragment(当这个Fragment的detach执行之后)
- 当Fragment被detach后,Fragment的生命周期执行完onDestroyView就终止了,这意味着Fragment的实例并没有被销毁,只是UI界面被移除了(注意和remove是有区别的)。
- 当Fragment被detach后,执行attach操作,会让Fragment从onCreateView开始执行,一直执行到onResume。
- attach无法像add一样单独使用,单独使用会抛异常。方法存在的意义是对detach后的Fragment进行界面恢复。
2.add/remove方法:
我想这二个是用的最多的了,add()和remove()是将fragment添加和移除. remove()比detach()要彻底一些, 如果不加入到回退栈中, remove()的时候, fragment的生命周期会一直走到onDetach();如果加入了回退栈,则会只执行到onDestoryView(),Fragment对象还是存在的。
add一个fragment,如果加到的是同一个id的话,有点像我们的Activity栈,启动多个Activity时候,Activity一个个叠在上面,fragment也是类似,一个个fragment叠在上面。
3.replace方法:
replace = remove + add
, 所以可以理解为先把相同id下的Fragment移除掉,然后再加入这个当前的fragment。
所以如果你觉得Fragment存在太多,影响性能,可以用replace来切换各个界面,就可以保证当前只有一个Fragment,但是因为每次切换后,Fragment都会重建,所以如果这个界面有网络请求相关的,你就会发现这个界面又重新去请求网络接口了,显得很多此一举。
4.hide/show方法:
就是字面意思,让一个Fragment隐藏,让一个Fragment显示。你可以理解为Button设置了View.GONE和View.VISIBLE。常常配合有多个Fragment及有TAB等切换方式的时候,选中某个按钮,然后根据相应的让对应的Fragment显示,其他Fragment隐藏。
5.commit/commitAllowingStateLoss:
我估计很多人认识这个commitAllowingStateLoss
大部分是因为自己的代码有闪退异常:
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
在你离开当前Activity
等情况下,系统会调用onSaveInstanceState()
帮你保存当前Activity
的状态、数据等,直到再回到该Activity
之前(onResume()
之前),你执行Fragment
事务,就会抛出该异常。然后网上有很多教程,叫你提交的时候使用commitAllowingStateLoss()
方法,虽然说不会抛出错误,但是如果在Activity
已经保存状态完之后提交了它,到时候Ativity
意外崩溃,再恢复数据的时候就不会恢复在Activity
保存状态之后提交的fragment
的更新,造成状态丢失了。
额外补充:
1.commit()方法并不立即执行transaction中包含的动作,而是把它加入到UI线程队列中. 如果想要立即执行,可以在commit之后立即调用FragmentManager的executePendingTransactions()方法.
2. commit()方法必须在状态存储之前调用,否则会抛出异常,如果觉得状态丢失没关系, 可以调用commitAllowingStateLoss(). 但是除非万不得已, 一般不推荐用这个方法, 会掩盖很多错误.
6. addToBackStack:
我们可以看到FragmentTransaction
里面有加入回退栈方法,但是没有退出的方法:popBackStack
。这是因为这个方法在FragmentManager
里面。
也就是如下图:
一般反应是,addToBackStack和popBackStack不是应该像上面的类似add和remove一样,都一个层级的吗??所以popBackStack不也应该是FragmentTransaction下的一个方法???
所以我们单从图片所示就能知道,popBackStack
与FragmentTransaction
是一个层级,所以popBackStack
操作的其实也是《fragment事务》(FragmentTransaction),所以可以理解为addToBackStack
把我们前面的FragmentTransaction
事务(比如add,remove,replace等一系列操作)加入到了回退栈(!!!记住不是把fragment加入到了回退栈),而popBackStack
是操作回退栈里面的事务。
当然具体的源码过程分析,细讲的话又是很多,都可以另外专门写一篇文章,所以直接借鉴网上别人已经写好的文章:
额外补充:
1.加入回退栈:remove掉的fragment执行onDestoryView,并没有执行onDestory,fragment实例对象还是存在,当回退时候,fragment从onCreateView处执行
2. 未加入回退栈:remove掉的fragment 执行 onDestoryView和onDestory,彻底销毁移除
3.Fragment中获取Context
我们可以直接在fragment代码里面直接使用getActivity()
和getContext()
方法。
但是有时候获取为空,所以一般我们使用的是:
Class xxxFragment extends Fragment { private Context mContext; //‘高版本后,都是回调这个方法’ @Override public void onAttach(Context context) { super.onAttach(context); mContext = context; } //‘API低于 23 的版本的时候,是会回调这个方法’ @Override public void onAttach(Activity activity) { super.onAttach(activity); mContext = activity; } }
4.Fragment配合ViewPager
ViewPager配合Fragment的时候,主要使用FragmentPagerAdapter
和FragmentStatePagerAdapter
这二个Adapter。其实使用很简单(一般的最最简单的写法):
public class FragmentAdapter extends FragmentPagerAdapter{ private ArrayList list; //通过构造获取fragment集合 public Fragment_pager(FragmentManager fm,ArrayList list) { super(fm); this.list=list; } //设置具体position的fragment @Override public Fragment getItem(int position) { // TODO Auto-generated method stub return list.get(position); } //设置有多少个fragment @Override public int getCount() { // TODO Auto-generated method stub return list.size(); } }
然后ViewPager.setAdapter(xxxx);
但是大家会奇怪为啥有二个Adapter:FragmentPagerAdapter
和FragmentStatePagerAdapter
,他们的区别我们可以看具体的源码:
FragmentPagerAdapter源码:
public abstract class FragmentPagerAdapter extends PagerAdapter { //‘初始化创建Item:’ @NonNull public Object instantiateItem(@NonNull ViewGroup container, int position) { if (this.mCurTransaction == null) { this.mCurTransaction = this.mFragmentManager.beginTransaction(); } long itemId = this.getItemId(position); String name = makeFragmentName(container.getId(), itemId); Fragment fragment = this.mFragmentManager.findFragmentByTag(name); if (fragment != null) { //‘后面使用fragment是通过FragmentTransaction.attach方式加进来的,’ //‘只是重新绘制了UI,fragment对象还在。’ this.mCurTransaction.attach(fragment); } else { //‘我们知道刚返回fragment使用的是getItem(position)方法’ //‘我们可以看到第一次使用fragment是通过FragmentTransaction.add方式加进来的’ fragment = this.getItem(position); this.mCurTransaction.add(container.getId(), fragment, makeFragmentName(container.getId(), itemId)); } if (fragment != this.mCurrentPrimaryItem) { fragment.setMenuVisibility(false); fragment.setUserVisibleHint(false); } return fragment; } //‘销毁item:’ public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) { if (this.mCurTransaction == null) { this.mCurTransaction = this.mFragmentManager.beginTransaction(); } //‘我们可以看到FragmentTransaction只是单纯的detach了fragment,视图不在了,但是fragment对象还在’ this.mCurTransaction.detach((Fragment)object); } }
我们可以看到fragment并没有真的销毁,FragmentPageAdapter则适用于固定的,少量的Fragment情况,例如和TabLayout共同使用时。
FragmentStatePagerAdapter源码:
public abstract class FragmentStatePagerAdapter extends PagerAdapter { @NonNull public Object instantiateItem(@NonNull ViewGroup container, int position) { Fragment fragment; if (this.mFragments.size() > position) { fragment = (Fragment)this.mFragments.get(position); if (fragment != null) { return fragment; } } if (this.mCurTransaction == null) { this.mCurTransaction = this.mFragmentManager.beginTransaction(); } fragment = this.getItem(position); if (this.mSavedState.size() > position) { SavedState fss = (SavedState)this.mSavedState.get(position); if (fss != null) { fragment.setInitialSavedState(fss); } } while(this.mFragments.size() <= position) { this.mFragments.add((Object)null); } fragment.setMenuVisibility(false); fragment.setUserVisibleHint(false); this.mFragments.set(position, fragment); //‘我们可以看到fragment都是add进来的’ this.mCurTransaction.add(container.getId(), fragment); return fragment; } public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) { Fragment fragment = (Fragment)object; if (this.mCurTransaction == null) { this.mCurTransaction = this.mFragmentManager.beginTransaction(); } while(this.mSavedState.size() <= position) { this.mSavedState.add((Object)null); } this.mSavedState.set(position, fragment.isAdded() ? this.mFragmentManager.saveFragmentInstanceState(fragment) : null); this.mFragments.set(position, (Object)null); //‘可以看到都是通过remove的方式移除了’ this.mCurTransaction.remove(fragment); }