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


  • 2020-11-22 23:00:16

    androidx中的lifecycle组件

    Lifecycle-aware components生命周期感知组件执行操作,以响应另一个组件生命周期状态的更改,例如Activity和Fragment。这些组件可以帮助您生成更有组织、更容易维护的轻量级代码。

  • 2020-11-22 23:02:50

    Android数据存储之DataBase的Room

    Room是Google在AndroidX中提供的一个ORM(Object Relational Mapping,对象关系映射)库。它是在SQLite上提供的一个抽象层,可以使用SQLite的全部功能,同时可以更好更便捷流畅地访问数据库。(关于AndroidX可以参考

  • 2020-11-22 23:04:39

    Android组件 LiveData与MutableLiveData教程

    LiveData与ViewMode是经常搭配在一起使用的,但是为了不太混乱,我还是拆分开来说明,此篇博客只讲解 LiveData 与 MutableLiveData的概念与使用方式(但是会涉及到ViewMode的部分代码).

  • 2020-11-22 23:14:52

    Dagger 2 在 Android 上的用法

    在前面的文章我们介绍了Dagger2 中的大部分注解的使用,接下来我们从源码角度分析下第一篇文章中例子的原理。

  • 2020-11-22 23:18:59

    Android开发从Dagger2迁移至Kodein的感受

    最近个人在尝试构建 Kotlin版本 的Android MVVM开发框架,在依赖注入框架的选型上,我最终选择了 Kodein 。这是一个非常轻量级的DI框架,相比于配置繁琐的Dagger(繁琐的配置也是导致Dagger学习成本一直居高不下的原因!),它的配置过程更清晰且简单,并且,这个库的源码也是 Kotlin 的。

  • 2020-11-22 23:25:56

    Dagger2源码解析inject过程

    添加inject后,通过编译生成的DaggerMainComponent类来导入,说明编译以后生成了一些类,那到底生成了什么类呢。 Module和Component又是什么,该怎么里理解 在这篇文章后里将一一讨论。

  • 2020-11-22 23:27:28

    dagger学习教程

    dagger android 学习(一):dagger基础使用 dagger android 学习(二):AndroidInjector的使用 dagger android 学习(三):ContributesAndroidInjector的进一步优化 dagger android 学习(四):基于dagger2的mvp架构

  • 2020-11-22 23:31:22

    Dagger2与AndroidInjector详解

    相信使用过Dagger开发Android应用的小伙伴会知道(如果你还不是很了解Daager,可以先看我之前的一篇基本介绍:Dagger2使用攻略),我们会在Activity或Fragment的生命周期方法中执行成员注入。比如这样:

  • 2020-11-23 08:52:59

    asm.js 和 Emscripten 入门教程

    asm.js 就是为了解决这两个问题而设计的:它的变量一律都是静态类型,并且取消垃圾回收机制。除了这两点,它与 JavaScript 并无差异,也就是说,asm.js 是 JavaScript 的一个严格的子集,只能使用后者的一部分语法。

  • 2020-11-23 09:11:07

    爬虫——记一次破解前端加密详细过程

    从最初使用webdriver+selenium爬虫到现在利用http请求解析html,经历过各种各样的问题,webdriver+selenium这种办法虽然万能,而且可以用JS写解析脚本方便调试,