Android ViewPager 删除动画

ViewPager作为Android中使用最多的组件之一,具有良好的稳定性。接到交互的需求,想在项目的Viewpager的卡片删除中加入一些动画,提升逼格。具体的实现如下面的gif所示:

View删除动画

如果觉得效果可以,请您赏脸接着看。如果觉得low逼,不耽误您的喝茶时间。
直接甩源码下载链接:
赚点积分下别人的,望您赏脸

—————-噪起来气氛分割线————-

ViewPager可以通过设置setPageTransformer()来设置每个View在滑动时的切换动画。但交互给的需求却不是简单的单页切换,比如有3张卡片,删除第1张时,第2张要滑动到第1张的位置,第3张要滑动到第2张的位置,需要多张联动,同时三者的动画也不同。所以仅仅通过设置Page的切换动画是不行的。
经过分析决定采用View动画+Viewpager更新的方式实现。也就是现在指定的位置执行动画播放,然后再去更新Viewpager。

动画分两段:主卡片消失动画+副卡片移入动画,线性执行。
主卡片消失动画:startCardDisappearAnimation()
负卡片移入动画:startCardPushAnimation()

    private void startCardDisappearAnimation() {
        ...
        AnimationSet animationSet = new AnimationSet(true);
        //透明度变化动画
        AlphaAnimation alphaAnimation = new AlphaAnimation();
        animationSet.addAnimation(alphaAnimation);
        //缩放动画
        ScaleAnimation scaleAnimation = new ScaleAnimation();
        animationSet.addAnimation(scaleAnimation);
        //移动动画
        TranslateAnimation translateAnimation = new TranslateAnimation();
        animationSet.addAnimation(translateAnimation);
        ...

        view.startAnimation(animationSet);
        //动画完成监听
        animationSet.setAnimationListener(new Animation.AnimationListener(){
            @Override
            public void onAnimationStart(Animation animation) {
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                //卡片推出
                startCardPushAnimation();
            }

            @Override
            public void onAnimationRepeat(Animation animation) {
            }
        });
    }

卡片推出的动画是一个集合包括透明度变化,缩放变化,位移动画三种。其中位移动画的终点可以指定移动到某个其它控件的后面,比如代码中是移动到底部TextView的位置。对动画播放执行监听,当动画播放完之后onAnimationEnd(),执行卡片推出动画startCardPushAnimation()。

   //卡片推出动画
   private void startCardPushAnimation() {
        View view = mCardList.get(mCurrentPage);
        mNewPosition = mCurrentPage;
        int cardSize = mCardList.size();
        //获得消失View在屏幕上的位置
        int[] location = new int[2];
        view.getLocationOnScreen(location);
        int endLeft = location[0];
        if (mNewPosition < (cardSize - 2)) {//右边移动两张
            //第二张卡片动画
            ...
            View viewRight1 = mCardList.get(mNewPosition + 1);
            Animation anim1 = getTranslateAnimation(0, endLeft - rightEnd, 0, 0, true);
            //第三张卡片动画
            ...
            final View viewRight2 = mCardList.get(mNewPosition + 2);
            final Animation anim2 = getTranslateAnimation(0, rightEnd - location[0], 0, 0, true);
            viewRight1.startAnimation(anim1);
            anim1.setAnimationListener(new Animation.AnimationListener() {
            ...
                @Override
                public void onAnimationEnd(Animation animation) {
                    //第二张播放完之后播放第三张
                    viewRight2.startAnimation(anim2);
                }
            ...
            });

            anim2.setAnimationListener(new Animation.AnimationListener() {
            ...
                @Override
                public void onAnimationEnd(Animation animation) {
                    //第三张播放结束后,执行更新ViewPager操作
                    //延迟更新ViewPager
                    mHandler.sendEmptyMessageDelayed(HANDLER_MESSAGE_UPDATE_CARD, CARD_DELAYED_UPDATE_TIME);
                }
            ...
            });
            return;
        } else if (mNewPosition == (cardSize - 2)) {
        //右侧移动一张
            ...
            return;
        } else if (mNewPosition == (cardSize - 1) && mNewPosition > 1) {
        //右侧没有,左侧移动两张
            ...
            return;
        } else if (mNewPosition == (cardSize - 1) && mNewPosition == 1) {
        //右侧没有,左侧移动一张
            ...
            return;
        }
    }

卡片推入动画主要是位移动画,核心是计算位移的始终。由于每个卡片都是在X轴方向移动,所以Y的相对位移不变都是0。X轴方向的偏移量均通过getLocationOnScreen()获得在屏幕上的绝对位置,此处一定要用屏幕绝对位置,而不能使用getX()这种相对位置。对两张卡片X轴做差,即可获得卡片的相对位置偏移。

卡片移动以右侧移动为主,分为四种情况:右侧两张,右侧一张,左侧两张,左侧一张。当有两张时,第一张移动结束移动第二张,第二张移动结束执行ViewPager的更新操作。当只有一张时,移动结束后执行ViewPager的更新操作。有个很重要的事情需要交代:卡片从左右移入时,卡片标记位是不同的。比如从右侧移入,卡片标记位不变,从左侧移入时,卡片标记位减1。

    /**
     * 更新页面
     */
    private void updateCard() {
        //删除已经“消失”的卡片数据
        mCardData.remove(mCurrentPage);
        //更新卡片位置。卡片从左右移入时,卡片标记位是不同的。
        mCurrentPage = mNewPosition;
        //重置Viewpager
        setViewPager();
    }

    /**
     * 设置卡片View
     */
    private void setViewPager(){
        mCardList.clear();
        for(int counter =0, size=mCardData.size(); counter < size; counter++){
            CardView view = new CardView(this);
            view.setCardInfo(mCardData.get(counter));
            view.setOnCheckListener(this);
            mCardList.add(view);
        }
        mPagerAdapter.setViews(mCardList);
        mPagerAdapter.notifyDataSetChanged();
        mPager.setCurrentItem(mCurrentPage);
    }

到此代码流程已经展示完毕,详解还需要读源码。

代码易懂,心情难解。下面主要说明几个在实现代码时遇到的问题点:

1.View不动,只有动画在动。在动画执行结束后,需要更新当前的Viewpager。

2.延迟更新Viewpager。您可能注意到在动画执行完之后,没有直接执行updateCard()的操作,而是通过mHandler.sendEmptyMessageDelayed(HANDLER_MESSAGE_UPDATE_CARD, CARD_DELAYED_UPDATE_TIME),加入延迟操作。在实际应用时,OPPO R817T手机出现崩溃的现象,分析原因可能是手机性能较差,动画未执行完就调用onAnimationEnd()导致提前更新UI。仅仅是个人猜测,加入延迟后没有出现该问题。

3.要获得View的绝对位置,而不是相对位置。但执行动画时,位移是按照相对位置来做的。

最后的友好提醒:

1.当页面在执行动画时,如果此时手动的滑动页面,则会出现页面错乱的情况,删除的不一定是哪一个。原因在于动画执行时需要获得卡片在屏幕上的绝对位置,而此时滑动页面就会出现位置计算错误的情况。相信聪明的你已经有了解决方案:执行动画时禁止用户操作。

2.别忘了做屏幕适配。如果卡片显示的内容较为复杂,记得适配不同屏幕尺寸。

做动画不容易,且做且珍惜。有问题欢迎一起讨论。

Over~

不要找了,源码在头上。

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值