您的位置:首页 > 运维架构

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

2016-05-29 15:06 393 查看
因为项目需要,开始研究动画。这是为了实现直播平台的点赞功能特效而做的,参阅了很多资料,大家想看 一些关于安卓的高机动画内容可以去关注一下他的[刘某人]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

                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: