Android使用SurfaceView实现飘赞动画
最近做直播项目,需要实现点赞动画,一提起动画就想到了使用View的属性动画,后来想了一下,那么多用户点赞,会导致屏幕上出现很多View,开销太大,一定会很卡,所以看主流主播软件用什么方案解决的。
于是反编译了映客apk,大概看了一下,它的点赞只用了一个SurfaceView,每个心都是实时画到画布上去的,这样效率确实很高,再多的心也不怕了。思路有了,但是自己从头到尾写毕竟麻烦,后来上网查了是否有其他人已经做好了呢?果然有现成的,思路很清晰,很简洁,根据自己的需求改一改就好了。
前面说了一堆,主要想说明有些效果自己虽然没做过,但是可以参考其他成熟产品是怎么做的,这样会少走弯路,试想如果自己只用view属性动画,也实现了,岂不是卡的要死,最后还是要推倒重做的。
先看一下效果:
ZanBean类,每个ZanBean都要负责实时更新自己的位置、透明度等数据
importandroid.animation.TypeEvaluator;
importandroid.animation.ValueAnimator;
importandroid.annotation.TargetApi;
importandroid.content.Context;
importandroid.graphics.Bitmap;
importandroid.graphics.BitmapFactory;
importandroid.graphics.Canvas;
importandroid.graphics.Matrix;
importandroid.graphics.Paint;
importandroid.graphics.Point;
importandroid.os.Build;
importjava.util.Random;
publicclassZanBean{
/**
*心的当前坐标
*/
publicPointpoint;
/**
*移动动画
*/
privateValueAnimatormoveAnim;
/**
*放大动画
*/
privateValueAnimatorzoomAnim;
/**
*透明度
*/
publicintalpha=255;//
/**
*心图
*/
privateBitmapbitmap;
/**
*绘制bitmap的矩阵用来做缩放和移动的
*/
privateMatrixmatrix=newMatrix();
/**
*缩放系数
*/
privatefloatsf=0;
/**
*产生随机数
*/
privateRandomrandom;
publicbooleanisEnd=false;//是否结束
publicZanBean(Contextcontext,intresId,ZanViewzanView){
random=newRandom();
bitmap=BitmapFactory.decodeResource(context.getResources(),resId);
init(newPoint(zanView.getWidth()/2,zanView.getHeight()-bitmap.getHeight()/2),newPoint((random.nextInt(zanView.getWidth())),0));
}
publicZanBean(Bitmapbitmap,ZanViewzanView){
random=newRandom();
this.bitmap=bitmap;
//为了让在起始坐标点时显示完整需要减去bitmap.getHeight()/2
init(newPoint(zanView.getWidth()/2,zanView.getHeight()-bitmap.getHeight()/2),newPoint((random.nextInt(zanView.getWidth())),0));
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
privatevoidinit(finalPointstartPoint,PointendPoint){
moveAnim=ValueAnimator.ofObject(newBezierEvaluator(newPoint(random.nextInt(startPoint.x*2),Math.abs(endPoint.y-startPoint.y)/2)),startPoint,endPoint);
moveAnim.setDuration(1500);
moveAnim.addUpdateListener(newValueAnimator.AnimatorUpdateListener(){
@Override
publicvoidonAnimationUpdate(ValueAnimatoranimation){
point=(Point)animation.getAnimatedValue();
alpha=(int)((float)point.y/(float)startPoint.y*255);
}
});
moveAnim.start();
zoomAnim=ValueAnimator.ofFloat(0,1f).setDuration(700);
zoomAnim.addUpdateListener(newValueAnimator.AnimatorUpdateListener(){
@Override
publicvoidonAnimationUpdate(ValueAnimatoranimation){
Floatf=(Float)animation.getAnimatedValue();
sf=f.floatValue();
}
});
zoomAnim.start();
}
//publicvoidpause(){
//if(moveAnim!=null&&moveAnim.isRunning()){
//moveAnim.pause();
//}
//if(zoomAnim!=null&&zoomAnim.isRunning()){
//zoomAnim.pause();
//}
//}
//
//publicvoidresume(){
//if(moveAnim!=null&&moveAnim.isPaused()){
//moveAnim.resume();
//}
//if(zoomAnim!=null&&zoomAnim.isPaused()){
//zoomAnim.resume();
//}
//}
publicvoidstop(){
if(moveAnim!=null){
moveAnim.cancel();
moveAnim=null;
}
if(zoomAnim!=null){
zoomAnim.cancel();
zoomAnim=null;
}
}
/**
*主要绘制函数
*/
publicvoiddraw(Canvascanvas,Paintp){
if(bitmap!=null&&alpha>0){
p.setAlpha(alpha);
matrix.setScale(sf,sf,bitmap.getWidth()/2,bitmap.getHeight()/2);
matrix.postTranslate(point.x-bitmap.getWidth()/2,point.y-bitmap.getHeight()/2);
canvas.drawBitmap(bitmap,matrix,p);
}else{
isEnd=true;
}
}
/**
*二次贝塞尔曲线
*/
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
privateclassBezierEvaluatorimplementsTypeEvaluator{
privatePointcenterPoint;
publicBezierEvaluator(PointcenterPoint){
this.centerPoint=centerPoint;
}
@Override
publicPointevaluate(floatt,PointstartValue,PointendValue){
intx=(int)((1-t)*(1-t)*startValue.x+2*t*(1-t)*centerPoint.x+t*t*endValue.x);
inty=(int)((1-t)*(1-t)*startValue.y+2*t*(1-t)*centerPoint.y+t*t*endValue.y);
returnnewPoint(x,y);
}
}
}
ZanView代码如下:SurfaceView,不断将ZanBean画到自己的画布上。
importandroid.content.Context;
importandroid.graphics.Canvas;
importandroid.graphics.Color;
importandroid.graphics.Paint;
importandroid.graphics.PixelFormat;
importandroid.graphics.PorterDuff;
importandroid.util.AttributeSet;
importandroid.view.SurfaceHolder;
importandroid.view.SurfaceView;
importjava.util.ArrayList;
publicclassZanViewextendsSurfaceViewimplementsSurfaceHolder.Callback{
privateSurfaceHoldersurfaceHolder;
/**
*心的个数
*/
privateArrayListzanBeen=newArrayList<>();
privatePaintp;
/**
*负责绘制的工作线程
*/
privateDrawThreaddrawThread;
publicZanView(Contextcontext){
this(context,null);
}
publicZanView(Contextcontext,AttributeSetattrs){
this(context,attrs,0);
}
publicZanView(Contextcontext,AttributeSetattrs,intdefStyleAttr){
super(context,attrs,defStyleAttr);
this.setZOrderOnTop(true);
/**设置画布背景透明*/
this.getHolder().setFormat(PixelFormat.TRANSLUCENT);
surfaceHolder=getHolder();
surfaceHolder.addCallback(this);
p=newPaint();
p.setAntiAlias(true);
drawThread=newDrawThread();
}
/**
*点赞动作添加心的函数控制画面最大心的个数
*/
publicvoidaddZanXin(ZanBeanzanBean){
zanBeen.add(zanBean);
if(zanBeen.size()>40){
zanBeen.remove(0);
}
start();
}
@Override
publicvoidsurfaceCreated(SurfaceHolderholder){
if(drawThread==null){
drawThread=newDrawThread();
}
drawThread.start();
}
@Override
publicvoidsurfaceChanged(SurfaceHolderholder,intformat,intwidth,intheight){
}
@Override
publicvoidsurfaceDestroyed(SurfaceHolderholder){
if(drawThread!=null){
drawThread.isRun=false;
drawThread=null;
}
}
classDrawThreadextendsThread{
booleanisRun=true;
@Override
publicvoidrun(){
super.run();
/**绘制的线程死循环不断的跑动*/
while(isRun){
Canvascanvas=null;
try{
synchronized(surfaceHolder){
canvas=surfaceHolder.lockCanvas();
/**清除画面*/
canvas.drawColor(Color.TRANSPARENT,PorterDuff.Mode.CLEAR);
booleanisEnd=true;
/**对所有心进行遍历绘制*/
for(inti=0;i
调用方式:
publicclassTestActivityextendsBaseActivity{
@Override
protectedvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.test_zan);
finalZanViewzan=(ZanView)findViewById(R.id.zan_view);
zan.start();
findViewById(R.id.button).setOnClickListener(newView.OnClickListener(){
@Override
publicvoidonClick(Viewv){
ZanBeanzanBean=newZanBean(BitmapFactory.decodeResource(getResources(),R.drawable.ic_default_avatar),zan);
zan.addZanXin(zanBean);
}
});
}
}
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。