美文网首页
ViewPager中切换RecyclerView/ListVie

ViewPager中切换RecyclerView/ListVie

作者: 鹈鹕醍醐 | 来源:发表于2018-06-29 19:29 被阅读60次

    在项目开发中我们经常会碰到这样的需求: 一个左右滑动切换的Viewpager,page中是一个列表。这时大家一般都会采用ViewpagerFragment\ 自定义View的方式去实现,每个页面复用Adapter,当page.size > 2的时候,会出现白屏问题Adapter设置或刷新不生效的问题

    Example:
    总共有3页,前两页正常加载,到第三页加载完以后左滑回第一页,这时会发现列表丢失了,变成空白页面了,无论是listViewAdapter.notifyDatasetChanged还是RecyclerViewnotifyXxxx都没有生效。

    排查原因:

      1. 首先发现直接用匿名类方式使用Adapter每次直接new对象是不会出这种问题的,但是这种实现方法很明显太浪费内存了
      1. 执行过 PagerAdapter#destroyItem()方法的页面出现这个问题,我们以FragmentPagerAdapter的源码来看:
     @Override
        public Object instantiateItem(ViewGroup container, int position) {
            final long itemId = getItemId(position);
            // Do we already have this fragment?
            通过name作为tag标记在事务中缓存了Fragment
            String name = makeFragmentName(container.getId(), itemId);
            Fragment fragment = mFragmentManager.findFragmentByTag(name);
            if (fragment != null) {
                复用的时候直接执行attach()方法
                mCurTransaction.attach(fragment);
            } else {
                fragment = getItem(position);
                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);
        }
    

    FragmentPageAdapterdestoryItem()detach掉了需要销毁的fragment,在instantiateItem()方法中获取Fragment重新加载,在复用时直接调用FragmentTransaction.attach()
    理一下Fragment的状态顺序,以index=0fragment为例,

    • 初次实例化的时候通过FragmentTransaction.add()添加到窗口时执行顺序为 Fragment.onAttach()Activity.onAttachFragment(Fragment)Fragment.performCreate()Fragment.onCreate()Fragment.performCreateView()Fragment.onCreateView()Fragment.onViewCreated()
    • detach()时顺序为 Fragment.performDestroyView()Fragment.onDestroyView()Fragment.mContainer.removeView(Fragment.mView)
    • 重新attach()时顺序为
      Fragment.onCreateView()Fragment.onViewCreated()

    onViewCreated()执行的前提是onCreateView()的返回view不为空,
    因此onViewCreated()中的view一定是非空的

    public class RedFragment extends BaseFragment {
    
        private MyAdapter mAdapter;
    
        public static RedBagFragment getInstance(String type){
            ······return fragment;
        }
    
        @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);······
        }
    

    复用时重新调用了fragmentonCreateView(),重新创建了一个新的view,因此RecyclerView\ListView已经是一个全新的对象了,而Adapter仍然对应的是旧的RecyclerView\ListView。代码自然就不会生效了,而是会展示一片空白

    if(mAdapter ==null){
        mAdapter =new MyRedBagAdapter(mData);
        mListView.setAdapter(mAdapter);
    }else {
        mAdapter.notifyDataSetChanged();
    }
    

    解决方法:

    • 1,复用FragmentinflaterView(推荐)
    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);
        }
        return mInflated;
    }
    
    • 2,在onDestoryView时释放adapter(不推荐,内存浪费)
    @Override
    public void onDestroyView() {
        super.onDestroyView();
        mAdapter = null;
    }
    
    • 3, adapter中自定义关联方法,使得adapterListView/RecyclerView双向关联(不推荐,内存浪费 画蛇添足)
    在adapter中,定义attach
    public void setAttachedView(View listView){
        mListView = listView;
    }
    public View getAttachedView() {
        return mListView;
    }
    在fragment中
    void setAdapter(){
        if(mAdapter == null || mAdapter.getAttachedView() != mListView){
            mAdapter = new MyAdapter(mData);
            mAdapter.setAttachedView(mListView);
            mListView.setAdapter(mAdapter);
         } else {
            mAdapter.notifyDataSetChanged();
         }
    }
    

    2 3两个方案比 1 差多了,在对象销毁前旧的View和adapter都是内存垃圾处于泄漏状态,而且多执行了inflatefindViewById操作,消耗更多的计算时间。

    相关文章

      网友评论

          本文标题:ViewPager中切换RecyclerView/ListVie

          本文链接:https://www.haomeiwen.com/subject/kjfzyftx.html