Android使用SurfaceView实现飘赞动画
最近做直播项目,需要实现点赞动画,一提起动画就想到了使用View的属性动画,后来想了一下,那么多用户点赞,会导致屏幕上出现很多View,开销太大,一定会很卡,所以看主流主播软件用什么方案解决的。
于是反编译了映客apk,大概看了一下,它的点赞只用了一个SurfaceView,每个心都是实时画到画布上去的,这样效率确实很高,再多的心也不怕了。思路有了,但是自己从头到尾写毕竟麻烦,后来上网查了是否有其他人已经做好了呢?果然有现成的,思路很清晰,很简洁,根据自己的需求改一改就好了。
前面说了一堆,主要想说明有些效果自己虽然没做过,但是可以参考其他成熟产品是怎么做的,这样会少走弯路,试想如果自己只用view属性动画,也实现了,岂不是卡的要死,最后还是要推倒重做的。
先看一下效果:
ZanBean类,每个ZanBean都要负责实时更新自己的位置、透明度等数据
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画到自己的画布上。
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();
}
}
}
调用方式:
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);
}
});
}
}
以上是 Android使用SurfaceView实现飘赞动画 的全部内容, 来源链接: utcz.com/z/333243.html