Android 在 ViewPager 中使用 Fragment 的懒加载

转载:https://www.jb51.net/article/129574.htm

ViewPager+Fragment的搭配在日常开发中也比较常见,可用于切换展示不同类别的页面,我们日常所见的咨询、购物、金融、社交等类型的APP都有机会用到这种控件组合.

如:

ViewPager控件有个特有的预加载机制,即默认情况下当前页面左右两侧的1个页面会被加载,以方便用户滑动切换到相邻的界面时,可以更加顺畅的显示出来。预加载让用户可以更快的看到接下来的内容,浏览起来连贯性更好,但是app在展示内容的同时还增加了额外的任务,这样可能影响界面的流畅度,并且可能造成流量的浪费。

所以Fragment使用懒加载是非常有必要的。

试想那么多的分类如果一下子都加载出来,真的是极大地消耗了系统资源。可能有人会说 ViewPager 有 viewPager.setOffscreenPageLimit() 的方法,我们传个 0 进去不就好了吗?但是通过ViewPager方法setOffscreenPageLimit(int limit)的源码可以发现,ViewPager通过一定的逻辑判断来确保至少会预加载左右两侧相邻的1个页面,也就是说无法通过简单的配置做到懒加载的效果.

ViewPager方法setOffscreenPageLimit(int limit) 相关源码:

public void setOffscreenPageLimit(int limit) {
    if (limit < 1) {
        Log.w("ViewPager", "Requested offscreen page limit " + limit + " too small; defaulting to " + 1);
        limit = 1;
    }

    if (limit != this.mOffscreenPageLimit) {
        this.mOffscreenPageLimit = limit;
        this.populate();
    }

}

如何做到懒加载?

实现思路:

使用Fragment类自带方法setUserVisibleHint()判断当前fragment是否对用户可见,根据回调的isVisibleToUser参数来进行相关的逻辑判断。重写该方法,创建变量isVisible拿到是否可见标志。

但是直接根据isVisible判断就加载数据,可能onCreateView()方法并未执行完毕,此时就会出现NullPointerException空指针异常。所以就需要满足控件初始化完成,用户可见,才能加载数据。

由于ViewPager内会装载多个Fragment,而这种懒加载机制对于各个Fragment属于共同操作,因此适合将其抽取到BaseFragment中.

注意:

setUserVisibleHint(boolean isVisibleToUser)方法会多次回调,而且可能会在onCreateView()方法执行完毕之前回调.如果isVisibleToUser==true,然后进行数据加载和控件数据填充,但是onCreateView()方法并未执行完毕,此时就会出现NullPointerException空指针异常.

基于以上原因,我们进行数据懒加载的时机需要满足两个条件

  1. onCreateView()方法执行完毕
  2. setUserVisibleHint(boolean isVisibleToUser)方法返回true

所以在BaseFragment中用两个布尔型标记来记录这两个条件的状态.只有同时满足了,才能加载数据

LazyloadFragment 代码:

public abstract class LazyloadFragment extends Fragment {

    protected View rootView;
    private boolean isInitView = false;
    private boolean isVisible = false;
    
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        rootView = inflater.inflate(setContentView(), container, false);
        init();
        isInitView = true;
        isCanLoadData();
        return rootView;
    }

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        
        //isVisibleToUser这个boolean值表示:该Fragment的UI 用户是否可见,获取该标志记录下来
        if (isVisibleToUser) {
            isVisible = true;
            isCanLoadData();
        } else {
            isVisible = false;
        }
    }

    private void isCanLoadData() {

        //所以条件是view初始化完成并且对用户可见
        if (isInitView && isVisible) {
            lazyLoad();
            //防止重复加载数据
            isInitView = false;
            isVisible = false;
        }

    }
}

Fragment 代码:

public class PageFragment extends LazyloadFragment implements XRecyclerView.LoadingListener {
    private CommonAdapter<String> adapter;
    private ArrayList<String> datas = new ArrayList<>();
    private XRecyclerView recyclerView;
    private Handler handler = new Handler();

    @Override
    public int setContentView() {
        return R.layout.fragment_page;
    }


    @Override
    public void init() {
        recyclerView = rootView.findViewById(R.id.recyclerview);
        recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
        adapter = new CommonAdapter<String>(getActivity(),R.layout.item,datas) {
            @Override
            protected void convert(ViewHolder holder, String s, int position) {

            }
        };
        recyclerView.setAdapter(adapter);
        recyclerView.setPullRefreshEnabled(true);
        recyclerView.setLoadingListener(this);

    }

    @Override
    public void lazyLoad() {
        recyclerView.refresh();
    }

    @Override
    public void onRefresh() {
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                recyclerView.refreshComplete();
                for(int i=0;i<10;i++){
                    datas.add("");
                }
                adapter.notifyDataSetChanged();
            }
        },500);
    }

    @Override
    public void onLoadMore() {

    }
}

MainActivity 代码:

public class MainActivity extends AppCompatActivity {
    private TabLayout tabLayout;
    private String[] topics = new String[]{"推荐","热点","北京","视频","社会","图片"};
    private ViewPager viewPager;
    private ArrayList<Fragment> fragments = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
        setContentView(R.layout.activity_main);

        init();
    }

    private void init() {
        viewPager = (ViewPager) findViewById(R.id.viewpager);
        tabLayout = (TabLayout) findViewById(R.id.tablayout);
        viewPager.setOffscreenPageLimit(3);

        for(int i=0;i<topics.length;i++){
            tabLayout.addTab(tabLayout.newTab());
            fragments.add(new PageFragment());
        }
        viewPager.setAdapter(new FmPagerAdapter(fragments,getSupportFragmentManager()));
        tabLayout.setupWithViewPager(viewPager);

        for (int j = 0; j < topics.length; j++) {
            tabLayout.getTabAt(j).setText(topics[j]);
        }
    }
}

大坑:

大家千篇一律地说用setUserVisibleHint()方法就可以了,但是没有说这个问题。是不是用了Lazyloadfragment不加载数据了?因为你用的是Viewpager用的是PagerAdapter,用pageradapter,打断点调试,根本就没有调用setUserVisibleHint(),所以isVisible还是false,不执行lazyload方法。需要用FragmentPagerAdapter显示调用setUserVisibleHint()。

猜你喜欢

转载自blog.csdn.net/hanxiongwei/article/details/82626978