ViewPager作为Android中使用最多的组件之一,具有良好的稳定性。接到交互的需求,想在项目的Viewpager的卡片删除中加入一些动画,提升逼格。具体的实现如下面的gif所示:
如果觉得效果可以,请您赏脸接着看。如果觉得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~
不要找了,源码在头上。