仿Periscope,映客用贝塞尔曲线实现点赞送爱心。

因为项目需要,开始研究动画。这是为了实现直播平台的点赞功能特效而做的,参阅了很多资料,大家想看 一些关于安卓的高机动画内容可以去关注一下他的[刘某人] http://http://my.csdn.net/qq_26787115的博客,相信会有一些收获的。
 首先我们来看一下效果:(因为自己还不太熟gif动态录制工具,就先拿的别人的效果图过来了)

首先我们要知道什么是贝塞尔曲线。告诉你们一个方法 贝塞尔曲线
现在我们开始正题
首先我们想要去规划爱心的轨迹,我们需要通过贝塞尔曲线公式来进行一个计算!
 
 BezierEvaluator.java
  在这里面我们可以通过修改pointF1,pointF2等参数来修改我们的爱心运行时候的轨迹
package com.lgl.heartfaom;

import android.animation.TypeEvaluator;
import android.graphics.PointF;
import android.util.Log;

public class BezierEvaluator implements TypeEvaluator<PointF> {

	private PointF pointF1;
	private PointF pointF2;

	public BezierEvaluator(PointF pointF1, PointF pointF2) {
		this.pointF1 = pointF1;
		this.pointF2 = pointF2;
	}
	
	
  //公式:B(t)=P(0)(1-t)^3+3P(1)t(1-t)*(1-t)
	@Override
	//求开始到结束时候的平滑过度的值
	//pointF1.y 运行曲线的高度
	public PointF evaluate(float time, PointF startValue, PointF endValue) {

		float timeLeft = 1.0f - time;
		PointF point = new PointF();// 结果
//		  float p0=startValue.x;
//		  float p1=pointF1.x;
//		  point.x=p0+(p1-p0)*timeLeft;
		  

		point.x = timeLeft * timeLeft * timeLeft * (startValue.x) + 3
				* timeLeft * timeLeft * time * (pointF1.x+450) + 3 * timeLeft
				* time * time * (pointF2.x) + time * time * time * (endValue.x);
		
		System.out.println("startValue.x"+startValue.x+"pointF1.x"+pointF1.x+"pointF2.x的值"+pointF2.x+"point.x的值"+point.x+"endValue.x"+endValue.x);
      
		point.y = timeLeft * timeLeft * timeLeft * (startValue.y) + 3
				* timeLeft * timeLeft * time * (pointF1.y) + 3 * timeLeft
				* time * time * (pointF2.y) + time * time * time * (endValue.y);
		System.out.println("stratvalue.y的值" +startValue.y+"pointF1.y的值"+pointF1.y+"pointF2.y的值"+pointF2.y+"endValue.y的值"+endValue.y);
		return point;
	}
}
<strong>下面这个PeriscopeLayout.java才是真正的大头</strong>
<div>package com.lgl.heartfaom;</div><div>
</div><div>import java.util.Random;</div><div>
</div><div>import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.PointF;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;</div><div>
</div><div>import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.LinearInterpolator;
import android.widget.ImageView;
import android.widget.RelativeLayout;</div><div>
</div><div>public class PeriscopeLayout extends RelativeLayout {</div><div>
</div><div>// private Interpolator line = new LinearInterpolator();// 线性
// private Interpolator acc = new AccelerateInterpolator();// 加速
// private Interpolator dce = new DecelerateInterpolator();// 减速
// private Interpolator accdec = new AccelerateDecelerateInterpolator();// 先加速后减速
// private Interpolator[] interpolators;
  /**缩小动画**/
    Animation mLitteAnimation = null;
   Context content;
 private int mHeight;//整个布局的高度
 private int mWidth;//整个布局的宽度
 private LayoutParams lp;
 private Drawable[] drawables;
 private Random random = new Random();
 private int dHeight;//爱心的高度
 private int dWidth;//爱心的宽度
   //content 上下文 
 //AttributeSet 属性集
 //defStyleAttr 预设样式属性集
 //defStyleRes 预设样式资源属性集
 public PeriscopeLayout(Context context) {
  super(context); 
  init();
 }
 public PeriscopeLayout(Context context, AttributeSet attrs) {
  super(context, attrs);
  init();
 }
 public PeriscopeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  init();
 }
 
 public PeriscopeLayout(Context context, AttributeSet attrs,
   int defStyleAttr, int defStyleRes) {
  super(context, attrs, defStyleAttr);
  init();
 }
private void init() {
  // 初始化显示的图片
  drawables = new Drawable[3];
  Drawable red = getResources().getDrawable(R.drawable.pl_red);
  Drawable yellow = getResources().getDrawable(R.drawable.pl_yellow);
  Drawable blue = getResources().getDrawable(R.drawable.pl_blue);
  drawables[0] = red;
  drawables[1] = yellow;
  drawables[2] = blue;
  // 获取图的宽高 用于后面的计算
  // 注意 我这里3张图片的大小都是一样的,所以我只取了一个
  dHeight = red.getIntrinsicHeight()/5*3;
  dWidth = red.getIntrinsicWidth()/5*3;
  // 底部 并且 水平居中
  lp = new LayoutParams(dWidth, dHeight);
 //rightMargin设置爱心离父布局相对边缘的位置,可以通过这个属性来自由调节图片的位置
  lp.rightMargin=80;
  //center_Horizontal,allgn_parent_right
  //lp.addRule(CENTER_IN_PARENT,TRUE);// 这里的TRUE 要注意 不是true
     lp.addRule(ALIGN_PARENT_RIGHT, TRUE);
       lp.addRule(ALIGN_PARENT_BOTTOM, TRUE);
  // 初始化插补器
//  interpolators = new Interpolator[4];
//  interpolators[0] = line;
//  interpolators[3] = acc;
//  interpolators[1] = dce;
//  interpolators[2] = accdec;
 }</div><div>
</div><div> @Override
 //获取view的宽,高。
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  mWidth = getMeasuredWidth();
  mHeight = getMeasuredHeight();
  super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//  mWidth = getMeasuredWidth();
//  mHeight = getMeasuredHeight();
 }
  //添加心跳图片,在此处添加等级对应的图片资源
 public  void addHeart() {
 //缩小动画
  mLitteAnimation = AnimationUtils.loadAnimation(getContext(), R.anim.scalelitte);
  //动画
  // 随机选一个
    ImageView imageView = new ImageView(getContext());
    imageView.startAnimation(mLitteAnimation);
            imageView.setImageDrawable(drawables[random.nextInt(1)]);
            imageView.setLayoutParams(lp);
            
  //缩小
  addView(imageView);
  Animator set = getAnimator(imageView);
  set.addListener(new AnimEndListener(imageView));
  set.start();
 }
  //获得动画器
 private Animator getAnimator(View target) {
  AnimatorSet set = getEnterAnimtor(target);
  ValueAnimator bezierValueAnimator = getBezierValueAnimator(target);
  AnimatorSet finalSet = new AnimatorSet();
  finalSet.playSequentially(set);
  finalSet.playSequentially(set, bezierValueAnimator);
  //finalSet.setInterpolator(interpolators[random.nextInt(4)]);
  /*Random.nextint() 和Math.random()的区别
                    前者生成的随机数效率高于后者,时间上前者大约是后者50%到80%的时间. 
                    造成这个原因如下: 
         Math.random()是Random.nextDouble()的一个内部方法. 
         Random.nextDouble()使用Random.next()两次,均匀的分布范围为0到1 - (2 ^ -53). 
         Random.nextInt(n)的使用Random.next()不多于两次, 返回值范围为0到n - 1的分布
         ****
         *nextInt(4)将产生0,1,2,3这4个数字中的任何一个数字,注意这里不是0-4,而是0-3。
                         但下限总是零,不能更改
   * 
   * */
   
  finalSet.setTarget(target);
  return finalSet;
 }
  //字面上的意思 获取进入的动画器
 private AnimatorSet getEnterAnimtor(final View target) {
  //关于这边的内容 可以去 参考 郭大神的博客
  //http://blog.csdn.net/guolin_blog/article/details/43816093
       //大概意思是获取一个float类型数值的动画(target:是对象,假如我们是textview的动画,我们就传入其对象名字)
  //View.ALPHA 是我们用到的动画
  //0.2f,lf 跟我们动画的透明值。
  ObjectAnimator alpha = ObjectAnimator.ofFloat(target, View.ALPHA, 0.2f,
    1f);
  ObjectAnimator scaleX = ObjectAnimator.ofFloat(target, View.SCALE_X,
    0.2f, 1f);
  ObjectAnimator scaleY = ObjectAnimator.ofFloat(target, View.SCALE_Y,
    0.2f, 1f);
  AnimatorSet enter = new AnimatorSet();
  //这里要设置成0,不然的话在右上角也会出现一个爱心
  enter.setDuration(0);
  enter.setInterpolator(new LinearInterpolator());
  enter.playTogether(alpha, scaleX, scaleY);
  enter.setTarget(target);
  return enter;
 }
 /*
  * 
  * 
  * */
 private ValueAnimator getBezierValueAnimator(View target) {</div><div>
</div><div>  // 初始化一个贝塞尔计算器- - 传入
  BezierEvaluator evaluator = new BezierEvaluator(getPointF(2),
    getPointF(1));</div><div>
</div><div>  // 这里最好画个图 理解一下 传入了起点 和 终点
  //mHeight - dHeight-200
  //mWidth - dWidth) / 7*6 代表的心跳的起点位置在靠右边占整个屏幕的七分之六
  ValueAnimator animator = ValueAnimator.ofObject(evaluator, new PointF(
    (mWidth - dWidth) / 7*6, mHeight - dHeight-200),
    new PointF((mWidth - dWidth) / 7*6, getMeasuredHeight()/2));
  animator.addUpdateListener(new BezierListenr(target));
  animator.setTarget(target);
  //设置心跳动画的持续时间
  animator.setDuration(4000);
  return animator;
 }</div><div>
</div><div> /**
  * 获取中间的两个 点
  *
  * @param scale
  */
 private PointF getPointF(int scale) {</div><div>
</div><div>  PointF pointF = new PointF();
  int Startpx=(mWidth - dWidth) / 7*6;
  //pointF.x = random.nextInt(getMeasuredWidth()-(mWidth - dWidth) / 7*6);//这里是 是为了控制 x轴活动范围,看效果 随意~~
  //pointF.x=random.nextInt(getMeasuredWidth());
  pointF.x = random.nextInt(Startpx+getMeasuredWidth()/100+25);
   Log.i("x", "随机值");
  pointF.y =  getMeasuredHeight()/2;
  // 再Y轴上 为了确保第二个点 在第一个点之上,我把Y分成了上下两半 这样动画效果好一些 也可以用其他方法
  //pointF.y = random.nextInt((mHeight - 100))/scale;
  
  // pointF.x = random.nextInt((mWidth - 100));
  //
        //pointF.y = random.nextInt((mHeight - 100))/scale;
  return pointF;
 }</div><div>
</div><div> private class BezierListenr implements ValueAnimator.AnimatorUpdateListener {</div><div>
</div><div>  private View target;</div><div>
</div><div>  public BezierListenr(View target) {
   this.target = target;
  }</div><div>
</div><div>  @Override
  public void onAnimationUpdate(ValueAnimator animation) {
   // 这里获取到贝塞尔曲线计算出来的的x y值 赋值给view 这样就能让爱心随着曲线走啦
   PointF pointF = (PointF) animation.getAnimatedValue();
   target.setX(pointF.x);
   target.setY(pointF.y);
   // 这里顺便做一个alpha动画
   target.setAlpha((float) (1-animation.getAnimatedFraction()));
  // target.setAlpha(1 - animation.getAnimatedFraction());
  }
 }</div><div>
</div><div> private class AnimEndListener extends AnimatorListenerAdapter {
  private View target;</div><div>
</div><div>  public AnimEndListener(View target) {
   this.target = target;
  }</div><div>
</div><div>  @Override
  public void onAnimationEnd(Animator animation) {
   super.onAnimationEnd(animation);
   // 因为不停的add 导致子view数量只增不减,所以在view动画结束后remove掉
   removeView((target));
  }
 }
}
</div>


大家耐心的看下注释,跟着这个照着葫芦画瓢 也能做出这样一个动画的

为了实现点击屏幕出现这个爱心 所有 我们 要另外在一个类里面实现他的屏幕点击事情。

微笑 

public boolean onTouchEvent(MotionEvent event) {
  // TODO Auto-generated method stub
  int touchEvent = event.getAction(); 
   switch (touchEvent)
   {
   //监听屏幕被按下
   case MotionEvent.ACTION_DOWN:
   //调用添加泡泡的方法
    periscopeLayout.addHeart();

    break;
   }
  return super.onTouchEvent(event);
 }


我们需要实现一个图片缩小到大的动画,就需要在res文件下新建一个anim 文件夹 写我们的xml动画
<?xml version="1.0" encoding="utf-8"?>
<scale
   xmlns:android="http://schemas.android.com/apk/res/android"
   android:fromXScale="0.0"
   android:toXScale="1.0"
   android:fromYScale="0.0"
   android:toYScale="1.0"
   android:pivotX="50%"
   android:pivotY="50%"
   android:duration="2000">
</scale>


我们实现点赞的代码也就是这么简单了,demo下载地址:demo下载链接(配合博客一起看)

对这个编辑器还是不太懂,写出来的东西可读性没有人家那些好。不过后面多写了,也就会慢慢熟悉了。
http://download.csdn.net/detail/ningzhouxu/9528218







 

 




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值