Android使用SurfaceView實現(xiàn)飄贊動畫
最近做直播項目,需要實現(xiàn)點贊動畫,一提起動畫就想到了使用View的屬性動畫,后來想了一下,那么多用戶點贊,會導致屏幕上出現(xiàn)很多View,開銷太大,一定會很卡,所以看主流主播軟件用什么方案解決的。
于是反編譯了映客apk,大概看了一下,它的點贊只用了一個SurfaceView,每個心都是實時畫到畫布上去的,這樣效率確實很高,再多的心也不怕了。思路有了,但是自己從頭到尾寫畢竟麻煩,后來上網(wǎng)查了是否有其他人已經(jīng)做好了呢?果然有現(xiàn)成的,思路很清晰,很簡潔,根據(jù)自己的需求改一改就好了。
前面說了一堆,主要想說明有些效果自己雖然沒做過,但是可以參考其他成熟產(chǎn)品是怎么做的,這樣會少走彎路,試想如果自己只用view屬性動畫,也實現(xiàn)了,豈不是卡的要死,最后還是要推倒重做的。
先看一下效果:
ZanBean類,每個ZanBean都要負責實時更新自己的位置、透明度等數(shù)據(jù)
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(); /** * 縮放系數(shù) */ private float sf = 0; /** * 產(chǎn)生隨機數(shù) */ 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; } } /** * 主要繪制函數(shù) */ 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; /** * 心的個數(shù) */ 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(); } /** * 點贊動作 添加心的函數(shù) 控制畫面最大心的個數(shù) */ 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(); /**繪制的線程 死循環(huán) 不斷的跑動*/ 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); } /**這里做一個性能優(yōu)化的動作,由于線程是死循環(huán)的 在沒有心需要的繪制的時候會結束線程*/ 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(); } } }
調(diào)用方式:
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); } }); } }
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
Android LayoutInflater.inflate()詳解及分析
這篇文章主要介紹了Android LayoutInflater.inflate()詳解及分析的相關資料,需要的朋友可以參考下2017-01-01詳解Android Studio3.5及使用AndroidX的一些坑
這篇文章主要介紹了詳解Android Studio3.5及使用AndroidX的一些坑,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-11-11使用Android的OkHttp包實現(xiàn)基于HTTP協(xié)議的文件上傳下載
OkHttp(GitHub主頁https://github.com/square/okhttp)是近來人氣攀升的一款安卓第三方HTTP包,這里我們來講解一下如何使用Android的OkHttp包實現(xiàn)基于HTTP協(xié)議的文件上傳下載:2016-07-07