Twitter的like动画安卓版 - 备选方案
2016-01-28 16:47
459 查看
英文: Twitter’s like animation in Android - alternative
相关动画网址:http://frogermcs.github.io/twitters-like-animation-in-android-alternative/,
更多动画网址:http://frogermcs.github.io/
作者来源:Miroslaw Stanek
小编自己遇到的问题:最近在研究一个Twitter的like动画安卓版,在任何手机上面都可以正常播放动画,只有在一个htc的手机上面,没有任何错误日志,动画播放不出来
操作:设置–》开发者选择–》高级选项–》动画,把动画的选项打开就好了。
不久前Twitter展示了具有现代感的心形动画-作为star图标的替代。
虽然实现这个动画最简单的方法是使用 Frame Animation ,但是我们尝试用更灵活的方法来实现-手动绘制并用属性动画。这篇文章只是概要,没有深入的技术细节
这个视图负责绘制星星图标下面的大圆。它本可以实现得更简单(通过xml ),但是这里我们应该考虑按钮下面的背景颜色。
我们在canvas上绘制圆的实现:
先使用CLEAR 模式绘制颜色以清除canvas。然后根据给定的进度(各自的进度是独立的)绘制内外圆。
内圆使用这样定义的mask paint :
我们需要完全透明,不然的话内圆就会显示窗口颜色。
对于那些眼睛机灵的人应该还注意到了另外一件事-我们的外圆颜色是基于当前进度而变化的。这是通过 ArgbEvaluator 类来完成,该类可以基于一个给定的因子在两个颜色之间变换:
这个view将绘制浮动在星星图标周围的圆点。跟CircleView一样,它是使用onDraw()来做这件事的:
圆点是基于currentProgress绘制的,背后是数学逻辑,老实说从安卓sdk的角度来看这里没有什么有趣的地方,倒是有两个跟数学相关的东西:
圆点分布在一个圆上-它们的位置决定于:
int cX = (int) (centerX + currentRadius1 * Math.cos(i * OUTER_DOTS_POSITION_ANGLE * Math.PI / 180));
int cY = (int) (centerY + currentRadius1 * Math.sin(i * OUTER_DOTS_POSITION_ANGLE * Math.PI / 180));
意味着:在每个 OUTER_DOTS_POSITION_ANGLE 上设置圆点 (51 度).
每个圆点都有它自己的颜色动画:
这意味着圆点颜色在3个区间形式的值之间动画。我们再一次使用ArgbEvaluator 让它平滑。其余就很简单了
CircleView完整代码
DotsView完整代码
我们使用 Merge 标签帮助消除多余的ViewGroup。LikeButtonView本身就是一个FrameLayout,因此没有必要出现两次。
我们最终的动画是由更小的动画组成的,通过AnimatorSet一起播放:
LikeButtonView完整代码
源代码可以在github上面得到:https://github.com/frogermcs/LikeAnimation/
相关动画网址:http://frogermcs.github.io/twitters-like-animation-in-android-alternative/,
更多动画网址:http://frogermcs.github.io/
作者来源:Miroslaw Stanek
小编自己遇到的问题:最近在研究一个Twitter的like动画安卓版,在任何手机上面都可以正常播放动画,只有在一个htc的手机上面,没有任何错误日志,动画播放不出来
操作:设置–》开发者选择–》高级选项–》动画,把动画的选项打开就好了。
不久前Twitter展示了具有现代感的心形动画-作为star图标的替代。
虽然实现这个动画最简单的方法是使用 Frame Animation ,但是我们尝试用更灵活的方法来实现-手动绘制并用属性动画。这篇文章只是概要,没有深入的技术细节
一:实现
我们将创建一个名叫LikeButtonView的view,它是一个由三个子view构成的FrameLayout- CircleView 显示星星图标下面的圆,ImageView (星星)以及代表按钮周围浮点的DotsView 。CircleView
这个视图负责绘制星星图标下面的大圆。它本可以实现得更简单(通过xml ),但是这里我们应该考虑按钮下面的背景颜色。
我们在canvas上绘制圆的实现:
Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); tempCanvas.drawColor(0xffffff, PorterDuff.Mode.CLEAR); tempCanvas.drawCircle(getWidth() / 2, getHeight() / 2, outerCircleRadiusProgress * maxCircleSize, circlePaint); tempCanvas.drawCircle(getWidth() / 2, getHeight() / 2, innerCircleRadiusProgress * maxCircleSize, maskPaint); canvas.drawBitmap(tempBitmap, 0, 0, null); }
先使用CLEAR 模式绘制颜色以清除canvas。然后根据给定的进度(各自的进度是独立的)绘制内外圆。
内圆使用这样定义的mask paint :
Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); maxCircleSize = w / 2; tempBitmap = Bitmap.createBitmap(getWidth(), getWidth(), Bitmap.Config.ARGB_8888); tempCanvas = new Canvas(tempBitmap); }
我们需要完全透明,不然的话内圆就会显示窗口颜色。
对于那些眼睛机灵的人应该还注意到了另外一件事-我们的外圆颜色是基于当前进度而变化的。这是通过 ArgbEvaluator 类来完成,该类可以基于一个给定的因子在两个颜色之间变换:
private void updateCircleColor() { float colorProgress = (float) Utils.clamp(outerCircleRadiusProgress, 0.5, 1); colorProgress = (float) Utils.mapValueFromRangeToRange(colorProgress, 0.5f, 1f, 0f, 1f); this.circlePaint.setColor((Integer) argbEvaluator.evaluate(colorProgress, START_COLOR, END_COLOR)); }
三:DotsView
这个view将绘制浮动在星星图标周围的圆点。跟CircleView一样,它是使用onDraw()来做这件事的:
protected void onDraw(Canvas canvas) { drawOuterDotsFrame(canvas); drawInnerDotsFrame(canvas); } private void drawOuterDotsFrame(Canvas canvas) { for (int i = 0; i < DOTS_COUNT; i++) { int cX = (int) (centerX + currentRadius1 * Math.cos(i * OUTER_DOTS_POSITION_ANGLE * Math.PI / 180)); int cY = (int) (centerY + currentRadius1 * Math.sin(i * OUTER_DOTS_POSITION_ANGLE * Math.PI / 180)); canvas.drawCircle(cX, cY, currentDotSize1, circlePaints[i % circlePaints.length]); } } private void drawInnerDotsFrame(Canvas canvas) { for (int i = 0; i < DOTS_COUNT; i++) { int cX = (int) (centerX + currentRadius2 * Math.cos((i * OUTER_DOTS_POSITION_ANGLE - 10) * Math.PI / 180)); int cY = (int) (centerY + currentRadius2 * Math.sin((i * OUTER_DOTS_POSITION_ANGLE - 10) * Math.PI / 180)); canvas.drawCircle(cX, cY, currentDotSize2, circlePaints[(i + 1) % circlePaints.length]); } }
圆点是基于currentProgress绘制的,背后是数学逻辑,老实说从安卓sdk的角度来看这里没有什么有趣的地方,倒是有两个跟数学相关的东西:
圆点分布在一个圆上-它们的位置决定于:
int cX = (int) (centerX + currentRadius1 * Math.cos(i * OUTER_DOTS_POSITION_ANGLE * Math.PI / 180));
int cY = (int) (centerY + currentRadius1 * Math.sin(i * OUTER_DOTS_POSITION_ANGLE * Math.PI / 180));
意味着:在每个 OUTER_DOTS_POSITION_ANGLE 上设置圆点 (51 度).
每个圆点都有它自己的颜色动画:
private void updateDotsPaints() { if (currentProgress < 0.5f) { float progress = (float) Utils.mapValueFromRangeToRange(currentProgress, 0f, 0.5f, 0, 1f); circlePaints[0].setColor((Integer) argbEvaluator.evaluate(progress, COLOR_1, COLOR_2)); circlePaints[1].setColor((Integer) argbEvaluator.evaluate(progress, COLOR_2, COLOR_3)); circlePaints[2].setColor((Integer) argbEvaluator.evaluate(progress, COLOR_3, COLOR_4)); circlePaints[3].setColor((Integer) argbEvaluator.evaluate(progress, COLOR_4, COLOR_1)); } else { float progress = (float) Utils.mapValueFromRangeToRange(currentProgress, 0.5f, 1f, 0, 1f); circlePaints[0].setColor((Integer) argbEvaluator.evaluate(progress, COLOR_2, COLOR_3)); circlePaints[1].setColor((Integer) argbEvaluator.evaluate(progress, COLOR_3, COLOR_4)); circlePaints[2].setColor((Integer) argbEvaluator.evaluate(progress, COLOR_4, COLOR_1)); circlePaints[3].setColor((Integer) argbEvaluator.evaluate(progress, COLOR_1, COLOR_2)); } }
这意味着圆点颜色在3个区间形式的值之间动画。我们再一次使用ArgbEvaluator 让它平滑。其余就很简单了
CircleView完整代码
package com.loveta.umengexample;
import android.animation.ArgbEvaluator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Property;
import android.view.View;
/**
* Created by Miroslaw Stanek on 21.12.2015.
*/
public class CircleView extends View {
private static final int START_COLOR = 0xFFFF5722;
private static final int END_COLOR = 0xFFFFC107;
private ArgbEvaluator argbEvaluator = new ArgbEvaluator();
private Paint circlePaint = new Paint();
private Paint maskPaint = new Paint();
private Bitmap tempBitmap;
private Canvas tempCanvas;
private float outerCircleRadiusProgress = 0f;
private float innerCircleRadiusProgress = 0f;
private int maxCircleSize;
public CircleView(Context context) {
super(context);
init();
}
public CircleView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public CircleView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public CircleView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
private void init() {
// Log.e("==tjj", "============");
circlePaint.setStyle(Paint.Style.FILL);//实心园
maskPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
//PorterDuff.Mode.CLEAR---所绘制不会提交到画布上。http://blog.csdn.net/edisonlg/article/details/7084977
}
@Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); maxCircleSize = w / 2; tempBitmap = Bitmap.createBitmap(getWidth(), getWidth(), Bitmap.Config.ARGB_8888); tempCanvas = new Canvas(tempBitmap); }
@Override
protected void onDraw(Canvas canvas) {//视图负责绘制星星图标下面的大圆
super.onDraw(canvas);
Log.e("==========tan111===", "tan111");
tempCanvas.drawColor(0xffffff, PorterDuff.Mode.CLEAR);//设置画布的背景颜色
tempCanvas.drawCircle(getWidth() / 2, getHeight() / 2, outerCircleRadiusProgress * maxCircleSize, circlePaint);
tempCanvas.drawCircle(getWidth() / 2, getHeight() / 2, innerCircleRadiusProgress * maxCircleSize, maskPaint);
canvas.drawBitmap(tempBitmap, 0, 0, null);
}
public void setInnerCircleRadiusProgress(float innerCircleRadiusProgress) {
this.innerCircleRadiusProgress = innerCircleRadiusProgress;
// Log.e("==========tan===", "tan");
postInvalidate();
// invalidate();
}
public float getInnerCircleRadiusProgress() {
return innerCircleRadiusProgress;
}
public void setOuterCircleRadiusProgress(float outerCircleRadiusProgress) {
this.outerCircleRadiusProgress = outerCircleRadiusProgress;
updateCircleColor();
postInvalidate();
// invalidate();
}
private void updateCircleColor() { float colorProgress = (float) Utils.clamp(outerCircleRadiusProgress, 0.5, 1); colorProgress = (float) Utils.mapValueFromRangeToRange(colorProgress, 0.5f, 1f, 0f, 1f); this.circlePaint.setColor((Integer) argbEvaluator.evaluate(colorProgress, START_COLOR, END_COLOR)); }
public float getOuterCircleRadiusProgress() {
return outerCircleRadiusProgress;
}
public static final Property<CircleView, Float> INNER_CIRCLE_RADIUS_PROGRESS =
new Property<CircleView, Float>(Float.class, "innerCircleRadiusProgress") {
@Override
public Float get(CircleView object) {
return object.getInnerCircleRadiusProgress();
}
@Override
public void set(CircleView object, Float value) {
// Log.e("==================tjj===","INNER_CIRCLE_RADIUS_PROGRESS");
object.setInnerCircleRadiusProgress(value);
}
};
public static final Property<CircleView, Float> OUTER_CIRCLE_RADIUS_PROGRESS =
new Property<CircleView, Float>(Float.class, "outerCircleRadiusProgress") {
@Override
public Float get(CircleView object) {
return object.getOuterCircleRadiusProgress();
}
@Override
public void set(CircleView object, Float value) {
object.setOuterCircleRadiusProgress(value);
}
};
}
DotsView完整代码
package com.loveta.umengexample;
import android.animation.ArgbEvaluator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Property;
import android.view.View;
/**
* Created by Miroslaw Stanek on 20.12.2015.
*/
public class DotsView extends View {
private static final int DOTS_COUNT = 7;
private static final int OUTER_DOTS_POSITION_ANGLE = 360 / DOTS_COUNT;
private static final int COLOR_1 = 0xFFFFC107;
private static final int COLOR_2 = 0xFFFF9800;
private static final int COLOR_3 = 0xFFFF5722;
private static final int COLOR_4 = 0xFFF44336;
private final Paint[] circlePaints = new Paint[4];
private int centerX;
private int centerY;
private float maxOuterDotsRadius;
private float maxInnerDotsRadius;
private float maxDotSize;
private float currentProgress = 0;
private float currentRadius1 = 0;
private float currentDotSize1 = 0;
private float currentDotSize2 = 0;
private float currentRadius2 = 0;
private ArgbEvaluator argbEvaluator = new ArgbEvaluator();
public DotsView(Context context) {
super(context);
init();
}
public DotsView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public DotsView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public DotsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
private void init() {
for (int i = 0; i < circlePaints.length; i++) {
circlePaints[i] = new Paint();
circlePaints[i].setStyle(Paint.Style.FILL);
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
centerX = w / 2;
centerY = h / 2;
maxDotSize = 20;
maxOuterDotsRadius = w / 2 - maxDotSize * 2;
maxInnerDotsRadius = 0.8f * maxOuterDotsRadius;
}
@Override
protected void onDraw(Canvas canvas) {//绘制浮动在星星图标周围的圆点
drawOuterDotsFrame(canvas);
drawInnerDotsFrame(canvas);
}
private void drawOuterDotsFrame(Canvas canvas) {
for (int i = 0; i < DOTS_COUNT; i++) {
int cX = (int) (centerX + currentRadius1 * Math.cos(i * OUTER_DOTS_POSITION_ANGLE * Math.PI / 180));
int cY = (int) (centerY + currentRadius1 * Math.sin(i * OUTER_DOTS_POSITION_ANGLE * Math.PI / 180));
canvas.drawCircle(cX, cY, currentDotSize1, circlePaints[i % circlePaints.length]);
}
}
private void drawInnerDotsFrame(Canvas canvas) {
for (int i = 0; i < DOTS_COUNT; i++) {
int cX = (int) (centerX + currentRadius2 * Math.cos((i * OUTER_DOTS_POSITION_ANGLE - 10) * Math.PI / 180));
int cY = (int) (centerY + currentRadius2 * Math.sin((i * OUTER_DOTS_POSITION_ANGLE - 10) * Math.PI / 180));
canvas.drawCircle(cX, cY, currentDotSize2, circlePaints[(i + 1) % circlePaints.length]);
}
}
public void setCurrentProgress(float currentProgress) {
this.currentProgress = currentProgress;
updateInnerDotsPosition();//1
updateOuterDotsPosition();//2
updateDotsPaints();//3
updateDotsAlpha();//4
postInvalidate();//1
}
public float getCurrentProgress() {
return currentProgress;
}
private void updateInnerDotsPosition() {
if (currentProgress < 0.3f) {
this.currentRadius2 = (float) Utils.mapValueFromRangeToRange(currentProgress, 0, 0.3f, 0.f, maxInnerDotsRadius);
} else {
this.currentRadius2 = maxInnerDotsRadius;
}
if (currentProgress < 0.2) {
this.currentDotSize2 = maxDotSize;
} else if (currentProgress < 0.5) {
this.currentDotSize2 = (float) Utils.mapValueFromRangeToRange(currentProgress, 0.2f, 0.5f, maxDotSize, 0.3 * maxDotSize);
} else {
this.currentDotSize2 = (float) Utils.mapValueFromRangeToRange(currentProgress, 0.5f, 1f, maxDotSize * 0.3f, 0);
}
}
private void updateOuterDotsPosition() {
if (currentProgress < 0.3f) {
this.currentRadius1 = (float) Utils.mapValueFromRangeToRange(currentProgress, 0.0f, 0.3f, 0, maxOuterDotsRadius * 0.8f);
} else {
this.currentRadius1 = (float) Utils.mapValueFromRangeToRange(currentProgress, 0.3f, 1f, 0.8f * maxOuterDotsRadius, maxOuterDotsRadius);
}
if (currentProgress < 0.7) {
this.currentDotSize1 = maxDotSize;
} else {
this.currentDotSize1 = (float) Utils.mapValueFromRangeToRange(currentProgress, 0.7f, 1f, maxDotSize, 0);
}
}
private void updateDotsPaints() { if (currentProgress < 0.5f) { float progress = (float) Utils.mapValueFromRangeToRange(currentProgress, 0f, 0.5f, 0, 1f); circlePaints[0].setColor((Integer) argbEvaluator.evaluate(progress, COLOR_1, COLOR_2)); circlePaints[1].setColor((Integer) argbEvaluator.evaluate(progress, COLOR_2, COLOR_3)); circlePaints[2].setColor((Integer) argbEvaluator.evaluate(progress, COLOR_3, COLOR_4)); circlePaints[3].setColor((Integer) argbEvaluator.evaluate(progress, COLOR_4, COLOR_1)); } else { float progress = (float) Utils.mapValueFromRangeToRange(currentProgress, 0.5f, 1f, 0, 1f); circlePaints[0].setColor((Integer) argbEvaluator.evaluate(progress, COLOR_2, COLOR_3)); circlePaints[1].setColor((Integer) argbEvaluator.evaluate(progress, COLOR_3, COLOR_4)); circlePaints[2].setColor((Integer) argbEvaluator.evaluate(progress, COLOR_4, COLOR_1)); circlePaints[3].setColor((Integer) argbEvaluator.evaluate(progress, COLOR_1, COLOR_2)); } }
private void updateDotsAlpha() {
float progress = (float) Utils.clamp(currentProgress, 0.6f, 1f);
int alpha = (int) Utils.mapValueFromRangeToRange(progress, 0.6f, 1f, 255, 0);
circlePaints[0].setAlpha(alpha);
circlePaints[1].setAlpha(alpha);
circlePaints[2].setAlpha(alpha);
circlePaints[3].setAlpha(alpha);
}
public static final Property<DotsView, Float> DOTS_PROGRESS = new Property<DotsView, Float>(Float.class, "dotsProgress") {
@Override
public Float get(DotsView object) {
return object.getCurrentProgress();
}
@Override
public void set(DotsView object, Float value) {
object.setCurrentProgress(value);
}
};
}
四:LikeButtonView
最终的ViewGroup是由CircleView, ImageView 以及DotsView组成的。<?xml version="1.0" encoding="utf-8"?> <merge xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <frogermcs.io.likeanimation.DotsView android:id="@+id/vDotsView" android:layout_width="200dp" android:layout_height="200dp" android:layout_gravity="center"/> <frogermcs.io.likeanimation.CircleView android:id="@+id/vCircle" android:layout_width="80dp" android:layout_height="80dp" android:layout_gravity="center"/> <ImageView android:id="@+id/ivStar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:src="@drawable/ic_star_rate_off"/> </merge>
我们使用 Merge 标签帮助消除多余的ViewGroup。LikeButtonView本身就是一个FrameLayout,因此没有必要出现两次。
我们最终的动画是由更小的动画组成的,通过AnimatorSet一起播放:
LikeButtonView完整代码
package com.loveta.umengexample; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.annotation.TargetApi; import android.content.Context; import android.os.Build; import android.util.AttributeSet; import android.util.Log; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.DecelerateInterpolator; import android.view.animation.OvershootInterpolator; import android.widget.FrameLayout; import android.widget.ImageView; import butterknife.Bind; import butterknife.ButterKnife; /** * Created by Miroslaw Stanek on 20.12.2015. */ public class LikeButtonView extends FrameLayout implements View.OnClickListener { private static final DecelerateInterpolator DECCELERATE_INTERPOLATOR = new DecelerateInterpolator(); private static final AccelerateDecelerateInterpolator ACCELERATE_DECELERATE_INTERPOLATOR = new AccelerateDecelerateInterpolator(); private static final OvershootInterpolator OVERSHOOT_INTERPOLATOR = new OvershootInterpolator(4); @Bind(R.id.ivStar) ImageView ivStar; @Bind(R.id.vDotsView) DotsView vDotsView; @Bind(R.id.vCircle) CircleView vCircle; private boolean isChecked; private AnimatorSet animatorSet; public LikeButtonView(Context context) { super(context); init(); } public LikeButtonView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public LikeButtonView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public LikeButtonView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); init(); } private void init() { LayoutInflater.from(getContext()).inflate(R.layout.view_like_button, this, true); ButterKnife.bind(this); setOnClickListener(this); } @Override public void onClick(View v) { // Log.e("==tjj======",v.getId()+""); isChecked = !isChecked; ivStar.setImageResource(isChecked ? R.drawable.smart11 : R.drawable.smart1); if (animatorSet != null) { animatorSet.cancel(); } if (isChecked) { ivStar.animate().cancel(); ivStar.setScaleX(0); ivStar.setScaleY(0); Log.e("====1111=======", "setCurrentProgress"); vCircle.setInnerCircleRadiusProgress(0); vCircle.setOuterCircleRadiusProgress(0); vDotsView.setCurrentProgress(0); animatorSet = new AnimatorSet(); ObjectAnimator outerCircleAnimator = ObjectAnimator.ofFloat(vCircle, CircleView.OUTER_CIRCLE_RADIUS_PROGRESS, 0.1f, 1f); outerCircleAnimator.setDuration(250); outerCircleAnimator.setInterpolator(DECCELERATE_INTERPOLATOR); ObjectAnimator innerCircleAnimator = ObjectAnimator.ofFloat(vCircle, CircleView.INNER_CIRCLE_RADIUS_PROGRESS, 0.1f, 1f); innerCircleAnimator.setDuration(200); innerCircleAnimator.setStartDelay(200); innerCircleAnimator.setInterpolator(DECCELERATE_INTERPOLATOR); ObjectAnimator starScaleYAnimator = ObjectAnimator.ofFloat(ivStar, ImageView.SCALE_Y, 0.2f, 1f); starScaleYAnimator.setDuration(350); starScaleYAnimator.setStartDelay(250); starScaleYAnimator.setInterpolator(OVERSHOOT_INTERPOLATOR); ObjectAnimator starScaleXAnimator = ObjectAnimator.ofFloat(ivStar, ImageView.SCALE_X, 0.2f, 1f); starScaleXAnimator.setDuration(350); starScaleXAnimator.setStartDelay(250); starScaleXAnimator.setInterpolator(OVERSHOOT_INTERPOLATOR); ObjectAnimator dotsAnimator = ObjectAnimator.ofFloat(vDotsView, DotsView.DOTS_PROGRESS, 0, 1f); dotsAnimator.setDuration(900); dotsAnimator.setStartDelay(50); dotsAnimator.setInterpolator(ACCELERATE_DECELERATE_INTERPOLATOR); animatorSet.playTogether( outerCircleAnimator, innerCircleAnimator, starScaleYAnimator, starScaleXAnimator, dotsAnimator ); animatorSet.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationCancel(Animator animation) { vCircle.setInnerCircleRadiusProgress(0); vCircle.setOuterCircleRadiusProgress(0); // Log.e("====2222=======", "setCurrentProgress"); vDotsView.setCurrentProgress(0); ivStar.setScaleX(1); ivStar.setScaleY(1); } }); animatorSet.start(); } } //LikeButtonView还会响应触摸事件(缩放动画): @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: ivStar.animate().scaleX(0.7f).scaleY(0.7f).setDuration(150).setInterpolator(DECCELERATE_INTERPOLATOR); setPressed(true); break; case MotionEvent.ACTION_MOVE: float x = event.getX(); float y = event.getY(); boolean isInside = (x > 0 && x < getWidth() && y > 0 && y < getHeight()); if (isPressed() != isInside) { setPressed(isInside); } break; case MotionEvent.ACTION_UP: ivStar.animate().scaleX(1).scaleY(1).setInterpolator(DECCELERATE_INTERPOLATOR); if (isPressed()) { performClick(); setPressed(false); } break; } return true; } }
源代码可以在github上面得到:https://github.com/frogermcs/LikeAnimation/
相关文章推荐
- POJ 2251(BFS)
- Android Camera Framework
- 登陆脚本
- 面向对象程序设计第二次作业
- 上传文件详解
- python2.7_1.2_打印设备名和IPv4地址
- java如何通过socket实现服务端与客户端交互
- JAVA NIO之浅谈内存映射文件原理与DirectMemory
- NSURLSession的应用
- 取消word的修订模式
- 几种任务调度的 Java 实现方法与比较
- Android特效 五种Toast
- Android中使用Volley开源库进行Http网络请求(GET方式)
- UITextView的基本用法
- Zoho People咨询行业案例
- Python渗透测试工具合集
- 移除IIS默认的响应头
- 在Cordova中自定义AndroidTest(Instrumented Unit Test)
- MYSQL存储过程怎么写
- redis练习手册<二>快速入门