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


  • 2017-02-16 08:09:01

    HTML中PRE和p的区别

    pre 元素可定义预格式化的文本。被包围在 pre 元素中的文本通常会保留空格和换行符。而文本也会呈现为等宽字体。 <pre> 标签的一个常见应用就是用来表示计算机的源代码。

  • 2017-02-16 15:14:14

    动态加载js和css

    开发过程中经常需要动态加载js和css,今天特意总结了一下常用的方法。

  • 2017-02-17 17:11:24

    mysql插入数据后返回自增ID的方法c

    产生的ID 每次连接后保存在服务器中。这意味着函数向一个给定客户端返回的值是该客户端产生对影响AUTO_INCREMENT列的最新语句第一个 AUTO_INCREMENT值的。这个值不能被其它客户端影响,即使它们产生它们自己的 AUTO_INCREMENT值。这个行为保证了你能够找回自己的 ID 而不用担心其它客户端的活动,而且不需要加锁或处理

  • 2017-02-21 07:59:45

    不会被 iOS 停掉的网页定时器

    其实这个标题略微有点标题党:iOS 中,除了少数服务(如播放音乐),大部分 App 在用户按了 Home 键之后,过不了多久就会被完全冻结,这对 Safari 同样适用。本文不考虑这样情况,只考虑 Safari 运行时,怎样让定时器持续工作。