ViewPager+Fragment取消预加载以及禁止滑动

2017-04-15 23:54:49

取消预加载

网上了解了很多取消预加载的方法,里面提到了使用一个viewpager的public方法setOffscreenPageLimit 经过查看源码以及验证发现该方法是管理Viewpager预加载的页数,最低也是默认为一页(例如ViewPager一共有4页,当前手机屏幕显示第一页,那么第二页已经被缓存了)。如果你想多缓存几页可以通过该方法进行设置;所以想要完全取消预加载(一页都不缓存,只加载当前页),通过这个方法是行不通的(通过其他的方法能不能行就不知道了)。

但是在细想之后发现我们取消预加载大多无非是取消掉我们放在这个fragment中的异步操作(我的是放在OnCreate或者OnCreateView方法里加载数据源的),那我们把这个异步操作的方法提取出来,然后在这个fragment的view每次显示的时候调用,不就达到了取消预加载的效果了。所以Fragment的public方法setUserVisibleHint (设置fragment的可见状态)恰好是我们所需要的,我们可以重写他然后在里面获取到fragment的显示状态实现我们的方法回调,这样就大功告成了,下面给出我写的几个重要类以及方法原型。

  • 以下是重写setUserVisibleHint方法的基础类,在用到Fragment的时候继承该类然后将你的异步加载操作方法放入相应的回调方法就能够实现需要的效果:

public abstract class BaseFragment extends Fragment {
    /** Fragment当前状态是否可见 */
    protected boolean isVisible;    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {        super.setUserVisibleHint(isVisibleToUser);        if(getUserVisibleHint()) {
            isVisible = true;
            onVisible();
        } else {
            isVisible = false;
            onInvisible();
        }
    }    /**
     * 可见时的回调方法
     */
    protected abstract void onVisible();    /**
     * 不可见时的回调方法
     */
    protected abstract void onInvisible();
}123456789101112131415161718192021222324252627
  • 以下是setOffscreenPageLimit方法原型(里面的常量DEFAULT_OFFSCREEN_PAGES为整形1):

/**
     * Set the number of pages that should be retained to either side of the
     * current page in the view hierarchy in an idle state. Pages beyond this
     * limit will be recreated from the adapter when needed.
     *
     * <p>This is offered as an optimization. If you know in advance the number
     * of pages you will need to support or have lazy-loading mechanisms in place
     * on your pages, tweaking this setting can have benefits in perceived smoothness
     * of paging animations and interaction. If you have a small number of pages (3-4)
     * that you can keep active all at once, less time will be spent in layout for
     * newly created view subtrees as the user pages back and forth.</p>
     *
     * <p>You should keep this limit low, especially if your pages have complex layouts.
     * This setting defaults to 1.</p>
     *
     * @param limit How many pages will be kept offscreen in an idle state.
     */
    public void setOffscreenPageLimit(int limit) {        if (limit < DEFAULT_OFFSCREEN_PAGES) {
            Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to " +
                    DEFAULT_OFFSCREEN_PAGES);
            limit = DEFAULT_OFFSCREEN_PAGES;
        }        if (limit != mOffscreenPageLimit) {
            mOffscreenPageLimit = limit;
            populate();
        }
    }12345678910111213141516171819202122232425262728
  • 以下是setUserVisibleHint方法的原型

/**
     * Set a hint to the system about whether this fragment's UI is currently visible
     * to the user. This hint defaults to true and is persistent across fragment instance
     * state save and restore.
     *
     * <p>An app may set this to false to indicate that the fragment's UI is
     * scrolled out of visibility or is otherwise not directly visible to the user.
     * This may be used by the system to prioritize operations such as fragment lifecycle updates
     * or loader ordering behavior.</p>
     *
     * @param isVisibleToUser true if this fragment's UI is currently visible to the user (default),
     *                        false if it is not.
     */
    public void setUserVisibleHint(boolean isVisibleToUser) {        if (!mUserVisibleHint && isVisibleToUser && mState < STARTED) {
            mFragmentManager.performPendingDeferredStart(this);
        }
        mUserVisibleHint = isVisibleToUser;
        mDeferStart = !isVisibleToUser;
    }1234567891011121314151617181920

小结一下:取消预加载不需要任何额外的ViewPager属性配置操作,只需要在构建类Fragment的时候继承BaseFragment实现两个抽象方法,然后将异步操作方法放入相应的回调方法即可。

禁止滑动

在布局中引用以下重写的ViewPager CustomViewPager 类,然后再调用其中的public方法setScanScroll 设置(如在Activity的onCreate方法中:

CustomViewPager viewPager = (CustomViewPager) findViewById(R.id.viewpager);viewPager.setScanScroll(false);12

)调用即可。 
以下给出重要的类以及重要注释。

public class CustomViewPager extends ViewPager {

    private boolean isCanScroll = true;    public CustomViewPager(Context context) {        super(context);
    }    public CustomViewPager(Context context, AttributeSet attrs) {        super(context, attrs);
    }    /**
     * 设置其是否能滑动换页
     * @param isCanScroll false 不能换页, true 可以滑动换页
     */
    public void setScanScroll(boolean isCanScroll) {        this.isCanScroll = isCanScroll;
    }    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {        return isCanScroll && super.onInterceptTouchEvent(ev);
    }    @Override
    public boolean onTouchEvent(MotionEvent ev) {        return isCanScroll && super.onTouchEvent(ev);

    }
}1234567891011121314151617181920212223242526272829303132

方法onInterceptTouchEvent API注释:

android.support.v4.view.ViewPager
public boolean onInterceptTouchEvent(@NotNull MotionEvent ev)
Description copied from class: ViewGroup Implement this method to intercept all touch screen motion events. This allows you to watch events as they are dispatched to your children, and take ownership of the current gesture at any point.

实现此方法可拦截所有触摸屏幕事件。这允许您监听事件传递给你的子类,并且在任何点取得当前手势的所有权。

Using this function takes some care, as it has a fairly complicated interaction with View.onTouchEvent(MotionEvent), and using it requires implementing that method as well as this one in the correct way. Events will be received in the following order:
You will receive the down event here.
The down event will be handled either by a child of this view group, or given to your own onTouchEvent() method to handle; this means you should implement onTouchEvent() to return true, so you will continue to see the rest of the gesture (instead of looking for a parent view to handle it). Also, by returning true from onTouchEvent(), you will not receive any following events in onInterceptTouchEvent() and all touch processing must happen in onTouchEvent() like normal.
For as long as you return false from this function, each following event (up to and including the final up) will be delivered first here and then to the target's onTouchEvent().
If you return true from here, you will not receive any following events: the target view will receive the same event but with the action MotionEvent.ACTION_CANCEL, and all further events will be delivered to your onTouchEvent() method and no longer appear here.
Overrides:
onInterceptTouchEvent in class ViewGroup
Parameters:
ev - The motion event being dispatched down the hierarchy.
Returns:
Return true to steal motion events from the children and have them dispatched to this ViewGroup through onTouchEvent(). The current target will receive an ACTION_CANCEL event, and no further messages will be delivered here.

从子类中拦截到移动事件并将事件传递到了ViewGroup则返回true


  • 2019-11-26 13:36:28

    Vue组件命名找不到的问题以及如何给vue组件命名

    首先,Vue 会将 template 中的内容插到 DOM 中,以方便解析标签。由于 HTML 标签不区分大小写,所以在生成的标签名都会转换为小写。例如,当你的 template 为 <MyComponent></MyComponent> 时,插入 DOM 后会被转换为 <mycomponent></mycomponent>。 然后,通过标签名寻找对应的自定义组件。匹配的优先顺序从高到低为:原标签名、camelCase化的标签名、PascalCase化的标签名。例如 <my-component>会依次匹配 my-component、myComponent、MyComponent。camelCase 和 PascalCase 的代码

  • 2019-11-28 11:00:35

    Vue子组件调用父组件的方法

    下面有三种方法,我自己重点推荐第一种,毕竟这种简单粗暴好用好理解,不过这个有一个弊端,再组件嵌套组件的时候,尤其是用第三方组件里面调用自己的子组件的时候,其实已经是孙子组件了,这个时候就要parent.parent。。。。,这样就不好了,我们就得考虑其他方法了,具体怎么判断是父组件,还是爷爷组件,我会单独出一篇文章讲述。

  • 2019-11-29 13:04:47

    计算一个多边形的重心点坐标(准确版)

    在之前的 《如何判断一个多边形是否合法》 一文中有提到,用无人机规划飞行路线前,往往需要框选一个多边形的区域。 而在地图控件上显示这个多边形区域时,往往会遇到这样一个需求:需要把所要测绘的多边形区域移动到地图中心。 实现这个需求的基本思路就是:获取到多边形区域的重心点坐标,然后利用地图控件的 setCenter方法,就可以把地图的显示中心移动到多边形区域重心了。那么问题来了,如何求出一个多边形的重心点坐标呢?

  • 2019-11-29 13:06:27

    如何判断一个多边形是否合法

    利用无人机对一片区域进行测绘前,我们会先在地图上框选一个区域,然后再规划飞行的路线,而需要测绘的这片区域往往是一个多边形。在 MeshKit 中,我们加入了多边形区域的编辑功能,其中就涉及判断用户所编辑出来的多边形是否合法的问题。

  • 2019-11-29 13:47:22

    百度地图做电子围栏总结

    在地图上画出围栏,设置围栏信息后保存,生成围栏列表。全选时,地图视野可看到全部的围栏区域,单独勾选会调整地图视野到当前勾选的围栏。围栏区域的中心点要显示围栏名称。

  • 2019-11-29 13:50:29

    图片连接处出现白线

    block导致,只要父元素设置font-size:0或者设置img display: block; 便可。但是我设置了没有用,这条线不是所有的机型都有,而且页面滚动之后又消失,我琢磨半天,各种尝试,发现把图片高度减少(增加)1px就解决了。因为我们的项目是用postcss-px-to-viewport,我每张图片都是设置高度的,应该是数值转换出现偏差。