仿映客点赞效果、点赞上升动画、还有全屏点赞效果、先上效果图
copy http://blog.youkuaiyun.com/qq284565035/article/details/46635697
实现分为两步
1、屏幕显示星星、动画分别是透明和缩放
2、实现贝塞尔曲线
显示星星
自定义控件、继承 RelativeLayout 并初始化
public RisingBubbleWidget(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
// 五星气泡
mStarDrawable = getContext().getResources().getDrawable(R.drawable.icon_five_stars);
// 获取图片的宽高
mStarWidth = mStarDrawable.getIntrinsicWidth();
mStarHeight = mStarDrawable.getIntrinsicHeight();
mParams = new LayoutParams(mStarWidth, mStarHeight);
//设置心形的位置,这里选择屏幕下方的中间位置(基于RelativeLayout)
mParams.addRule(ALIGN_PARENT_BOTTOM, TRUE);
mParams.addRule(ALIGN_PARENT_RIGHT, TRUE);
mParams.setMargins(0, 0, DensityUtility.dip2px(getContext(), STAR_RIGHT_MARGIN), 0);
this.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
playRisingBubbleAnim();
}
});
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidth = getMeasuredWidth();
mHeight = getMeasuredHeight();
}
设置动画效果
// 放大淡入动画
PropertyValuesHolder pScaleX = PropertyValuesHolder.ofFloat(View.SCALE_X, 0.5f, 1f);
PropertyValuesHolder pScaleY = PropertyValuesHolder.ofFloat(View.SCALE_Y, 0.5f, 1f);
PropertyValuesHolder pAlpha = PropertyValuesHolder.ofFloat(View.ALPHA, 0.5f, 1f);
PropertyValuesHolder pRotation = PropertyValuesHolder.ofFloat(View.ROTATION, 0, getRandomDegree());
mEnterAnim = ObjectAnimator.ofPropertyValuesHolder(starImageView, pScaleX, pScaleY, pAlpha, pRotation);
mEnterAnim.setDuration(100);
mEnterAnim.setInterpolator(new AccelerateInterpolator());
实现贝塞尔曲线动画
首先简单介绍一下贝塞尔曲线
P代表一个二维坐标点,很容易可以对应到PointF,而t代表是目标运行的时间,取值[0,1]。
P0是起始点,P3是终点,而P1和P2是趋向点。大概可以理解为一个正弦曲线的最高点和最低点。
1.使用插值器定义每一秒的目标的位置
初始化BezierEvaluator时将两个中间点传入
public class BezierEvaluator implements TypeEvaluator<PointF> {
@Override
public PointF evaluate(float time, PointF startValue, PointF endValue) {
PointF point1 = new PointF((startValue.x + endValue.x) / 2, (startValue.y + endValue.y) / 2);
if (time < 0 || time > 1) {
throw new IllegalArgumentException("time must between 0 and 1");
}
float timeLeft = 1.0f - time;
PointF pointF = new PointF();
pointF.x = timeLeft * timeLeft * startValue.x
+ 2 * time * timeLeft * point1.x
+ time * time * endValue.x;
pointF.y = timeLeft * timeLeft * startValue.y
+ 2 * time * timeLeft * point1.y
+ time * time * endValue.y;
return pointF;
}
}
2.将插值器插入到动画中
初始化ValueAnimator时将起点和终点的值传入
//贝塞尔曲线动画
BezierEvaluator bezierEvaluator = new BezierEvaluator();
mBezierAnim = ValueAnimator.ofObject(bezierEvaluator,
new PointF(mWidth - mStarWidth - DensityUtility.dip2px(getContext(), STAR_RIGHT_MARGIN), mHeight - mStarHeight),
new PointF(2 * mWidth / 3 + mRandom.nextInt(mWidth / 3), (mHeight - mStarHeight) / 2));
mBezierAnim.setDuration(2500);
mBezierAnim.setInterpolator(new AccelerateDecelerateInterpolator());
3.增加动画更新监听器,否则并无卵用
mBezierAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
//这里获取到贝塞尔曲线计算出来的x,y赋值给view,这样就能让五星随着曲线走
PointF pointF = (PointF) valueAnimator.getAnimatedValue();
starImageView.setX(pointF.x);
starImageView.setY(pointF.y);
//加上 alpha 动画
starImageView.setAlpha(1 - valueAnimator.getAnimatedFraction());
}
});
mBezierAnim.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
}
@Override
public void onAnimationEnd(Animator animator) {
//因为不停的add,导致子view只增不减,所以在view动画结束后remove
removeView(starImageView);
}
@Override
public void onAnimationCancel(Animator animator) {
}
@Override
public void onAnimationRepeat(Animator animator) {
}
});
4.将两个动画合并在一起、
mStarAnimSet = new AnimatorSet();
mStarAnimSet.playSequentially(mEnterAnim, mBezierAnim);
mStarAnimSet.start();
下面是完整代码
package com.rvitemtouch.sun.risingapplication.widget;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.PointF;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AccelerateInterpolator;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import com.rvitemtouch.sun.risingapplication.R;
import java.util.Random;
/**
* Created by sun on 2016/6/16.
* 上升气泡动画效果
*/
public class RisingBubbleWidget extends RelativeLayout implements BaseLayoutInterface {
private static final String TAG = "RisingBubbleWidget";
private int mStarWidth;
private int mStarHeight;
// widget 的width
private int mWidth;
// widget 的height
private int mHeight;
private Drawable mStarDrawable;
private Random mRandom = new Random();
private static final int STAR_RIGHT_MARGIN = 57;
private ObjectAnimator mEnterAnim;
private ValueAnimator mBezierAnim;
private AnimatorSet mStarAnimSet;
private LayoutParams mParams;
public RisingBubbleWidget(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public RisingBubbleWidget(Context context) {
super(context);
init();
}
public RisingBubbleWidget(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
// 五星气泡
mStarDrawable = getContext().getResources().getDrawable(R.drawable.icon_five_stars);
// 获取图片的宽高
mStarWidth = mStarDrawable.getIntrinsicWidth();
mStarHeight = mStarDrawable.getIntrinsicHeight();
mParams = new LayoutParams(mStarWidth, mStarHeight);
mParams.addRule(ALIGN_PARENT_BOTTOM, TRUE);
mParams.addRule(ALIGN_PARENT_RIGHT, TRUE);
mParams.setMargins(0, 0, DensityUtility.dip2px(getContext(), STAR_RIGHT_MARGIN), 0);
this.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
playRisingBubbleAnim();
}
});
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidth = getMeasuredWidth();
mHeight = getMeasuredHeight();
}
/**
* 开始上升五星气泡动画
*/
public void playRisingBubbleAnim() {
ImageView starImageView = new ImageView(getContext());
starImageView.setImageDrawable(mStarDrawable);
starImageView.setLayoutParams(mParams);
// 添加五星
addView(starImageView);
startRisingAnim(starImageView);
}
/**
* 播放上升动画
*/
private void startRisingAnim(final ImageView starImageView) {
// 放大淡入动画
PropertyValuesHolder pScaleX = PropertyValuesHolder.ofFloat(View.SCALE_X, 0.5f, 1f);
PropertyValuesHolder pScaleY = PropertyValuesHolder.ofFloat(View.SCALE_Y, 0.5f, 1f);
PropertyValuesHolder pAlpha = PropertyValuesHolder.ofFloat(View.ALPHA, 0.5f, 1f);
PropertyValuesHolder pRotation = PropertyValuesHolder.ofFloat(View.ROTATION, 0, getRandomDegree());
mEnterAnim = ObjectAnimator.ofPropertyValuesHolder(starImageView, pScaleX, pScaleY, pAlpha, pRotation);
mEnterAnim.setDuration(100);
mEnterAnim.setInterpolator(new AccelerateInterpolator());
//贝塞尔曲线动画
BezierEvaluator bezierEvaluator = new BezierEvaluator();
mBezierAnim = ValueAnimator.ofObject(bezierEvaluator,
new PointF(mWidth - mStarWidth - DensityUtility.dip2px(getContext(), STAR_RIGHT_MARGIN), mHeight - mStarHeight),
new PointF(2 * mWidth / 3 + mRandom.nextInt(mWidth / 3), (mHeight - mStarHeight) / 2));
mBezierAnim.setDuration(2500);
mBezierAnim.setInterpolator(new AccelerateDecelerateInterpolator());
mBezierAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
//这里获取到贝塞尔曲线计算出来的x,y赋值给view,这样就能让五星随着曲线走
PointF pointF = (PointF) valueAnimator.getAnimatedValue();
starImageView.setX(pointF.x);
starImageView.setY(pointF.y);
//加上 alpha 动画
starImageView.setAlpha(1 - valueAnimator.getAnimatedFraction());
}
});
mBezierAnim.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
}
@Override
public void onAnimationEnd(Animator animator) {
//因为不停的add,导致子view只增不减,所以在view动画结束后remove
removeView(starImageView);
}
@Override
public void onAnimationCancel(Animator animator) {
}
@Override
public void onAnimationRepeat(Animator animator) {
}
});
mStarAnimSet = new AnimatorSet();
mStarAnimSet.playSequentially(mEnterAnim, mBezierAnim);
mStarAnimSet.start();
}
/**
* 获取随机角度
*/
private float getRandomDegree() {
float degree;
int randomInt = mRandom.nextInt(5);
switch (randomInt) {
case 0:
degree = -15f;
break;
case 1:
degree = -30f;
break;
case 2:
degree = 0f;
break;
case 3:
degree = 15f;
break;
case 4:
degree = 30f;
break;
default:
degree = 30f;
break;
}
return degree;
}
@Override
public void onAdded() {
}
@Override
public void onResume() {
}
@Override
public void onPause() {
}
/**
* 防止内存溢出、
*/
@Override
public void onDestroy() {
if (mEnterAnim != null) {
mEnterAnim.removeAllListeners();
mEnterAnim.cancel();
}
if (mBezierAnim != null) {
mBezierAnim.removeAllListeners();
mBezierAnim.cancel();
}
if (mStarAnimSet != null) {
mStarAnimSet.removeAllListeners();
mStarAnimSet.cancel();
}
}
@Override
public void onStop() {
}
@Override
public void onStart() {
}
}
全屏点赞
原理很简单、找出点击的位置、然后将view add进去、等动画执行完后再removeView
下面是完整代码
package com.rvitemtouch.sun.risingapplication;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.view.MotionEventCompat;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.RotateAnimation;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import java.util.Random;
public class ZanActivity extends Activity {
private RelativeLayout zan_container;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_zan);
zan_container = (RelativeLayout) findViewById(R.id.zan_container);
zan_container.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
float x = event.getX();
float y = event.getY();
final int action = MotionEventCompat.getActionMasked(event);
switch (action) {
case MotionEvent.ACTION_DOWN://单点触摸动作
break;
case MotionEvent.ACTION_UP://单点触摸离开动作
ImageView imageView = new ImageView(ZanActivity.this);
RelativeLayout.LayoutParams param = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
final int width = imageView.getWidth();
final int height = imageView.getHeight();
param.setMargins((int) x - width / 2, (int) y - height / 2, 0, 0);
imageView.setLayoutParams(param);
imageView.setImageResource(R.drawable.zan_light_done);
zan_container.addView(imageView);
startAnim(x, y, imageView);
break;
case MotionEvent.ACTION_MOVE://触摸点移动动作
break;
case MotionEvent.ACTION_CANCEL://触摸动作取消
break;
case MotionEvent.ACTION_POINTER_DOWN://多点触摸动作
break;
case MotionEvent.ACTION_POINTER_UP://多点离开动作
break;
}
return true;
}
});
}
private void startAnim(final float x, final float y, final ImageView imageView) {
RotateAnimation rotateAnimation = createRandom();
AnimationSet animSet = new AnimationSet(true);
AlphaAnimation alphaAnimation = new AlphaAnimation(1, 0);
rotateAnimation.setDuration(600);//设置动画持续时间
alphaAnimation.setDuration(1000);//设置动画持续时间
alphaAnimation.setFillAfter(true);//动画执行完后是否停留在执行完的状态
/** 常用方法 */
// animation.setRepeatCount(int repeatCount);//设置重复次数
//animation.setStartOffset(long startOffset);//执行前的等待时间
animSet.addAnimation(rotateAnimation);
animSet.addAnimation(alphaAnimation);
rotateAnimation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
Log.i("SHF", "x-->" + x + "y--->" + y);
}
@Override
public void onAnimationEnd(Animation animation) {
if (zan_container.getChildCount() > 0) {
//Attempt to read from field 'int android.view.View.mViewFlags' on a null
//加动画图片这边也在移除动画解决办法
new Handler().post(new Runnable() {
public void run() {
imageView.clearAnimation();
zan_container.removeView(imageView);
}
});
}
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
imageView.startAnimation(animSet);
}
/**
* 随机角度
*
* @return
*/
private RotateAnimation createRandom() {
int i = new Random().nextInt(5);
switch (i) {
case 0:
return new RotateAnimation(0f, 15f, Animation.RELATIVE_TO_SELF,
0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
case 1:
return new RotateAnimation(0f, 30f, Animation.RELATIVE_TO_SELF,
0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
case 2:
return new RotateAnimation(0f, 0f, Animation.RELATIVE_TO_SELF,
0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
case 3:
return new RotateAnimation(0f, -15f, Animation.RELATIVE_TO_SELF,
0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
case 4:
return new RotateAnimation(0f, -30f, Animation.RELATIVE_TO_SELF,
0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
}
return new RotateAnimation(0f, 30f, Animation.RELATIVE_TO_SELF,
0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
}
}