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