ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

基于 SurfaceView 的直播点亮心形效果

2021-05-21 10:51:31  阅读:224  来源: 互联网

标签:heart canvas 点亮 心形 void float private SurfaceView public



本文来自zyyoona7,zyyoona7的blog链接为:http://www.jianshu.com/p/6d2cc30e4687。本文主要是直播界面中点赞效果,当然也可以用OpenGL去做。


zyyonna7简介:本人是一名Android开发者,从大学开始接触Android,现在工作刚好满两年。在这两年中自己觉得成长了很多很多,过去的两年中做了很多公司项目,每个项目中都充当比较重要的角色。坚持用blog在不断提升自己。


先来展示下效果图:


图片


大家看到效果应该都不陌生,网上已经有很多相同的效果,但是网上大多是通过动画来实现,而我这个是通过自定义 SurfaceView 来实现。这个想法主要来自于反编译映客 App,虽然看不到源码,但给我提供了思路。接下来进入正题~

1. 自定义 SurfaceView 巩固

自定义 SurfaceView 需要三点:继承 SurfaceView、实现SurfaceHolder.Callback、提供渲染线程。

继承 SurfaceView不需要多说,说一下 SurfaceHolder.Callback 需要实现的三个方法:

  • public void surfaceCreated(SurfaceHolder holder) : 当 Surface 第一次创建后会立即调用该函数。程序可以在该函数中做些和绘制界面相关的初始化工作,一般情况下都是在另外的线程来绘制界面,所以不要在这个函数中绘制 Surface。

  • public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) : 当 Surface 的状态(大小和格式)发生变化的时候会调用该函数,在 surfaceCreated() 调用后该函数至少会被调用一次。

  • public void surfaceDestroyed(SurfaceHolder holder) : 当 Surface 被销毁前会调用该函数,该函数被调用后就不能继续使用 Surface 了,一般在该函数中来清理使用的资源。

下面提供一个自定义 SurfaceView 的一个简单模板:

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

首先说下三阶贝塞尔曲线的几个主要参数:起始点、结束点、控制点1、控制点2、时间(从 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





标签:heart,canvas,点亮,心形,void,float,private,SurfaceView,public
来源: https://blog.51cto.com/u_15127656/2800480

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有