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

回到顶部