ViewPager详解-飞
发布时间: 2023-07-06
一、ViewPager介绍ViewPager使用一个键对象来关联每一页,而不是管理View。这个键用于追踪和唯一标识在adapter中独立位置中的一页。调用方法startUpdate(ViewGroup)表明ViewPager中的内容需要更改。通过调用一次或多次调用instantiateItem(ViewGroup, int)来构造页面视图。调用destroyItem(ViewGroup, int, Object)来取消ViewPager关联的页面视图。最后,当一次更新(添加和/或移除)完成之后将会调用finishUpdate(ViewGroup)来通知adapter, 提交关联和/或取消关联的操作。这三个方法就是用于ViewPager使用回调的方式来通知PagerAdapter来管理其中的页面。一个非常简单的方式就是使用每页视图作为key来关联它们自己,在方法instantiateItem(ViewGroup, int)中创建和添加它们到ViewGroup之后,返回该页视图。与之相匹配的方法destroyItem(ViewGroup, int, Object)实现从ViewGroup中移除视图。当然必须在isViewFromObject(View, Object)中这样实现:return view == object;PagerAdapter支持数据改变时刷新界面,数据改变必须在主线程中调用,并在数据改变完成后调用方法notifyDataSetChanged(), 和AdapterView中派生自BaseAdapter相似。一次数据的改变可能关联着页面的添加、移除、或改变位置。ViewPager将根据adapter中实现getItemPosition(Object)方法返回的结果,来判断是否保留当前已经构造的活动页面(即重用,而不完全自行构造)。二、ViewPager弊端分析1、普通viewpager的实现弊端

如果你不使用setoffscreenpagelimit(int limit)这个方法去设置默认加载数的话是会默认加载页面的左右两页的,也就是说当你进入viewpager的时候和是会被一起加载的,这样同时加载就会造成一些问题,试想我们如果设置了setoffscreenpagelimit为3的话,那么进入viewpager以后就会同时加载4个fragment,像我们平时的项目中在这些fragment中一般都是会发送网络请求的,也就是说我们有4个fragment同时发送网络请求去获取数据,这样的结果显而易见给用户的体验是不好的(如:浪费用户流量,造成卡顿等等)。

2、懒加载的实现弊端
    概念:当需要时才加载,加载之后一直保持该对象。而关于Fragment实现的PagerAdapter都没有完全保存其引用和状态。FragmentPageAdapter需要重建视图,FragmentStatePageAdapter使用状态恢复,View都被销毁,但是恢复的方式不同,而通常我们想得到的结果是,Fragment一旦被加载,其视图也不会被销毁,即不会再重新走一遍生命周期,而且ViewPager为了实现滑动效果,都是预加载左右两侧的页面。我们通常想要实现的两种效果:不提供滑动,需要时才构造,并且只走一遍生命周期,避免在Fragment中做过多的状态保存和恢复。
3、ViewPager预加载

ViewPager的预加载机制。默认缓存当前页面的前后各一个。

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();    }}
4、预加载实现
/** * <pre> *     @author yangchong *     blog  : https://github.com/yangchong211 *     time  : 2017/7/22 *     desc  : 懒加载 *     revise: 懒加载时机:onCreateView()方法执行完毕 + setUserVisibleHint()方法返回true * </pre> */public abstract class BaseLazyFragment extends BaseFragment {    /*     * 预加载页面回调的生命周期流程:     * setUserVisibleHint() -->onAttach() --> onCreate()-->onCreateView()-->     *              onActivityCreate() --> onStart() --> onResume()     */    /**     * 懒加载过     */    protected boolean isLazyLoaded = false;    /**     * Fragment的View加载完毕的标记     */    private boolean isPrepared = false;    /**     * 面可见,false表示不可见     */    @Override    public void setUserVisibleHint(boolean isVisibleToUser) {        super.setUserVisibleHint(isVisibleToUser);        LogUtil.d("setUserVisibleHint---"+isVisibleToUser);        //只有当fragment可见时,才进行加载数据        if (isVisibleToUser){            lazyLoad();        }    }    /**     * 调用懒加载     * 面时停止加载数据,可以覆写此方法            if (isLazyLoaded) {                stopLoad();            }        }    }    /**     * 视图销毁的时候讲Fragment是否初始化的状态变为false     */    @Override    public void onDestroyView() {        super.onDestroyView();        isLazyLoaded = false;        isPrepared = false;    }    /**     * 面时停止加载数据,可以覆写此方法。     * 存在问题,如何停止加载网络     */    protected void stopLoad(){    }    /**     * 面和需要销毁页面,然后通过调用Adapter的instantiateItem和destroyItem两个方法初始化新页面和销毁不需要的页面!根据newCurrentItem和mOffscreenPageLimit计算要加载的page页面,计算出startPos和endPos根据startPos和endPos初始化页面ItemInfo,先从缓存里面获取,如果没有就调用addNewItem方法,实际调用mAdapter.instantiateItem将不需要的ItemInfo移除: mItems.remove(itemIndex),并调用mAdapter.destroyItem方法设置LayoutParams参数(包括position和widthFactor),根据position排序待绘制的View列表:mDrawingOrderedChildren,重写了getChildDrawingOrder方法最后一步获取当前显示View的焦点:currView.requestFocus(View.FOCUS_FORWARD)*/void populate(int newCurrentItem) {        ItemInfo oldCurInfo = null;        if (mCurItem != newCurrentItem) {            oldCurInfo = infoForPosition(mCurItem);            mCurItem = newCurrentItem;        }        if (mAdapter == null) {            sortChildDrawingOrder();            return;        }        // Bail now if we are waiting to populate

微信