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

转载请注明出处 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);

这里写图片描述

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我会给您讲解使用 SurfaceTexture 和 SurfaceView 实现相机预览的方法。 首先,在布局文件添加一个 SurfaceView: ```xml <SurfaceView android:id="@+id/surfaceView" android:layout_width="match_parent" android:layout_height="match_parent" /> ``` 然后在 Activity 或 Fragment 获取 SurfaceView,并设置相机预览: ```java // 获取 SurfaceView SurfaceView surfaceView = findViewById(R.id.surfaceView); // 获取相机实例 Camera camera = Camera.open(); try { // 设置相机预览显示的 Surface camera.setPreviewDisplay(surfaceView.getHolder()); // 开始预览 camera.startPreview(); } catch (IOException e) { e.printStackTrace(); } ``` 接下来,我们来使用 SurfaceTexture 来实现相机预览。 首先,在布局文件添加一个 TextureView: ```xml <TextureView android:id="@+id/textureView" android:layout_width="match_parent" android:layout_height="match_parent" /> ``` 然后在 Activity 或 Fragment 获取 TextureView,并设置相机预览: ```java // 获取 TextureView TextureView textureView = findViewById(R.id.textureView); // 获取相机实例 Camera camera = Camera.open(); try { // 创建 SurfaceTexture SurfaceTexture surfaceTexture = new SurfaceTexture(0); // 设置相机预览显示的 Surface camera.setPreviewTexture(surfaceTexture); // 开始预览 camera.startPreview(); } catch (IOException e) { e.printStackTrace(); } ``` 使用 SurfaceTexture 来实现相机预览的好处在于,可以通过 TextureView 的 setSurfaceTextureListener() 方法来监听 SurfaceTexture 的状态,例如当 SurfaceTexture 准备好后,可以获取它的宽高等信息,从而进行一些额外的操作。 希望这些代码可以帮助到您。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值