Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 使用surfaceview實現直播中的點贊效果

使用surfaceview實現直播中的點贊效果

編輯:關於Android編程

直播功能現在已經是一個很熱門的功能了,很多應用都會涉及到直播模塊,比如 花椒 NOW 還有辣媽幫。。等,最近因為項目需要也加入了直播功能。直播中有一個點贊的效果 ,今天我也按照自己的思路實現了一個這樣的點贊功能,效果如下:

這裡寫圖片描述

簡單描述一下效果
1.產生一顆心
2.由下至上的曲線運動
3.開頭有一段放大效果
4.透明度漸變效果<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPsq1z9bS1MnP0Ke5+8nmvLC1vbXE1qrKtrXjPGJyIC8+DQrH+s/fuey8oyCjusr00NS2r7uto6uxtMj8tvvH+s/fy+O3qDxiciAvPg0K08nT2rXj1N7Qp7n71NrWsbKl1tC1xLPW0PjKsbzkysexyL3Ps6S1xDxiciAvPg0Ky/nS1NXiwO/KudPDtcTKx3N1cmZhY2V2aWV3IL/J0tTU2rmk1/fP37PM1tC75tbGdWk8YnIgLz4NCnVpu+bWxqO6c3VyZmFjZXZpZXc8L3A+DQo8cD64w9Cnufu1xLnsvKPUrdDNzbw8YnIgLz4NCjxpbWcgYWx0PQ=="這裡寫圖片描述" src="/uploadfile/Collfiles/20160905/20160905095858441.png" title="\" />

軌跡坐標點是通過屬性動畫 TypeEvaluator 生成的

 private class BezierEvaluator implements TypeEvaluator {

        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);
        }
    }

接下來分享一下代碼的實現思路
由兩個類構成的 Zanbean ZanView

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()), new Point((random.nextInt(zanView.getWidth())), 0));
    }
    public ZanBean(Context context,Bitmap bitmap,ZanView zanView) {
        random=new Random();
        this.bitmap= bitmap;
        init(new Point(zanView.getWidth() / 2, zanView.getHeight()), 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(2500);
        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 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 {

        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);
        }
    }
}

Zanbean
用來記錄心的軌跡 和 繪制的工作

這裡簡單介紹一下init方法

 @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(2500);
        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();
    }

在創建對象的時候 屬性動畫會直接啟動。更符合場景,相當每個心丟進畫面就會自動跑起來,每個心都是獨立的個體

然後是ZanView 畫面繪制和呈現的主體

public class ZanView extends SurfaceView implements SurfaceHolder.Callback {

    private SurfaceHolder surfaceHolder;

    /**心的個數*/
    private ArrayList 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();
             }

             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();}
    }
}

以上是ZanView的所有代碼 重要的地方都做了注釋 還是比較好懂的吧

為了驗證貝賽爾曲線
只要將這句話注釋就能看到每一幀的軌跡

          /**清除畫面*/
                        canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);

這裡寫圖片描述

以上就是關於點贊 心動的效果 有什麼問題歡迎指出

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved