前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android使用SurfaceView实现飘赞动画

Android使用SurfaceView实现飘赞动画

作者头像
砸漏
发布2020-11-04 13:31:10
7210
发布2020-11-04 13:31:10
举报
文章被收录于专栏:恩蓝脚本恩蓝脚本

最近做直播项目,需要实现点赞动画,一提起动画就想到了使用View的属性动画,后来想了一下,那么多用户点赞,会导致屏幕上出现很多View,开销太大,一定会很卡,所以看主流主播软件用什么方案解决的。

于是反编译了映客apk,大概看了一下,它的点赞只用了一个SurfaceView,每个心都是实时画到画布上去的,这样效率确实很高,再多的心也不怕了。思路有了,但是自己从头到尾写毕竟麻烦,后来上网查了是否有其他人已经做好了呢?果然有现成的,思路很清晰,很简洁,根据自己的需求改一改就好了。

前面说了一堆,主要想说明有些效果自己虽然没做过,但是可以参考其他成熟产品是怎么做的,这样会少走弯路,试想如果自己只用view属性动画,也实现了,岂不是卡的要死,最后还是要推倒重做的。

先看一下效果:

ZanBean类,每个ZanBean都要负责实时更新自己的位置、透明度等数据

代码语言:javascript
复制
import android.animation.TypeEvaluator; 
import android.animation.ValueAnimator; 
import android.annotation.TargetApi; 
import android.content.Context; 
import android.graphics.Bitmap; 
import android.graphics.BitmapFactory; 
import android.graphics.Canvas; 
import android.graphics.Matrix; 
import android.graphics.Paint; 
import android.graphics.Point; 
import android.os.Build; 
import java.util.Random; 
public class ZanBean { 
/** 
* 心的当前坐标 
*/ 
public Point point; 
/** 
* 移动动画 
*/ 
private ValueAnimator moveAnim; 
/** 
* 放大动画 
*/ 
private ValueAnimator zoomAnim; 
/** 
* 透明度 
*/ 
public int alpha = 255;// 
/** 
* 心图 
*/ 
private Bitmap bitmap; 
/** 
* 绘制bitmap的矩阵 用来做缩放和移动的 
*/ 
private Matrix matrix = new Matrix(); 
/** 
* 缩放系数 
*/ 
private float sf = 0; 
/** 
* 产生随机数 
*/ 
private Random random; 
public boolean isEnd = false;//是否结束 
public ZanBean(Context context, int resId, ZanView zanView) { 
random = new Random(); 
bitmap = BitmapFactory.decodeResource(context.getResources(), resId); 
init(new Point(zanView.getWidth() / 2, zanView.getHeight()- bitmap.getHeight() / 2), new Point((random.nextInt(zanView.getWidth())), 0)); 
} 
public ZanBean(Bitmap bitmap, ZanView zanView) { 
random = new Random(); 
this.bitmap = bitmap; 
//为了让在起始坐标点时显示完整 需要减去bitmap.getHeight()/2 
init(new Point(zanView.getWidth() / 2, zanView.getHeight() - bitmap.getHeight() / 2), new Point((random.nextInt(zanView.getWidth())), 0)); 
} 
@TargetApi(Build.VERSION_CODES.HONEYCOMB) 
private void init(final Point startPoint, Point endPoint) { 
moveAnim = ValueAnimator.ofObject(new BezierEvaluator(new Point(random.nextInt(startPoint.x * 2), Math.abs(endPoint.y - startPoint.y) / 2)), startPoint, endPoint); 
moveAnim.setDuration(1500); 
moveAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 
@Override 
public void onAnimationUpdate(ValueAnimator animation) { 
point = (Point) animation.getAnimatedValue(); 
alpha = (int) ((float) point.y / (float) startPoint.y * 255); 
} 
}); 
moveAnim.start(); 
zoomAnim = ValueAnimator.ofFloat(0, 1f).setDuration(700); 
zoomAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 
@Override 
public void onAnimationUpdate(ValueAnimator animation) { 
Float f = (Float) animation.getAnimatedValue(); 
sf = f.floatValue(); 
} 
}); 
zoomAnim.start(); 
} 
// public void pause(){ 
//  if(moveAnim !=null&& moveAnim.isRunning()){ 
//   moveAnim.pause(); 
//  } 
//  if(zoomAnim !=null&& zoomAnim.isRunning()){ 
//   zoomAnim.pause(); 
//  } 
// } 
// 
// public void resume(){ 
//  if(moveAnim !=null&& moveAnim.isPaused()){ 
//   moveAnim.resume(); 
//  } 
//  if(zoomAnim !=null&& zoomAnim.isPaused()){ 
//   zoomAnim.resume(); 
//  } 
// } 
public void stop() { 
if (moveAnim != null) { 
moveAnim.cancel(); 
moveAnim = null; 
} 
if (zoomAnim != null) { 
zoomAnim.cancel(); 
zoomAnim = null; 
} 
} 
/** 
* 主要绘制函数 
*/ 
public void draw(Canvas canvas, Paint p) { 
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) 
private class BezierEvaluator implements TypeEvaluator<Point  { 
private Point centerPoint; 
public BezierEvaluator(Point centerPoint) { 
this.centerPoint = centerPoint; 
} 
@Override 
public Point evaluate(float t, Point startValue, Point endValue) { 
int x = (int) ((1 - t) * (1 - t) * startValue.x + 2 * t * (1 - t) * centerPoint.x + t * t * endValue.x); 
int y = (int) ((1 - t) * (1 - t) * startValue.y + 2 * t * (1 - t) * centerPoint.y + t * t * endValue.y); 
return new Point(x, y); 
} 
} 
} 

ZanView代码如下:SurfaceView,不断将ZanBean画到自己的画布上。

代码语言:javascript
复制
import android.content.Context; 
import android.graphics.Canvas; 
import android.graphics.Color; 
import android.graphics.Paint; 
import android.graphics.PixelFormat; 
import android.graphics.PorterDuff; 
import android.util.AttributeSet; 
import android.view.SurfaceHolder; 
import android.view.SurfaceView; 
import java.util.ArrayList; 
public class ZanView extends SurfaceView implements SurfaceHolder.Callback { 
private SurfaceHolder surfaceHolder; 
/** 
* 心的个数 
*/ 
private ArrayList<ZanBean  zanBeen = new ArrayList< (); 
private Paint p; 
/** 
* 负责绘制的工作线程 
*/ 
private DrawThread drawThread; 
public ZanView(Context context) { 
this(context, null); 
} 
public ZanView(Context context, AttributeSet attrs) { 
this(context, attrs, 0); 
} 
public ZanView(Context context, AttributeSet attrs, int defStyleAttr) { 
super(context, attrs, defStyleAttr); 
this.setZOrderOnTop(true); 
/**设置画布 背景透明*/ 
this.getHolder().setFormat(PixelFormat.TRANSLUCENT); 
surfaceHolder = getHolder(); 
surfaceHolder.addCallback(this); 
p = new Paint(); 
p.setAntiAlias(true); 
drawThread = new DrawThread(); 
} 
/** 
* 点赞动作 添加心的函数 控制画面最大心的个数 
*/ 
public void addZanXin(ZanBean zanBean) { 
zanBeen.add(zanBean); 
if (zanBeen.size()   40) { 
zanBeen.remove(0); 
} 
start(); 
} 
@Override 
public void surfaceCreated(SurfaceHolder holder) { 
if (drawThread == null) { 
drawThread = new DrawThread(); 
} 
drawThread.start(); 
} 
@Override 
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { 
} 
@Override 
public void surfaceDestroyed(SurfaceHolder holder) { 
if (drawThread != null) { 
drawThread.isRun = false; 
drawThread = null; 
} 
} 
class DrawThread extends Thread { 
boolean isRun = true; 
@Override 
public void run() { 
super.run(); 
/**绘制的线程 死循环 不断的跑动*/ 
while (isRun) { 
Canvas canvas = null; 
try { 
synchronized (surfaceHolder) { 
canvas = surfaceHolder.lockCanvas(); 
/**清除画面*/ 
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); 
boolean isEnd = true; 
/**对所有心进行遍历绘制*/ 
for (int i = 0; i < zanBeen.size(); i++) { 
isEnd = zanBeen.get(i).isEnd; 
zanBeen.get(i).draw(canvas, p); 
} 
/**这里做一个性能优化的动作,由于线程是死循环的 在没有心需要的绘制的时候会结束线程*/ 
if (isEnd) { 
isRun = false; 
drawThread = null; 
} 
} 
} catch (Exception e) { 
e.printStackTrace(); 
} finally { 
if (canvas != null) { 
surfaceHolder.unlockCanvasAndPost(canvas); 
} 
} 
try { 
/**用于控制绘制帧率*/ 
Thread.sleep(10); 
} catch (InterruptedException e) { 
e.printStackTrace(); 
} 
} 
} 
} 
public void stop() { 
if (drawThread != null) { 
//   for (int i = 0; i < zanBeen.size(); i++) { 
//    zanBeen.get(i).pause(); 
//   } 
for (int i = 0; i < zanBeen.size(); i++) { 
zanBeen.get(i).stop(); 
} 
drawThread.isRun = false; 
drawThread = null; 
} 
} 
public void start() { 
if (drawThread == null) { 
//   for (int i = 0; i < zanBeen.size(); i++) { 
//    zanBeen.get(i).resume(); 
//   } 
drawThread = new DrawThread(); 
drawThread.start(); 
} 
} 
} 

调用方式:

代码语言:javascript
复制
public class TestActivity extends BaseActivity { 
@Override 
protected void onCreate(Bundle savedInstanceState) { 
super.onCreate(savedInstanceState); 
setContentView(R.layout.test_zan); 
final ZanView zan = (ZanView) findViewById(R.id.zan_view); 
zan.start(); 
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() { 
@Override 
public void onClick(View v) { 
ZanBean zanBean = new ZanBean(BitmapFactory.decodeResource(getResources(), R.drawable.ic_default_avatar), zan); 
zan.addZanXin(zanBean); 
} 
}); 
} 
} 

以上就是本文的全部内容,希望对大家的学习有所帮助。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-09-11 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云直播
云直播(Cloud Streaming Services,CSS)为您提供极速、稳定、专业的云端直播处理服务,根据业务的不同直播场景需求,云直播提供了标准直播、快直播、云导播台三种服务,分别针对大规模实时观看、超低延时直播、便捷云端导播的场景,配合腾讯云视立方·直播 SDK,为您提供一站式的音视频直播解决方案。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档