一、Fragment生命周期
文章图片
Fragment生命周期
先找一张官方给定的生命周期。先简略的解释每个周期的作用:
- onAttach(Context context) : Fragment与Activity完成绑定。传入的Context是宿主Activity,可以利用该上下文让Fragment和Activity通信。
- onCreate( Bundle savedInstanceState) :初始化创建Fragment。参数savedInstanceState获取之前保存的值,同Activity中的一样。
- onCreateView() :加载解析Fragment的布局,同Activity的onCreate类似。此回调方法不建议处理耗时的操作。
- onViewCreated() :布局创建好了,就会回调这个方法。
- onActivityCreated() :此时Activity的onCreate方法已经执行完成,在该方法内可以进行与Activity交互的UI操作,所以在该方法之前Activity的onCreate方法并未执行完成,如果提前进行交互操作,会引发空指针异常。
- onStart() :Fragment由不可见状态变为可见状态。不处在前台,不可交互。
- onResume() :Fragment处于活动状态,用户可与之交互。
- onPause() :Fragment处于暂停状态,但依然可见,不处在前台,不可交互。
- onStop() :Fragment不可见。
- onDestroyView() :销毁与Fragment有关的视图,但未与Activity解除绑定。
- onDestroy() :销毁Fragment。通常按Back键退出或者Fragment被回收时调用此方法。
- onDetach() :与Activity解除绑定。Activity与该Fragment没啥关系了。
使用的是FragmentTransaction该类的操作方法。
方法 | 描述 | 生命周期 |
---|---|---|
add(Fragment) | 将Fragment添加到Activity | onAttach->onCreateView()->onActivityCreated()->onStart()->onResume() |
remove(Fragment) | 从Activity中移除一个已将存在的Fragment | onPause()->onStop()->onDestroyView() ->onDestroy()->onDetach() |
replace(Fragment) | 从Activity中移除当前的Fragment,并添加新的 | 当前的销毁,新的走新建的方法 |
show(Fragment) | 配合hide使用,被hide的fragment再次show,会在Activity中显示 | 不走任何生命周期 |
hide(Fragment) | hide()方法只是隐藏了fragment的view并没有将view从viewtree中删除,随后可用show()方法将view设置为显示 | 不走任何生命周期,可通过fragment.isHidden()判断是否隐藏了 |
attach(Fragment) | 同detach配合使用,从内存中添加到Activity。fragment的状态还依然会保留着,再次调用onCreateView()来重绘视图 | onCreateView()->onActivityCreated()->onStart()->onResume() |
detach(Fragment) | ,而detach则只是销毁其视图结构,将view从viewtree中删除,实例并不会被销毁 | 不走任何生命周期 |
- Fragment第一次加载
- Fragment切换时状态发生变化回调
因为ViewPager是由预加载机制,所以只是用系统给定的生命周期,并不能很好的处理。
public abstract class BaseFragment extends Fragment {// 子类传入布局
protected abstract int getLayoutResource();
private boolean isFragmentVisible;
private boolean isReuseView;
private boolean isFirstVisible;
private boolean hasCreateView;
private View rootView;
//setUserVisibleHint()在Fragment创建时会先被调用一次,传入isVisibleToUser = false
//如果当前Fragment可见,那么setUserVisibleHint()会再次被调用一次,传入isVisibleToUser = true
//如果Fragment从可见->不可见,那么setUserVisibleHint()也会被调用,传入isVisibleToUser = false
//总结:setUserVisibleHint()除了Fragment的可见状态发生变化时会被回调外,在new Fragment()时也会被回调
//如果我们需要在 Fragment 可见与不可见时干点事,用这个的话就会有多余的回调了,那么就需要重新封装一个
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
//setUserVisibleHint()有可能在fragment的生命周期外被调用
if (rootView == null) {
return;
}
hasCreateView = true;
if (isFirstVisible && isVisibleToUser) {
onFragmentFirstVisible();
isFirstVisible = false;
}
if (isVisibleToUser) {
onFragmentVisibleChange(true);
isFragmentVisible = true;
return;
}
if (isFragmentVisible) {
isFragmentVisible = false;
onFragmentVisibleChange(false);
}
}@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initVariable();
}@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
rootView = inflater.inflate(getLayoutResource(),container,false);
return rootView;
}@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
//如果setUserVisibleHint()在rootView创建前调用时,那么
//就等到rootView创建完后才回调onFragmentVisibleChange(true)
//保证onFragmentVisibleChange()的回调发生在rootView创建完成之后,以便支持ui操作
if (!hasCreateView) {
if (getUserVisibleHint()) {
if (isFirstVisible) {
onFragmentFirstVisible();
isFirstVisible = false;
}
onFragmentVisibleChange(true);
isFragmentVisible = true;
}
}super.onViewCreated(isReuseView ? rootView : view, savedInstanceState);
}@Override
public void onDestroyView() {
super.onDestroyView();
}@Override
public void onDestroy() {
super.onDestroy();
initVariable();
}private void initVariable() {
isFirstVisible = true;
isFragmentVisible = false;
rootView = null;
isReuseView = true;
}/**
* 设置是否使用 view 的复用,默认开启
* view 的复用是指,ViewPager 在销毁和重建 Fragment 时会不断调用 onCreateView() -> onDestroyView()
* 之间的生命函数,这样可能会出现重复创建 view 的情况,导致界面上显示多个相同的 Fragment
* view 的复用其实就是指保存第一次创建的 view,后面再 onCreateView() 时直接返回第一次创建的 view
*
* @param isReuse
*/
protected void reuseView(boolean isReuse) {
isReuseView = isReuse;
}/**
* 去除setUserVisibleHint()多余的回调场景,保证只有当fragment可见状态发生变化时才回调
* 回调时机在view创建完后,所以支持ui操作,解决在setUserVisibleHint()里进行ui操作有可能报null异常的问题
*
* 可在该回调方法里进行一些ui显示与隐藏,比如加载框的显示和隐藏
*
* @param isVisible true不可见 -> 可见
*false 可见-> 不可见
*/
protected void onFragmentVisibleChange(boolean isVisible) {
Log.e(getClass().getSimpleName(), "onFragmentVisibleChange: "+isVisible );
}/**
* 在fragment首次可见时回调,可在这里进行加载数据,保证只在第一次打开Fragment时才会加载数据,
* 这样就可以防止每次进入都重复加载数据
* 该方法会在 onFragmentVisibleChange() 之前调用,所以第一次打开时,可以用一个全局变量表示数据下载状态,
* 然后在该方法内将状态设置为下载状态,接着去执行下载的任务
* 最后在 onFragmentVisibleChange() 里根据数据下载状态来控制下载进度ui控件的显示与隐藏
*/
protected void onFragmentFirstVisible() {
Log.e(getClass().getSimpleName(), "onFragmentFirstVisible: ");
}}
四、结合ViewPager使用 ViewPager的给定的两个适配器一个是FragmentPagerAdapter,另一个是FragmentStatePagerAdapter。
(1)FragmentPagerAdapter FragmentPagerAdapter 是一个用于Fragment不多的情况下,因为他会将所有加载过的fragment全部保存在内存中。
源码来看,他就是使用的是attach 和 detach 方法,第一次的时候并没有一个fragment实例,先add一个,再次添加就使用attach ,它并不会整的销毁,当重新attach()时不会走Fragment中的onAttach()生命周期。
@Override
public Object instantiateItem(ViewGroup container, int position) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}final long itemId = getItemId(position);
// Do we already have this fragment?
String name = makeFragmentName(container.getId(), itemId);
Fragment fragment = mFragmentManager.findFragmentByTag(name);
if (fragment != null) {
if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
mCurTransaction.attach(fragment);
} else {
fragment = getItem(position);
if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
mCurTransaction.add(container.getId(), fragment,
makeFragmentName(container.getId(), itemId));
}
if (fragment != mCurrentPrimaryItem) {
fragment.setMenuVisibility(false);
fragment.setUserVisibleHint(false);
}return fragment;
}@Override
public void destroyItem(ViewGroup container, int position, Object object) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
if (DEBUG) Log.v(TAG, "Detaching item #" + getItemId(position) + ": f=" + object
+ " v=" + ((Fragment)object).getView());
mCurTransaction.detach((Fragment)object);
}
(2)FragmentStatePagerAdapter 和FragmentPagerAdapter相反,该适配器适合用于大量的Fragment时,他会完全的销毁Fragment。
@Override
public Object instantiateItem(ViewGroup container, int position) {
// If we already have this item instantiated, there is nothing
// to do.This can happen when we are restoring the entire pager
// from its saved state, where the fragment manager has already
// taken care of restoring the fragments we previously had instantiated.
if (mFragments.size() > position) {
Fragment f = mFragments.get(position);
if (f != null) {
return f;
}
}if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}Fragment fragment = getItem(position);
if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
if (mSavedState.size() > position) {
Fragment.SavedState fss = mSavedState.get(position);
if (fss != null) {
fragment.setInitialSavedState(fss);
}
}
while (mFragments.size() <= position) {
mFragments.add(null);
}
fragment.setMenuVisibility(false);
fragment.setUserVisibleHint(false);
mFragments.set(position, fragment);
mCurTransaction.add(container.getId(), fragment);
return fragment;
}@Override
public void destroyItem(ViewGroup container, int position, Object object) {
Fragment fragment = (Fragment) object;
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
if (DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + object
+ " v=" + ((Fragment)object).getView());
while (mSavedState.size() <= position) {
mSavedState.add(null);
}
mSavedState.set(position, fragment.isAdded()
? mFragmentManager.saveFragmentInstanceState(fragment) : null);
mFragments.set(position, null);
mCurTransaction.remove(fragment);
}
核心源码就是两句:
- 初始化:mCurTransaction.add(container.getId(), fragment);
- 销毁 : mCurTransaction.remove(fragment);
就是采用添加和移除,并没有缓存的功能。
https://www.cnblogs.com/dasusu/p/5926731.html
http://www.cnblogs.com/dasusu/p/6745032.html