Android特效专辑(八)——实现心型起泡飞舞的特效,让你的APP瞬间暖心
马上也要放年假了,家里估计会没网,更完这篇的话,可能要到年后了,不过在此期间会把更新内容都保存在本地,这样有网就可以发表了,也是极好的,今天说的这个特效,原本是Only上的一个小彩蛋的,我们来看看图片
只要我点击了Only这个字,下面就开始上升起起泡了,这个实现起来其实就是一个欲盖弥彰的动画而已,准备好三张颜色不一样的心型图片咯,这样的话,我们就开始动手来写一写吧!
首先新建一个工程——HeartFaom
准备工作就是准备图片咯
BezierEvaluator
单位转换以及计算轨迹
packagecom.lgl.heartfaom;
importandroid.animation.TypeEvaluator;
importandroid.graphics.PointF;
publicclassBezierEvaluatorimplementsTypeEvaluator{
privatePointFpointF1;
privatePointFpointF2;
publicBezierEvaluator(PointFpointF1,PointFpointF2){
this.pointF1=pointF1;
this.pointF2=pointF2;
}
@Override
publicPointFevaluate(floattime,PointFstartValue,PointFendValue){
floattimeLeft=1.0f-time;
PointFpoint=newPointF();//结果
point.x=timeLefttimeLefttimeLeft(startValue.x)+3
timeLefttimeLefttime(pointF1.x)+3timeLeft
timetime(pointF2.x)+timetimetime(endValue.x);
point.y=timeLefttimeLefttimeLeft(startValue.y)+3
timeLefttimeLefttime(pointF1.y)+3timeLeft
timetime(pointF2.y)+timetimetime(endValue.y);
returnpoint;
}
}
PeriscopeLayout
贝塞尔曲线的计算以及气泡的实现
packagecom.lgl.heartfaom;
importjava.util.Random;
importandroid.animation.Animator;
importandroid.animation.AnimatorListenerAdapter;
importandroid.animation.AnimatorSet;
importandroid.animation.ObjectAnimator;
importandroid.animation.ValueAnimator;
importandroid.annotation.TargetApi;
importandroid.content.Context;
importandroid.graphics.PointF;
importandroid.graphics.drawable.Drawable;
importandroid.os.Build;
importandroid.util.AttributeSet;
importandroid.view.View;
importandroid.view.animation.AccelerateDecelerateInterpolator;
importandroid.view.animation.AccelerateInterpolator;
importandroid.view.animation.DecelerateInterpolator;
importandroid.view.animation.Interpolator;
importandroid.view.animation.LinearInterpolator;
importandroid.widget.ImageView;
importandroid.widget.RelativeLayout;
publicclassPeriscopeLayoutextendsRelativeLayout{
privateInterpolatorline=newLinearInterpolator();//线性
privateInterpolatoracc=newAccelerateInterpolator();//加速
privateInterpolatordce=newDecelerateInterpolator();//减速
privateInterpolatoraccdec=newAccelerateDecelerateInterpolator();//先加速后减速
privateInterpolator[]interpolators;
privateintmHeight;
privateintmWidth;
privateLayoutParamslp;
privateDrawable[]drawables;
privateRandomrandom=newRandom();
privateintdHeight;
privateintdWidth;
publicPeriscopeLayout(Contextcontext){
super(context);
init();
}
publicPeriscopeLayout(Contextcontext,AttributeSetattrs){
super(context,attrs);
init();
}
publicPeriscopeLayout(Contextcontext,AttributeSetattrs,intdefStyleAttr){
super(context,attrs,defStyleAttr);
init();
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
publicPeriscopeLayout(Contextcontext,AttributeSetattrs,
intdefStyleAttr,intdefStyleRes){
super(context,attrs,defStyleAttr,defStyleRes);
init();
}
privatevoidinit(){
//初始化显示的图片
drawables=newDrawable[3];
Drawablered=getResources().getDrawable(R.drawable.pl_red);
Drawableyellow=getResources().getDrawable(R.drawable.pl_yellow);
Drawableblue=getResources().getDrawable(R.drawable.pl_blue);
drawables[0]=red;
drawables[1]=yellow;
drawables[2]=blue;
//获取图的宽高用于后面的计算
//注意我这里3张图片的大小都是一样的,所以我只取了一个
dHeight=red.getIntrinsicHeight();
dWidth=red.getIntrinsicWidth();
//底部并且水平居中
lp=newLayoutParams(dWidth,dHeight);
lp.addRule(CENTER_HORIZONTAL,TRUE);//这里的TRUE要注意不是true
lp.addRule(ALIGN_PARENT_BOTTOM,TRUE);
//初始化插补器
interpolators=newInterpolator[4];
interpolators[0]=line;
interpolators[1]=acc;
interpolators[2]=dce;
interpolators[3]=accdec;
}
@Override
protectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec){
super.onMeasure(widthMeasureSpec,heightMeasureSpec);
mWidth=getMeasuredWidth();
mHeight=getMeasuredHeight();
}
publicvoidaddHeart(){
ImageViewimageView=newImageView(getContext());
//随机选一个
imageView.setImageDrawable(drawables[random.nextInt(3)]);
imageView.setLayoutParams(lp);
addView(imageView);
Animatorset=getAnimator(imageView);
set.addListener(newAnimEndListener(imageView));
set.start();
}
privateAnimatorgetAnimator(Viewtarget){
AnimatorSetset=getEnterAnimtor(target);
ValueAnimatorbezierValueAnimator=getBezierValueAnimator(target);
AnimatorSetfinalSet=newAnimatorSet();
finalSet.playSequentially(set);
finalSet.playSequentially(set,bezierValueAnimator);
finalSet.setInterpolator(interpolators[random.nextInt(4)]);
finalSet.setTarget(target);
returnfinalSet;
}
privateAnimatorSetgetEnterAnimtor(finalViewtarget){
ObjectAnimatoralpha=ObjectAnimator.ofFloat(target,View.ALPHA,0.2f,
1f);
ObjectAnimatorscaleX=ObjectAnimator.ofFloat(target,View.SCALE_X,
0.2f,1f);
ObjectAnimatorscaleY=ObjectAnimator.ofFloat(target,View.SCALE_Y,
0.2f,1f);
AnimatorSetenter=newAnimatorSet();
enter.setDuration(500);
enter.setInterpolator(newLinearInterpolator());
enter.playTogether(alpha,scaleX,scaleY);
enter.setTarget(target);
returnenter;
}
privateValueAnimatorgetBezierValueAnimator(Viewtarget){
//初始化一个贝塞尔计算器--传入
BezierEvaluatorevaluator=newBezierEvaluator(getPointF(2),
getPointF(1));
//这里最好画个图理解一下传入了起点和终点
ValueAnimatoranimator=ValueAnimator.ofObject(evaluator,newPointF(
(mWidth-dWidth)/2,mHeight-dHeight),
newPointF(random.nextInt(getWidth()),0));
animator.addUpdateListener(newBezierListenr(target));
animator.setTarget(target);
animator.setDuration(3000);
returnanimator;
}
/
获取中间的两个点
@paramscale
/
privatePointFgetPointF(intscale){
PointFpointF=newPointF();
pointF.x=random.nextInt((mWidth-100));//减去100是为了控制x轴活动范围,看效果随意~~
//再Y轴上为了确保第二个点在第一个点之上,我把Y分成了上下两半这样动画效果好一些也可以用其他方法
pointF.y=random.nextInt((mHeight-100))/scale;
returnpoinwww.shanxiwang.nettF;
}
privateclassBezierListenrimplementsValueAnimator.AnimatorUpdateListener{
privateViewtarget;
publicBezierListenr(Viewtarget){
this.target=target;
}
@Override
publicvoidonAnimationUpdate(ValueAnimatoranimation){
//这里获取到贝塞尔曲线计算出来的的xy值赋值给view这样就能让爱心随着曲线走啦
PointFpointF=(PointF)animation.getAnimatedValue();
target.setX(pointF.x);
target.setY(pointF.y);
//这里顺便做一个alpha动画
target.setAlpha(1-animation.getAnimatedFraction());
}
}
privateclassAnimEndListenerextendsAnimatorListenerAdapter{
privateViewtarget;
publicAnimEndListener(Viewtarget){
this.target=target;
}
@Override
publicvoidonAnimationEnd(Animatoranimation){
super.onAnimationEnd(animation);
//因为不停的add导致子view数量只增不减,所以在view动画结束后remove掉
removeView((target));
}
}
}
activity_main.xml
布局的实现
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000">
|
|