ViewPager的onPageScrolled回调不完整

问题描述

在开发时我们可能经常需要使用到ViewPager的onPageScrolled(int position, float offset, int offsetPixels)方法来获取ViewPager的滚动信息,然而在使用中发现onPageScrolled并不能准确地回调每一个滚动信息,可能会错过一些信息,在一些手机上甚至连onPageScrolled滚动停止(offset参数为0的状态)的信息也不会回调。

问题分析

通过阅读ViewPager的源码知道,ViewPager的滚动是配合Scroller实现的,而Scroller的实现原理就是在Scroller的动画时间内不断的调整View的位置然后重绘布局,而onPageScrolled的回调和view位置的调整是同步的,但是View的重新绘制是耗时的,这也就是说,onPageScrolled的回调频率和view的重新绘制相关联,如果手机的性能差一点,View的刷新次数少,那么onPageScrolled的回调次数也会相对减少。

后来又发现onPageScrolled的最终状态的回调在部分手机上存在问题,有部手机很卡也能正常回调,有部手机比较流畅却也没有回调,所以可能和安卓系统版本有关,于是对比了一下ViewPager在各个版本的实现,发现了问题;我们知道要通过Scroller实现滚动需要实现computeScroll方法,在这个方法里判断滚动是否已完成,没有完成则通过scrollTo方法滚动到相应的位置。下面是ViewPager的实现:

@Override
public void computeScroll() {
    if (!mScroller.isFinished() && mScroller.computeScrollOffset()) {
        int oldX = getScrollX();
        int oldY = getScrollY();
        int x = mScroller.getCurrX();
        int y = mScroller.getCurrY();
        if (oldX != x || oldY != y) {
            scrollTo(x, y);
            if (!pageScrolled(x)) {
                mScroller.abortAnimation();
                scrollTo(0, y);
            }
        }

        // Keep on drawing until the animation has finished.
        ViewCompat.postInvalidateOnAnimation(this);
        return;
    }

    // Done with scroll, clean up state.
    completeScroll(true);
}

可以看到如果mScroller没有Finished则会通过pageScrolled回调onPageScrolled 并滚动到指定位置,如果已经Finished则调用completeScroll方法完成最终的状态,我们的问题是onPageScrolled在最终状态的调用在有些手机上不正常,所以这其中的玄机很可能发生在completeScroll方法中,于是进一步查看completeScroll方法:

private void completeScroll(boolean postEvents) {
    boolean needPopulate = mScrollState == SCROLL_STATE_SETTLING;
    if (needPopulate) {
        // Done with scroll, no longer want to cache view drawing.
        setScrollingCacheEnabled(false);
        mScroller.abortAnimation();
        int oldX = getScrollX();
        int oldY = getScrollY();
        int x = mScroller.getCurrX();
        int y = mScroller.getCurrY();
        if (oldX != x || oldY != y) {
            scrollTo(x, y);
            if (x != oldX) {
                pageScrolled(x);
            }
        }
    }
    .....省略部分代码
}

这里主要注意下面这个判断,通过查阅各个版本的源码,发现只有在api23以上才有这个判断

if (x != oldX) {
      pageScrolled(x);
}

而这个判断就是回调onPageScrolled方法的。所以答案就出来了:假如不是api23的手机,如果scroller已经finish状态(动画的时间到),而view还没来得及完全刷新,就会走到completeScroll方法,而这时api23以下的手机不回回调onPageScrolled方法。

问题总结

ViewPager的onPageScrolled方法比较不可靠,如果有一些比较关键的代码要放在这里处理最好改用其它方式实现,不然稳定性不高,容易出问题。

时间: 2024-11-05 02:51:38

ViewPager的onPageScrolled回调不完整的相关文章

ViewPager的 onPageScrolled 规律总结实现动画(一)

ViewPager OnPageChangeListener的三个方法: onPageSelected(int arg0):滑动停止后调用,arg0表示当前选中页面的position onPageScrollStateChanged(int arg0):滑动状态改变时调用,有三种状态:1 开始滑动  2 滑动完毕  0 保持不变  所以滑动过程中变化顺序是: 1 ->2 ->0 onPageScrolled(int position, float positionOffset, int pos

TP5.1接入支付宝实现网页/APP支付完整请求回调流程(沙箱环境)

目前互联网项目如果涉及到第三方支付模块,那么支付宝/微信无非是最好的选择,此文先以支付宝为例讲解,想了解微信支付的可以看我之后的文章,当然支付也分很多种形式,比如扫码付.当面付.声波付.调用APP付,网页直接付等等.但最常用的形式还是服务端+APP+调用支付宝APP或服务端+网页扫码/调用支付宝APP/直接支付,所以接下来我就以ThinkPHP5.1作为服务端从接入SDK到实现支付请求以及回调业务流程完整的操作讲解一下,虽然我用的是TP5.1但是TP5和TP5.1在此使用过程中没有太大的区别,无

ViewPager的缓存机制

1.实现Viewpager的页面懒加载: 在某些情况下,例如使用ViewPager查看多张大图,此时多张图片不能一次性载入,只有在浏览该页面时才载入(或者预先载入下一页面)页面的具体内容. 2.可控Viewpager缓存页面的数量: 常见的情况: (1)页面的总数是已知的,或者可以计算出来,每个页面占用的资源并不多并且需要经常使用这些页面.这时可以考虑将其常驻ViewPager而不去销毁(频繁的销毁和重建也会消耗比较多的资源). (2)切换页面时默认情况下非相邻的页面会被销毁掉(ViewPage

Android 实现带指示器的自动轮播式ViewPager

前言 最近在做项目的时候,有个需求就是实现自动轮播式的ViewPager,最直观的例子就是知乎日报顶部的ViewPager,它内部有着好几个子view,每个一段时间便自动滑动到下一个item view,而底部的指示器也随之跟着改变.使用这种ViewPager的好处是在有限的空间内可以展示出多样化的信息.轮播式ViewPager广泛应用于各种应用内部,用于展示广告等.抱着学习和分享的目的,笔者把轮播式ViewPager写成了一个独立的控件,以方便以后的使用. 效果展示 话不多说,我们先来看看实现的

处女男学Android(十一)---Gallery、ViewPager和ViewPager+Fragment实现的Tab导航

一.前言 有阵子没更新博客了,主要是最近公司接了个P2P的金融借贷项目没人做,被拉去写服务端,所以迟迟没时间继续学习大安卓,想了想自己的安卓水平和公司的专业安卓璟博比起来依旧差距挺大,于是乎我要加把劲赶上才行,所以继续翻开李刚疯狂讲义系列,看到Gallery这个控件了,大致功能是横向滚动查看列表项,再仔细看了一下居然过时了,官方推荐用ViewPager来替代,还没学就过时了,有点不爽,干脆新的旧的一起学习一下,也好进行一下比较吧.废话不多说,首先是已经过时的Gallery. 二.画廊视图Gall

ViewPager的简单用法+适配器+监听器的介绍

之前的actionbar+fragment文章中写过viewpager的简单用法,但因为是融合的文章,所以今天把viewpager提取出来写了.方便查询浏览~ 思路: 1.在布局文件中设置viewpager控件 2.在acitvity中找到它 3.自定义一个适配器,这个适配器需要一个list<View>来构造 4.定义一个list<View>来放入几个view,并用其来初始化适配器 5.给viewPager来设置适配器和监听器 实现: 1.布局文件,这里放一个指示文字.实际中可以用

使用PagerSlidingTabStrip实现顶部导航栏

在开发中,我们有时会遇到顶部导航栏滑动切换页面的设计,如网易新闻.实现的方式有很多种,今天我们使用PagerSlidingTabStrip配合ViewPager实现顶部导航栏. 效果图如下. PagerSlidingTabStrip是github上的一个开源项目,项目地址如下.https://github.com/astuetz/PagerSlidingTabStrip (一)PagerSlidingTabStrip的使用 在使用之前,我们先来看一下PagerSlidingTabStrip中的自

Android淘宝电影日期滚动栏的实现

最近又有大片上映了,前几天刚看完<末日崩塌>,<侏罗纪世界>又来了,对于大片迷来说是一种福利,所以这几天手机上装了各种电影票团购软件,没办法,同样的电影同样的电影院同样的座位,但是不同的团购软件,价格就不一样.ok,言归正传 在淘宝电影上面有这样一个功能,日期可以滑动,并且选中的是在正中间,效果如下: 看完了,那么问题来了.这个功能怎么实现呢? 我们先来分析一下: 把功能拆分一下来看,如果不能滚动,是不是很好实现?其实就是一个 tab 栏,我在前面的 blog 中Android 快

Loader详解

1.装载器API概述 Class/Interface 说明 LoaderManager 一个抽像类,关联到一个Activity或Fragment,管理一个或多个装载器的实例.这帮助一个应用管理那些与Activity或Fragment的生命周期相关的长时间运行的的操作.最常见的方式是与一个CursorLoader一起使用,然而应用是可以随便写它们自己的装载器以加载其它类型的数据. 每个activity或fragment只有一个LoaderManager.但是一个LoaderManager可以拥有多