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); } }); } }以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。