Fragment面试题目整理
为什么引入Fragment?
Android在api11中加入了Fragment,主要是给大屏设备上进行动态灵活的UI设计提供支持,更多的情况下我们把Fragment作为一个可重复利用的模块化组件,利用它自身的生命周期来对功能模块进行分离。
Fragment加载到Activity的两种方式:
静态加载,直接添加Fragment到Activity的布局文件房中。
动态的在Activity中添加Fragment
FragmentPagerAdapter与FragmentStatePagerAdapter的区别?
FragmentPagerAdapter一般用于少量界面的ViewPager,划过的Fragment会一直保存在内存中不会被销毁。而FragmentStatePagerAdapter适用于界面较多的ViewPager,它会保存当前的界面以及下一个界面和上一个界面,最多保存三个,其他的会在destroyItem()方法中被销毁调,可以节省内存占用。
Fragmnet的生命周期
Fragment通信
在Fragment中调用Activity中的方法
通过getActivity()方法获取所附属的Activity后调用当中的方法
在Activity中调用Fragment中的方法
采用接口回调的形式
在Fragment中调用Fragment中的方法
findFragmentById()
Fragment的replace、add、remove方法
add 是把一个Fragment添加到一个容器当中。一般在使用add的时候会配合 hide,show一起使用,为了避免Fragment的重复创建节省资源。
replace 在添加Fragment到容器之前先移除掉相同id的所有Fragment然后再进行添加
无法在activity调用了onSaveInstanceState之后再执行commit进行添加Fragment
Activity被系统回收(界面已经不存在了),为了能在下次打开的时候恢复原来的样子,系统为我们保存界面的所有状态,这个时候再去修改界面理论上肯定是不被允许的,为了避免这种异常可以使用:
transaction.commitAllowingStateLoss();
来提交添加Fragment到Activity的事务,与commit()不同的是使用这种方法允许丢失界面的状态和信息。
ViewPager与Fragment结合使用时的懒加载问题
所谓的 “懒加载” 就是数据只有在Fragment对于用户可见的时才进行加载。因为ViewPager会帮我们预先初始化Fragment。由于这个特性,我们不能把数据的加载放到onCreateView方法或者onCreate方法中。
因此,我们需要判定Fragment在什么时候是处于可见的状态。一般我们通常是通过Fragment中的生命周期方法onResume来判断Fragment是否可见,但是由于ViewPager预加载的特性,Fragment即便不可见也会执行onResume方法,因此使用这个方法进行可见性的判断就行不通了。这个时候我们需要用到下面的这个方法来进行Fragment可见性的判断:
setUserVisibleHint()方法:
什么时候被调用?
当fragment被创建的时,setUserVisibleHint(boolean isVisibleToUser)方法会被调用,且传入参数值为false。
当fragment可见时,setUserVisibleHint(boolean isVisibleToUser)方法会被调用,且传入参数值为true。
当fragment由 可见 -> 不可见 时,setUserVisibleHint(boolean isVisibleToUser)方法会被调用,且传入参数值为false。
了解了这个方法之后,我们应该比较清楚懒加载该如何实现了吧,只需要当setUserVisibleHint(boolean isVisibleToUser)方法中的 isVisibleToUser 参数的值为true的时候我们才开始进行数据的加载。但是有一点需要需要注意的是 setUserVisibleHint(boolean isVisibleToUser)方法在Fragment的生命周期方法onCreate 之前调用的,也就是说他并不在Fragment的生命周期中。既然是在 onCreate 方法之前被调用,这样就存在许多不确定因素,如果Fragmnet的View还没有完成初始化之前,就在setUserVisibleHint()方法中进行UI的操作,这样显然会导致空指针的出现。因此我们需要对Fragment创建的View进行缓存,确保缓存的View不为空的情况下我们才可以在setUserVisibleHint方法中进行UI操作。
BaseLazyFragment.java
/**
* Fragment的基类(懒加载)
* Created by wangke on 17-8-15.
*/
public abstract class BaseLazyFragment extends RxFragment {
private String TAG;
//标记当前Fragment的可见状态
private boolean isFragmentVisible;
private boolean isFirstVisible;
//对Fragment中加载的View进行缓存
private View mRootView;
private Unbinder mUnbinder;
public BaseLazyFragment(String TAG) {
this.TAG = TAG;
}
/**
* 当Fragment添加到Activity的时候最先调用此方法
*
* @param context 上下文对象
*/
@Override
public void onAttach(Context context) {
super.onAttach(context);
//获取Fragment之间传递过来的参数
initArgs(getArguments());
}
/**
* 获取Fragment中传递过来的参数,选择重写
*
* @param arguments
*/
protected void initArgs(Bundle arguments) {
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
LogHelper.i("wwk", TAG + " ===>onCreate");
super.onCreate(savedInstanceState);
initVariable();
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(getContentLayoutId(), container, false);
mUnbinder = ButterKnife.bind(this, view);
initWidget(mRootView);
initEvent();
return view;
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
LogHelper.i("wwk", TAG + " ===>onViewCreated");
if (mRootView == null) {
mRootView = view;
if (getUserVisibleHint()) {
if (isFirstVisible) {
//处于可见状态并且Fragment是第一次开启
onFragmentFirstVisible();
isFirstVisible = false;
}
onFragmentVisibleChange(true);
isFragmentVisible = true;
}
}
//直接使用缓存的mRootView
super.onViewCreated(mRootView, savedInstanceState);
}
/**
* 初始化控件事件,选择重写
*/
private void initEvent() {
}
/**
* 初始化组件
*
* @param view
*/
protected abstract void initWidget(View view);
/**
* 当Fragment第一次创建的时候调用,如果不可见则isVisible的参数值为false
* 当Fragment对于用户可见时调用,此时的isVisibleToUser参数值为true
* 当Fragment当前的状态由可见变为不可见时调用,此时的isVisibleToUser参数为false
*
* @param isVisibleToUser true : 可见
* false : 不可见
*/
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
LogHelper.i("tag", TAG + " ==> setUserVisibleHint ");
super.setUserVisibleHint(isVisibleToUser);
//setUserVisibleHint()的调用在Fragment的声明周期外调用,需要保证rootView不为空的时候调用 onFragmentVisibleChange方法
if (mRootView == null) {
return;
}
//第一次被开启,并且对用户可见(这个if语句中的判断一般不会被执行到,setUserVisibleHint方法的调用时mRootView还没有被缓存)
if (isFirstVisible && isVisibleToUser) {
//当Fragment第一次可见的时候调用
onFragmentFirstVisible();
isFirstVisible = false;
}
//Fragment对于用户可见(已经不是第一次开启)
if (isVisibleToUser) {
onFragmentVisibleChange(true);
isFragmentVisible = true;
return;
}
//能执行到这里表明 Fragment对于用户已经处于不可见的状态
if (isFragmentVisible) {
//由 可见 -> 不可见
isFragmentVisible = false;
onFragmentVisibleChange(false);
}
}
/**
* 获取填充Fragment的View的id
*
* @return
*/
protected abstract int getContentLayoutId();
/**
* 当Fragment的状态发生变化的时候调用,用于进行数据的刷新
*
* @param isVisible true 不可见 -> 可见
* false 可见 -> 不可见
*/
protected abstract void onFragmentVisibleChange(boolean isVisible);
/**
* 当Fragment第一次被开启的时候调用,用于请求数据
*/
protected abstract void onFragmentFirstVisible();
/**
* 获取当前Fragment的可见状态
*
* @return
*/
protected boolean isFragmentVisible() {
return isFragmentVisible;
}
/**
* 给变量赋初始值
*/
private void initVariable() {
//标记是否是第一次开启当前Fragment
isFirstVisible = true;
//标记Fragment对于用户是否可见
isFragmentVisible = false;
//缓存Fragment创建出来的View
mRootView = null;
}
@Override
public void onDestroy() {
initVariable();
mUnbinder.unbind();
super.onDestroy();
}
}