叨叨ViewPager那些事儿(二)

前言

上一篇叨叨ViewPager那些事儿(一)说了一点ViewPager概况,这篇打算说说实际应用。


从动画效果说起

先祭上一份谷歌官方的Transformer示例
效果如下


代码实现是这样

public class DepthPageTransformer implements ViewPager.PageTransformer {
    private static final float MIN_SCALE = 0.75f;

    public void transformPage(View view, float position) {
        int pageWidth = view.getWidth();

        if (position < -1) { // [-Infinity,-1)
            // This page is way off-screen to the left.
            view.setAlpha(0);

        } else if (position <= 0) { // [-1,0]
            // Use the default slide transition when moving to the left page
            view.setAlpha(1);
            view.setTranslationX(0);
            view.setScaleX(1);
            view.setScaleY(1);

        } else if (position <= 1) { // (0,1]
            // Fade the page out.
            view.setAlpha(1 - position);

            // Counteract the default slide transition
            view.setTranslationX(pageWidth * -position);

            // Scale the page down (between MIN_SCALE and 1)
            float scaleFactor = MIN_SCALE
                    + (1 - MIN_SCALE) * (1 - Math.abs(position));
            view.setScaleX(scaleFactor);
            view.setScaleY(scaleFactor);

        } else { // (1,+Infinity]
            // This page is way off-screen to the right.
            view.setAlpha(0);
        }
    }
}

乍一看有点懵懂,一步一问 慢慢来

transformPage(View view, float position)两个参数是何含义


第一个参数为各页卡对象,第二个是各个页卡对于当前所展示页卡的相对位置,例如,当前页卡的position为0,下一张位置为1,前一张则为-1。而ViewPager滑动中,position的值是处于平滑变化中的,这就为我们处理动画提供了机会。

为何position < -1position > 1时设置可见度为0
如前问分析,position < -1position > 1指代页卡所处区间为[-Infinity,-1)和(1,+Infinity],在可见范围之外,从节省绘制资源的角度出发,自然可见度为0。

position (0,1]区间时为何要设置偏移度为(pageWidth * -position)
官方注释Counteract the default slide transition,抵消页面滑动时的默认偏移量。如页面处在中间时偏移量为0*width,移至右边为1*width,从中间往右移除时position0-->1渐变,默认偏移量为position*width

滑动动画的缩放效果如何计算
如效果图示,当下一张页卡相对位置从1-->0变化时,scale的变化方向为MIN_SCALE(m)-->1,不妨列个公式
(1-position)/(position-0)=(m-scale)/(scale-1)s=(1-position)/(1-position*m)(对。。跟示例代码不一样。。但是效果一样哦。。
也可以理解为1-scale=|(m-1)|/(1-0)*position,scale从1-->m,位置从0-->1,即每单位position的变化量为|(m-1)|/(1-0),乘上position得出变化量。

理论储备到位之后,自力更生的第一步就是,实践!
下边我们来写一个左右位移,同时展示三张卡片的动画,预想中效果大概是这样


根据设想计算,左右两边的页卡大小应为中间大页卡的0.9倍,左右页卡的偏移量约为±0.2倍页卡宽。
故设定MIN_Trans_INDEX = 0.2f,MIN_SCALE_INDEX = 0.9f。如上分析,我们只关心[-1,1]区间页卡的动画,
每单位position的scale变化量绝对值为(1-MIN_SCALE_INDEX)/(1-0),即1-scale=|position|*(1-MIN_SCALE_INDEX)/(1-0)scale=1-|position|*(1-MIN_SCALE_INDEX)
同理,每单位position的translationx变化量为pageWidth*(1-MIN_Trans_INDEX)/(1-0),即|0-translationx|=|position|*pageWidth*(1-MIN_Trans_INDEX)|translationx|=|position|*pageWidth*(1-MIN_Trans_INDEX),然而viewpager页卡滑动过程中的默认偏移量绝对值为pageWidth * position,故需在上式基础上减pageWidth * position,|translationx|=|position|*pageWidth*MIN_Trans_INDEX
计算完毕,写入代码后发现,效果是出来了,但是反应好像永远。慢半拍。当前页滑动到位后,下一页才出现,体验不太美妙,如下动图所示

查看代码后思考,应该是[-1,1]位置限定导致,左右两页分别滑动到-1和1时才开始做动画,而此时这两张页卡已经出现在屏幕上,所以看起来就像“慢了半拍”。
尝试增加一点“缓冲量”,改动如下

public class MyTransPageTransformer implements ViewPager.PageTransformer {
    private static final float MIN_Trans_INDEX = 0.2f;
    private static final float MIN_SCALE_INDEX = 0.9f;

    @Override
    public void transformPage(@NonNull View page, float position) {
        int pageWidth = page.getWidth();
        if (position < -1.1) { // [-Infinity,-1.1)
            // This page is way off-screen to the left.
            page.setAlpha(0);
        } else if (position <= 0) { // [-1.1,0]
            // Use the default slide transition when moving to the left page
            page.setAlpha(1);
            page.setTranslationX(-pageWidth * position * MIN_Trans_INDEX);
            page.setScaleX((1 - MIN_SCALE_INDEX) * position + 1);
            page.setScaleY((1 - MIN_SCALE_INDEX) * position + 1);
            Log.i("test", page.getTag()+"--"+((1 - MIN_SCALE_INDEX) * position + 1));
        } else if (position <= 1.1) { // (0,1.1]
            // Fade the page out.
            page.setAlpha(1);
            // Counteract the default slide transition
            page.setTranslationX(-pageWidth * position * MIN_Trans_INDEX);
            page.setScaleX(1 - (1 - MIN_SCALE_INDEX) * position);
            page.setScaleY(1 - (1 - MIN_SCALE_INDEX) * position);
        } else { // (1.1,+Infinity]
            // This page is way off-screen to the right.
            page.setAlpha(0);
        }
    }
}

再次运行。这才是想要的效果嘛


问题来了

之前遇到过一个现象,刷新ViewPager,调用notifyDataSetChanged(),如下。

诶,怎么肥事!左右两边的页卡去哪了!冷静一下,刷新时对当前页卡重绘,若不需滚动,则pageoffset始终为0,transformer的动画效果无法显示。看来要想平稳刷新,还需手动更新单条数据。
行动起来,更改如下逻辑

@Override
    public void onClick(View v) {
        if (v.getId() == R.id.tv_notify) {
            if (mCurPageIndex == 3) {//测试,代表逻辑需跳转到的页卡
                isNeedNotify = true;
            }
            //更新当前页卡数据
            updateViewPager(mCurPageIndex);
        }
    }
private void updateViewPager(int position) {
        if (isNeedNotify) {//需要滚动时,调用notifyDataSetChanged重走instantiateItem和destroyItem逻辑
            mPagerAdapter.notifyDataSetChanged();
            mViewPager.setCurrentItem(3);
            isNeedNotify = false;
        }
        //仅需更新当前页时则单独刷新当前页卡view
        MyViewPagerItem item = mPageItemList.get(position);
        if (item != null) {
            PageItemBean bean = new PageItemBean();
            bean.num = position;
            bean.tip = mContext.getString(R.string.num_tip, position + 1 + "");
            item.updateUI(bean);
        }
    }

重跑程序,问题解决,Demo先放下。
为解决问题临时想的方案,是实现更新的一条思路但感觉不够优,欢迎各路大神指点。


最后

本想多写一点,但动画已经占了不少篇幅,先在这结束吧。这个系列会努力补充,欢迎多多指点和关注。

你可能感兴趣的:(叨叨ViewPager那些事儿(二))