在项目开发中我们经常会碰到这样的需求: 一个左右滑动切换的Viewpager
,每一页存在一个列表。这时大家一般都会采用Viewpager
加Fragment
的方式去实现,每个页面复用Adapter
。当我使用如下的代码并且页面多于2个的时候,会出现白屏问题Adapter
设置或刷新不生效的问题:
public class MyFragment extends BaseFragment {......
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle state) {
return inflater.inflate(R.layout.fragment_red, container, false);
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
mListView = (RecyclerView) view.findViewById(R.id.rv_data);
if(mAdapter ==null){
mAdapter =new MyRedBagAdapter(mData);
mListView.setAdapter(mAdapter);
}else {
mAdapter.notifyDataSetChanged();
}
}
FragmentPagerAdapter源码分析:
@Override
public Object instantiateItem(ViewGroup container, int position) {
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) {
复用的时候直接执行attach()方法
mCurTransaction.attach(fragment);
} else {
fragment = getItem(position); //通过name作为tag标记在事务中缓存了Fragment
mCurTransaction.add(container.getId(), fragment, makeFragmentName(container.getId(), itemId));
}
······
return fragment;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
······ 在viewpager销毁页面的时候执行了detach方法,回收了页面
mCurTransaction.detach((Fragment)object);
}
- FragmentPageAdapter在
instantiateItem()
方法中获取Fragment,如果是第一次加载调用FragmentTransaction.add(...)
, 执行的Fragment与Activity的生命周期方法是:Fragment.onAttach()
→Activity.onAttachFragment(Fragment)
→Fragment.performCreate()
→Fragment.onCreate()
→Fragment.performCreateView()
→Fragment.onCreateView()
→Fragment.onViewCreated()
。 - FragmentPageAdapter在
instantiateItem()
方法中复用复用Fragment时FragmentTransaction.attach()
执行的fragment的生命周期方法是:onCreateView()
→onViewCreated()
。 - FragmentPageAdapter在
destoryItem()
中detach掉了需要销毁的fragment,此时fragment的生命周期为:performDestroyView()
→onDestroyView()
→Fragment.mContainer.removeView(Fragment.mView)
。
代码缺陷:
- 分析源码可知
onViewCreated()
执行的前提是Fragment.mView不为空,因此onViewCreated()
方法的view
参数一定是非空的。 - 复用时同样会调用
onCreateView()
,inflate创建一个新的view,因此RecyclerView\ListView也是一个全新的对象了,而Adapter仍然对应的是旧的RecyclerView\ListView。Notify自然就不会生效了,导致展示区一片空白。
解决方法:
-
复用
Fragment#onCreateView(...)
的返回值mInflated
(推荐)private View mInflated; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) { if(mInflated == null) mInflated = inflater.inflate(R.layout.fragment_red, container, false); //findView ........ return mInflated; }
- 或者在
onDestoryView
时强制释放adapter
(不推荐)@Override public void onDestroyView() { super.onDestroyView(); mAdapter = null; }