首页 > 系统 > Android > 正文

Android使用SurfaceView实现飘赞动画

2019-12-12 01:04:11
字体:
来源:转载
供稿:网友

最近做直播项目,需要实现点赞动画,一提起动画就想到了使用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);    }   });  } } 

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持武林网。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表