InstaMaterial 概念设计(第五部分) - like操作相关效果
作者: Miroslaw Stanek
英文原文 InstaMaterial concept (part 5) - Like action effects
译文原文 http://jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0412/2709.html
转载译文务必注明出处
这篇文章是实现Material 风格的INSTAGRAM 系列文章第第五部分。今天我们将实现概念视频20到27秒里面 新闻item中like操作的效果。
下面是今天所要达到的效果(分别是Lollipop以及Lollipop之前的效果):
视频暂略
Like the feed item
根据概念视频,我们有两种“like”一条新闻的方式-通过点击心形按钮或者点击图片(实际上应该是双击才对,真实的Instagram app也是双击,不过这点就留给你们自己下去做练习吧,嘻嘻)。
如以往一样,我需要在项目中添加一些资源文件,这次是一串心形图片:
从左到右的心形依次是like计数按钮,like按钮,图片like按钮。
like计数按钮
我们从最简单的效果开始做。like计数按钮的数字更新需要伴随这进/出动画(旧数字向上滚出,同时新数字由下到上移动),下面是实际效果图:
看起来眼熟?那就对了,这和我们在 第三篇文章 中实现的评论发送按钮是一样的效果。
但是这次我们使用TextSwitcher - 一个帮助我们动画切换两个TextView文字的ViewGroup。这两个TextView均有用(我们需要通过调用TestSwitcher.setText()
方法来实现在两个TextView之间动画切换并更新值)。
首先,我们修改item_feed.xml
布局,在底部添加一个like计数按钮:
<!--...-->
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center_vertical|right">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_heart_small_blue" />
<TextSwitcher
android:id="@+id/tsLikesCounter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:inAnimation="@anim/slide_in_likes_counter"
android:outAnimation="@anim/slide_out_likes_counter">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="123 likes"
android:textColor="@color/text_like_counter" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/text_like_counter" />
</TextSwitcher>
</LinearLayout>
<!--...-->
下面是添加的view在Android Studio中的预览效果:
代码中TextSwitcher将让两个子TextView动画切换,我们不需要担心隐藏的问题-内部已经实现了。android:inAnimation
和 android:outAnimation用于指定前一个子view和后一个子view的过度动画。
用0dp来达到更好的性能
包含了like计数按钮的LinearLayout
被赋予了layout_weight="1"
的属性,其父布局依然是LinearLayout
,当LinearLayout
只有一个子view使用了weight值,那么这个子view将吸收剩余的所有空间。而使用layout_width="0dp"
可以让性能更高,因为这个view不需要被测量宽度了。
现在,我们添加一些代码到FeedAdapter
。唯一值得一提的代码是updateLikesCounter()
方法,这里是 包含所有改变的一次提交。
private void updateLikesCounter(CellFeedViewHolder holder, boolean animated) {
int currentLikesCount = likesCount.get(holder.getPosition()) + 1;
String likesCountText = context.getResources().getQuantityString(
R.plurals.likes_count, currentLikesCount, currentLikesCount
);
if (animated) {
holder.tsLikesCounter.setText(likesCountText);
} else {
holder.tsLikesCounter.setCurrentText(likesCountText);
}
likesCount.put(holder.getPosition(), currentLikesCount);
}
这个方法用两种方式更新like计数按钮:
带动画的:(用于动态更新,当用户点击like按钮的时候使用) - 使用 TextSwitcher.setText()
方法。
不带动画的:(onBindViewHolder()
中被调用,这种情况不需要动画) - 使用方法
- 这个方法更新当前TextView的值。
其实你可能还是不了解这两个方法的区别,看看官网的解释就一目了然了。
TextSwitcher.setCurrentText()
: 设置当前正在显示的TextView的值。
TextSwitcher.setText()
: 设置下一个TextView的值,并切换到下一个TextView。
你可能注意到了,我们使用了_Quantity_字符串来处理单复数的问题,这里你可以找到更多关于android中使用Plurals 的知识。
like按钮动画
现在我们将专注于like按钮的动画,它的行为比概念视频上的看起来还要炫一些,截图如下:
同时播放多个动画
这个动画效果由捆绑在一起的多个动画组成。因此我们不能像以前那样使用 ViewPropertyAnimator。幸好有播放多个动画的简便方法。 AnimatorSet 这个类可以将多个动画放在一起,然后可以选择同时播放,顺序播放,或者是混合播放。这里有更多的描述 Choreographing Multiple Animations 。
我们的like按钮效果由3个动画组成:
360度的旋转动画 - 这个是最开始播放的动画
Bounce动画 - 由两个动画组成( scaleX 和 scaleY 动画),当旋转动画结束后触发这个动画。
下面是设置like按钮的所有相关代码(和like计数按钮一样,分为动画与静态两种方式):
private void updateHeartButton(final CellFeedViewHolder holder, boolean animated) {
if (animated) {
if (!likeAnimations.containsKey(holder)) {
AnimatorSet animatorSet = new AnimatorSet();
likeAnimations.put(holder, animatorSet);
ObjectAnimator rotationAnim = ObjectAnimator.ofFloat(holder.btnLike, "rotation", 0f, 360f);
rotationAnim.setDuration(300);
rotationAnim.setInterpolator(ACCELERATE_INTERPOLATOR);
ObjectAnimator bounceAnimX = ObjectAnimator.ofFloat(holder.btnLike, "scaleX", 0.2f, 1f);
bounceAnimX.setDuration(300);
bounceAnimX.setInterpolator(OVERSHOOT_INTERPOLATOR);
ObjectAnimator bounceAnimY = ObjectAnimator.ofFloat(holder.btnLike, "scaleY", 0.2f, 1f);
bounceAnimY.setDuration(300);
bounceAnimY.setInterpolator(OVERSHOOT_INTERPOLATOR);
bounceAnimY.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
holder.btnLike.setImageResource(R.drawable.ic_heart_red);
}
});
animatorSet.play(rotationAnim);
animatorSet.play(bounceAnimX).with(bounceAnimY).after(rotationAnim);
animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
resetLikeAnimationState(holder);
}
});
animatorSet.start();
}
} else {
if (likedPositions.contains(holder.getPosition())) {
holder.btnLike.setImageResource(R.drawable.ic_heart_red);
} else {
holder.btnLike.setImageResource(R.drawable.ic_heart_outline_grey);
}
}
}
25和26行组成了我们的最终效果。
这次我们使用ObjectAnimator
,它可以“动画”目标对象的属性。可以使用int,float,argb值去播放动画,任何带有setter方法的属性都可以用它。本例中我们通过ObjectAnimator.ofFloat()
方法来来实现rotation
值的动画,在内部,其实是不断调用setRotation(Float f)
方法。
这里是添加 like按钮动画 提交的代码。
图片like 动画
图片like动画也稍微有点复杂。这次我们动画播放两个不同的对象(但仍然在一个AnimatorSet
中):
.心形底部圆形背景(缩放以及渐变动画)
.心形图标(放大缩小动画)
The effect looks like below:
下面是实现上面动画的代码:
private void animatePhotoLike(final CellFeedViewHolder holder) {
if (!likeAnimations.containsKey(holder)) {
holder.vBgLike.setVisibility(View.VISIBLE);
holder.ivLike.setVisibility(View.VISIBLE);
holder.vBgLike.setScaleY(0.1f);
holder.vBgLike.setScaleX(0.1f);
holder.vBgLike.setAlpha(1f);
holder.ivLike.setScaleY(0.1f);
holder.ivLike.setScaleX(0.1f);
AnimatorSet animatorSet = new AnimatorSet();
likeAnimations.put(holder, animatorSet);
ObjectAnimator bgScaleYAnim = ObjectAnimator.ofFloat(holder.vBgLike, "scaleY", 0.1f, 1f);
bgScaleYAnim.setDuration(200);
bgScaleYAnim.setInterpolator(DECCELERATE_INTERPOLATOR);
ObjectAnimator bgScaleXAnim = ObjectAnimator.ofFloat(holder.vBgLike, "scaleX", 0.1f, 1f);
bgScaleXAnim.setDuration(200);
bgScaleXAnim.setInterpolator(DECCELERATE_INTERPOLATOR);
ObjectAnimator bgAlphaAnim = ObjectAnimator.ofFloat(holder.vBgLike, "alpha", 1f, 0f);
bgAlphaAnim.setDuration(200);
bgAlphaAnim.setStartDelay(150);
bgAlphaAnim.setInterpolator(DECCELERATE_INTERPOLATOR);
ObjectAnimator imgScaleUpYAnim = ObjectAnimator.ofFloat(holder.ivLike, "scaleY", 0.1f, 1f);
imgScaleUpYAnim.setDuration(300);
imgScaleUpYAnim.setInterpolator(DECCELERATE_INTERPOLATOR);
ObjectAnimator imgScaleUpXAnim = ObjectAnimator.ofFloat(holder.ivLike, "scaleX", 0.1f, 1f);
imgScaleUpXAnim.setDuration(300);
imgScaleUpXAnim.setInterpolator(DECCELERATE_INTERPOLATOR);
ObjectAnimator imgScaleDownYAnim = ObjectAnimator.ofFloat(holder.ivLike, "scaleY", 1f, 0f);
imgScaleDownYAnim.setDuration(300);
imgScaleDownYAnim.setInterpolator(ACCELERATE_INTERPOLATOR);
ObjectAnimator imgScaleDownXAnim = ObjectAnimator.ofFloat(holder.ivLike, "scaleX", 1f, 0f);
imgScaleDownXAnim.setDuration(300);
imgScaleDownXAnim.setInterpolator(ACCELERATE_INTERPOLATOR);
animatorSet.playTogether(bgScaleYAnim, bgScaleXAnim, bgAlphaAnim, imgScaleUpYAnim, imgScaleUpXAnim);
animatorSet.play(imgScaleDownYAnim).with(imgScaleDownXAnim).after(imgScaleUpYAnim);
animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
resetLikeAnimationState(holder);
}
});
animatorSet.start();
}
}
再一次用到了ObjectAnimator
和AnimatorSet
。(40-41行)。
这就是今天的全部内容,谢谢阅读。