仿Periscope,映客用贝塞尔曲线实现点赞送爱心。
2016-05-29 15:06
393 查看
因为项目需要,开始研究动画。这是为了实现直播平台的点赞功能特效而做的,参阅了很多资料,大家想看 一些关于安卓的高机动画内容可以去关注一下他的[刘某人]http://http://my.csdn.net/qq_26787115的博客,相信会有一些收获的。
首先我们来看一下效果:(因为自己还不太熟gif动态录制工具,就先拿的别人的效果图过来了)
首先我们要知道什么是贝塞尔曲线。告诉你们一个方法贝塞尔曲线
现在我们开始正题
首先我们想要去规划爱心的轨迹,我们需要通过贝塞尔曲线公式来进行一个计算!
大家耐心的看下注释,跟着这个照着葫芦画瓢 也能做出这样一个动画的
为了实现点击屏幕出现这个爱心 所有 我们 要另外在一个类里面实现他的屏幕点击事情。
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
首先我们来看一下效果:(因为自己还不太熟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
相关文章推荐
- linux grep命令详解
- Apache Bench (ab)测试出现 Failed requests
- 网站系统---数据库的设计
- Linux中ctags、make以及进度条小程序(\r的应用)
- poj1157LITTLE SHOP OF FLOWERS(简单DP)
- Spark WordCount TopN
- 微服务架构
- linux shell数组
- Ubuntu 14.04 - devstack + openstack +ceph统一存储
- shell 解析 json
- linux 中cat的用法
- PopupWindow
- 基于OpenCV的火焰检测(三)——HSI颜色判据
- 一台电脑同时运行多个tomcat配置方法
- Linux新手的--vim配置方法
- centos 研究
- drop.delete.trauncat的区别
- 命令行shell 用于SQLite
- Vim的配置 和 windows与Linux之间的复制粘贴
- Linux安装VMware Tools教程