基於 SurfaceView 的直播點亮心形效果


本文來自zyyoona7,zyyoona7的blog連接爲:http://www.jianshu.com/p/6d2cc30e4687。本文主要是直播界面中點贊效果,固然也能夠用OpenGL去作。
java


zyyonna7簡介:本人是一名Android開發者,從大學開始接觸Android,如今工做恰好滿兩年。在這年中本身以爲成長了不少不少,過去的兩年中作了不少公司項目,每一個項目中都充當比較重要的角色。堅持用blog在不斷提高本身。android


先來展現下效果圖:canvas


圖片


你們看到效果應該都不陌生,網上已經有不少相同的效果,可是網上大可能是經過動畫來實現,而我這個是經過自定義 SurfaceView 來實現。這個想法主要來自於反編譯映客 App,雖然看不到源碼,但給我提供了思路。接下來進入正題~app

1. 自定義 SurfaceView 鞏固

自定義 SurfaceView 須要三點:繼承 SurfaceView、實現SurfaceHolder.Callback、提供渲染線程。dom

繼承 SurfaceView不須要多說,說一下 SurfaceHolder.Callback 須要實現的三個方法:ide

  • public void surfaceCreated(SurfaceHolder holder) : 當 Surface 第一次建立後會當即調用該函數。程序能夠在該函數中作些和繪製界面相關的初始化工做,通常狀況下都是在另外的線程來繪製界面,因此不要在這個函數中繪製 Surface。函數

  • public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) : 當 Surface 的狀態(大小和格式)發生變化的時候會調用該函數,在 surfaceCreated() 調用後該函數至少會被調用一次。post

  • public void surfaceDestroyed(SurfaceHolder holder) : 當 Surface 被銷燬前會調用該函數,該函數被調用後就不能繼續使用 Surface 了,通常在該函數中來清理使用的資源。動畫

下面提供一個自定義 SurfaceView 的一個簡單模板:this

public class SimpleSurfaceView extends SurfaceView     implements SurfaceHolder.Callback, Runnable {    // 子線程標誌位    private boolean isRunning;    //畫筆    private Paint mPaint;    public SimpleSurfaceView(Context context) {        super(context, null);    }    public SimpleSurfaceView(Context context, AttributeSet attrs) {        super(context, attrs);        init();    }    private void init() {        mPaint = new Paint();        mPaint.setAntiAlias(true);        //...        getHolder().addCallback(this);        setFocusable(true);        setFocusableInTouchMode(true);        this.setKeepScreenOn(true);    }    @Override    public void surfaceCreated(SurfaceHolder holder) {        isRunning = true;        //啓動渲染線程        new Thread(this).start();    }    @Override    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {    }    @Override    public void surfaceDestroyed(SurfaceHolder holder) {        isRunning = false;    }    @Override    public void run() {        while (isRunning) {            Canvas canvas = null;            try {                canvas = getHolder().lockCanvas();                if (canvas != null) {                    // draw something                    drawSomething(canvas);                }            } catch (Exception e) {                e.printStackTrace();            } finally {                if (canvas != null) {                    getHolder().unlockCanvasAndPost(canvas);                }            }        }    }    /**     * draw something     *     * @param canvas     */    private void drawSomething(Canvas canvas) {    }}


2. HeartView 實現

HeartView 實現主要分爲3部分:

  • 初始化值,向集合中添加 Heart 對象

  • 經過三階貝塞爾曲線實時計算每一個 Heart 對象的座標

  • 在渲染線程遍歷集合,畫出 bitmap

首先說下三階貝塞爾曲線的幾個主要參數:起始點、結束點、控制點一、控制點二、時間(從 0 到 1 )。對貝塞爾曲線不瞭解的或者想更詳細的瞭解的能夠看一下 Path 之貝塞爾曲線 這邊文章。

接着來看一下 Heart 類中的主要屬性:

public class Heart {        //實時座標    private float x;    private float y;    //起始點座標    private float startX;    private float startY;    //結束點座標    private float endX;    private float endY;    //三階貝塞爾曲線(兩個控制點)    //控制點1座標    private float control1X;    private float control1Y;    //控制點2座標    private float control2X;    private float control2Y;    //實時的時間    private float t=0;    //速率    private float speed;}

經過三階貝塞爾曲線函數來計算實時座標的公式以下:

//三階貝塞爾曲線函數float x = (float) (Math.pow((1 - t), 3) * start.x +          3 * t * Math.pow((1 - t), 2) * control1.x +          3 * Math.pow(t, 2) * (1 - t) * control2.x +          Math.pow(t, 3) * end.x);float y = (float) (Math.pow((1 - t), 3) * start.y +          3 * t * Math.pow((1 - t), 2) * control1.y +          3 * Math.pow(t, 2) * (1 - t) * control2.y +          Math.pow(t, 3) * end.y);


有了公式,有了 Heart 類,咱們還須要在 Heart 初始化的時候,給它的屬性隨機設置初始值,代碼以下:

//Heart.java    /**     * 重置下x,y座標     * 位置在最底部的中間     *     * @param x     * @param y     */    public void initXY(float x, float y) {        this.x = x;        this.y = y;    }    /**     * 重置起始點和結束點     *     * @param width     * @param height     */    public void initStartAndEnd(float width, float height) {        //起始點和結束點爲view的正下方和正上方        this.startX = width / 2;        this.startY = height;        this.endX = width / 2;        this.endY = 0;        initXY(startX,startY);    }    /**     * 重置控制點座標     *     * @param width     * @param height     */    public void initControl(float width, float height) {        //隨機生成控制點1        this.control1X = (float) (Math.random() * width);        this.control1Y = (float) (Math.random() * height);        //隨機生成控制點2        this.control2X = (float) (Math.random() * width);        this.control2Y = (float) (Math.random() * height);        //若是兩個點重合,從新生成控制點        if (this.control1X == this.control2X        && this.control1Y == this.control2Y) {            initControl(width, height);        }    }    /**     * 重置速率     */    public void initSpeed() {        //隨機速率        this.speed = (float) (Math.random() * 0.01 + 0.003);    }//HeartView.java    /**     * 添加heart     */    public void addHeart() {        Heart heart = new Heart();        initHeart(heart);        mHearts.add(heart);    }    /**     * 重置 Heart 屬性     *     * @param heart     */    private void initHeart(Heart heart) {          //mWidth、mHeight 分別爲 view 的寬、高        heart.initStartAndEnd(mWidth, mHeight);        heart.initControl(mWidth, mHeight);        heart.initSpeed();    }

萬事具有,只欠東風。屬性都已經準備就緒,接下來就開始畫了:

//HeartView.java        @Override    public void run() {        while (isRunning) {            Canvas canvas = null;            try {                canvas = getHolder().lockCanvas();                if (canvas != null) {                      //開始畫                    drawHeart(canvas);                }            } catch (Exception e) {                Log.e(TAG, "run: " + e.getMessage());            } finally {                if (canvas != null) {                    getHolder().unlockCanvasAndPost(canvas);                }            }        }    }    /**     * 畫集合內的心形     * @param canvas     */    private void drawHeart(Canvas canvas) {        //清屏~        canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);        for (Heart heart : mHearts) {            if (mBitmapSparseArray.get(heart.getType()) == null) {                continue;            }            //會覆蓋掉以前的x,y數值            mMatrix.setTranslate(0, 0);            //位移到x,y            mMatrix.postTranslate(heart.getX(), heart.getY());            //縮放            //mMatrix.postScale();              //旋轉            //mMatrix.postRotate();            //畫bitmap            canvas.drawBitmap(mBitmapSparseArray.get(        heart.getType()), mMatrix, mPaint);            //計算時間            if (heart.getT() < 1) {                heart.setT(heart.getT() + heart.getSpeed());                //計算下次畫的時候,x,y座標                handleBezierXY(heart);            } else {                removeHeart(heart);            }        }    }    /**     * 計算實時的點座標     *     * @param heart     */    private void handleBezierXY(Heart heart) {        float x = (float) (Math.pow((1 - heart.getT()),                 3) * heart.getStartX() +                 3 * heart.getT() * Math.pow((1 -             heart.getT()), 2) * heart.getControl1X() +                 3 * Math.pow(heart.getT(), 2)       * (1 - heart.getT()) * heart.getControl2X() +                 Math.pow(heart.getT(), 3) *                 heart.getEndX());        float y = (float) (Math.pow((1 - heart.getT()),         3) * heart.getStartY() +   3 * heart.getT() * Math.pow((1 -        heart.getT()), 2) * heart.getControl1Y() +                 3 * Math.pow(heart.getT(), 2) * (1 - heart.getT()) * heart.getControl2Y() +                 Math.pow(heart.getT(), 3) *        heart.getEndY());        heart.setX(x);        heart.setY(y);    }

畫完了,然咱們寫在 demo 裏欣賞一下效果吧,使用代碼以下:

//xml<com.zyyoona7.heartlib.HeartView    android:id="@+id/heart_view"    android:layout_width="250dp"    android:layout_height="250dp"    android:layout_alignParentRight="true"    android:layout_alignParentBottom="true"    android:layout_marginBottom="40dp"/>//javamHeartView = (HeartView) findViewById(R.id.heart_view);mHeartView.addHeart();


大功告成,效果圖就回到頂部查看吧~須要查看完整代碼請點擊 Github 地址:HeartView

相關文章
相關標籤/搜索