Quantcast
Channel: CSDN博客移动开发推荐文章
Viewing all articles
Browse latest Browse all 5930

使用surfaceview实现直播中的点赞效果

$
0
0

转载请注明出处 http://blog.csdn.net/u011453163/article/details/52424328

直播功能现在已经是一个很热门的功能了,很多应用都会涉及到直播模块,比如 花椒 NOW 还有辣妈帮。。等,最近因为项目需要也加入了直播功能。直播中有一个点赞的效果 ,今天我也按照自己的思路实现了一个这样的点赞功能,效果如下:
这里写图片描述

简单描述一下效果
1.产生一颗心
2.由下至上的曲线运动
3.开头有一段放大效果
4.透明度渐变效果

实现以上效果涉及到的知识点
曲线轨迹 :属性动画+贝赛尔曲线算法
由于点赞效果在直播中的持续时间是比较长的
所以这里使用的是surfaceview 可以在工作线程中绘制ui
ui绘制:surfaceview

该效果的轨迹原型图
这里写图片描述

轨迹坐标点是通过属性动画 TypeEvaluator 生成的

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

接下来分享一下代码的实现思路
由两个类构成的 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<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);
        }
    }
}

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

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

这里写图片描述

以上就是关于点赞 心动的效果 有什么问题欢迎指出

作者:u011453163 发表于2016/9/3 17:17:27 原文链接
阅读:82 评论:0 查看评论

Viewing all articles
Browse latest Browse all 5930

Trending Articles