Android surfaceview的使用方式

1. 什么是surfaceview

surfaceview内部机制和外部层次结构

在安卓开发中,我们经常会遇到一些需要高性能、高帧率、高画质的应用场景,例如视频播放、游戏开发、相机预览等。这些场景中,我们需要直接操作图像数据,并且实时地显示到屏幕上。如果我们使用普通的view组件来实现这些功能,可能会遇到以下问题:

  • view组件是在主线程中进行绘制的,如果绘制过程耗时过长或者频繁刷新,可能会导致主线程阻塞,影响用户交互和界面响应。
  • view组件在绘制时没有使用双缓冲机制,也就是说每次绘制都是直接在屏幕上进行的,这可能会导致绘制过程中出现闪烁或者撕裂的现象。
  • view组件是基于view层次结构的,也就是说每个view都是一个矩形区域,如果我们想要实现一些不规则形状或者透明度变化的效果,可能会比较困难。

为了解决这些问题,安卓提供了一种特殊的view组件:surfaceview 。surfaceview拥有自己独立的surface,也就是一个可以在其上直接绘制内容的图形缓冲区。surfaceview的内容是透明的,可以嵌入到view层次结构中,并且可以和其他view进行重叠或者裁剪。surfaceview适用于需要频繁刷新或处理逻辑复杂的绘图场景,如视频播放、游戏等。

下图展示了surfaceview和普通view在屏幕上的显示效果:

surfaceview和普通view

从图中可以看出,普通view是按照顺序依次绘制到屏幕上的,而surfaceview则是直接绘制到屏幕上的一个透明区域,并且可以和其他view进行重叠或者裁剪。

2. surfaceview和view的区别

从上面的介绍中,我们已经了解了surfaceview和普通view在显示效果上的区别。那么,在实现原理和使用方式上,它们又有什么不同呢?下面我们来对比一下它们的主要区别:

特点 普通view surfaceview
更新方式 主动更新,可以在任何时候调用invalidate方法来触发重绘,在onDraw方法中使用canvas进行绘制 被动更新,不能直接控制重绘,需要通过一个子线程来进行页面的刷新,在子线程中直接操作surface进行绘制
刷新线程 主线程刷新,可以保证界面的一致性和同步性,但是可能导致主线程阻塞或者掉帧 子线程刷新,可以避免主线程阻塞,并且可以提高刷新频率和效率,但是需要注意线程间的通信和同步问题
缓冲机制 无双缓冲机制,每次绘制都是直接在屏幕上进行,可以节省内存空间,但是可能导致闪烁或者撕裂的现象 有双缓冲机制,每次绘制都是先在一个缓冲区中进行,然后再将缓冲区中的内容复制到屏幕上,可以避免闪烁或者撕裂的现象,并且可以提高绘制质量,但是需要消耗更多的内存空间
  • 更新方式:普通view适用于主动更新的情况,也就是说我们可以在任何时候调用view的invalidate方法来触发view的重绘,然后在onDraw方法中使用canvas进行绘制。而surfaceview主要用于被动更新的情况,也就是说我们不能直接控制surfaceview的重绘,而是需要通过一个子线程来进行页面的刷新,然后在子线程中直接操作surface进行绘制。
  • 刷新线程:普通view是在主线程里面进行刷新的,也就是说所有的绘制操作都是在主线程中完成的。这样的好处是可以保证界面的一致性和同步性,但是也有可能导致主线程阻塞或者掉帧。而surfaceview是通过一个子线程来进行页面的刷新的,也就是说所有的绘制操作都是在子线程中完成的。这样的好处是可以避免主线程阻塞,并且可以提高刷新频率和效率,但是也需要注意线程间的通信和同步问题。
  • 缓冲机制:普通view在绘图时没有使用双缓冲机制,也就是说每次绘制都是直接在屏幕上进行的。这样的好处是可以节省内存空间,但是也可能导致绘制过程中出现闪烁或者撕裂的现象。而surfaceview在底层实现机制中已经实现了双缓冲机制,也就是说每次绘制都是先在一个缓冲区中进行,然后再将缓冲区中的内容复制到屏幕上。这样的好处是可以避免闪烁或者撕裂的现象,并且可以提高绘制质量,但是也需要消耗更多的内存空间。

3. surfaceview的创建和使用

了解了surfaceview和普通view的区别之后,我们就可以开始创建和使用surfaceview了。创建自定义的surfaceview需要以下几个步骤:

  • 继承surfaceview:首先,我们需要创建一个自定义的类,继承自surfaceview,并实现两个接口:surfaceholder.callback和runnable。前者用于监听surface的状态变化,后者用于实现子线程的逻辑。
  • 初始化surfaceholder:其次,我们需要在构造方法中初始化surfaceholder对象,并注册surfaceholder的回调方法。surfaceholder是一个用于管理surface的类,它提供了一些方法来获取和操作surface。
  • 处理回调方法:然后,我们需要在回调方法中处理surface的创建、改变和销毁事件。当surface被创建时,我们需要启动子线程,并根据需要调整view的大小或位置;当surface被改变时,我们需要重新获取surface的宽高,并根据需要调整view的大小或位置;当surface被销毁时,我们需要停止子线程,并释放相关资源。
  • 实现run方法:接着,我们需要在run方法中实现子线程的绘图逻辑。我们可以使用一个循环来不断地刷新页面,并且根据不同的条件来控制循环的退出。
  • 获取canvas对象:最后,我们需要在draw方法中获取canvas对象,并通过lockcanvas和unlockcanvasandpost方法进行绘图操作。lockcanvas方法会返回一个canvas对象,我们可以使用它来对surface进行绘制;unlockcanvasandpost方法会将绘制好的内容显示到屏幕上,并且释放canvas对象。

下面给出一个简单的示例代码,实现了一个简单的画板功能:

java 复制代码
//自定义类继承自SurfaceView,并实现SurfaceHolder.Callback和Runnable接口
public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback, Runnable {

    //声明SurfaceHolder对象
    private SurfaceHolder mHolder;
    //声明子线程对象
    private Thread mThread;
    //声明画笔对象
    private Paint mPaint;
    //声明画布对象
    private Canvas mCanvas;
    //声明一个标志位,用于控制子线程的退出
    private boolean mIsDrawing;

    //构造方法,初始化相关对象
    public MySurfaceView(Context context) {
        super(context);
        //获取SurfaceHolder对象
        mHolder = getHolder();
        //注册SurfaceHolder的回调方法
        mHolder.addCallback(this);
        //初始化画笔对象,设置颜色和宽度
        mPaint = new Paint();
        mPaint.setColor(Color.RED);
        mPaint.setStrokeWidth(10);
    }

    //当Surface被创建时,启动子线程,并根据需要调整View的大小或位置
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        //设置标志位为true,表示子线程可以开始运行
        mIsDrawing = true;
        //创建并启动子线程
        mThread = new Thread(this);
        mThread.start();
    }

    //当Surface被改变时,重新获取Surface的宽高,并根据需要调整View的大小或位置
    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        //TODO: 根据需要调整View的大小或位置
    }

    //当Surface被销毁时,停止子线程,并释放相关资源
    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        //设置标志位为false,表示子线程可以停止运行
        mIsDrawing = false;
        try {
            //等待子线程结束,并释放子线程对象
            mThread.join();
            mThread = null;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    //实现run方法,实现子线程的绘图逻辑
    @Override
    public void run() {
        //使用一个循环来不断地刷新页面
        while (mIsDrawing) {
            //获取当前时间,用于计算绘制时间
            long start = System.currentTimeMillis();
            //调用draw方法进行绘制操作
            draw();
            //获取结束时间,用于计算绘制时间
            long end = System.currentTimeMillis();
            //如果绘制时间小于16ms,则延时一段时间,保证每秒60帧的刷新率
            if (end - start < 16) {
                try {
                    Thread.sleep(16 - (end - start));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    //获取canvas对象,并通过lockCanvas和unlockCanvasAndPost方法进行绘制操作
    private void draw() {
        try {
            //通过lockCanvas方法获取canvas对象,如果surface不可用,则返回null
            mCanvas = mHolder.lockCanvas();
            if (mCanvas != null) {
                //TODO: 在canvas上进行绘制操作,例如画线、画圆、画文字等

                //在本例中,我们简单地使用随机数生成一些坐标点,并用画笔连接它们,形成一条折线图

                //生成一个随机数对象
                Random random = new Random();
                //生成一个点的集合,用于存储坐标点
                List<Point> points = new ArrayList<>();
                //循环生成10个随机坐标点,并添加到集合中
                for (int i = 0; i < 10; i++) {
                    int x = random.nextInt(mCanvas.getWidth());
                    int y = random.nextInt(mCanvas.getHeight());
                    points.add(new Point(x, y));
                }
                //遍历点的集合,用画笔连接相邻的两个点,形成一条折线图
                for (int i = 0; i < points.size() - 1; i++) {
                    Point p1 = points.get(i);
                    Point p2 = points.get(i + 1);
                    mCanvas.drawLine(p1.x, p1.y, p2.x, p2.y, mPaint);
                }

                //通过unlockCanvasAndPost方法将绘制好的内容显示到屏幕上,并释放canvas对象
                mHolder.unlockCanvasAndPost(mCanvas);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

下图展示了上述代码运行的效果:

简单的画板

从图中可以看出,我们在surface上绘制了一条随机的折线图,并且显示到了屏幕上。这只是一个简单的示例,我们可以根据自己的需求,实现更复杂的绘图逻辑和效果。

4. surfaceview和activity的生命周期

在使用surfaceview时,我们需要注意它和activity的生命周期之间的关系。因为surfaceview是嵌入到view层次结构中的,所以它会受到activity的生命周期的影响。但是,surfaceview也有自己的生命周期,它是由surfaceholder来管理的。因此,对于具有surfaceview的activity,存在两个单独但相互依赖的状态机:应用oncreate/onresume/onpause和已创建/更改/销毁的surface。

下图展示了这两个状态机之间的关系:

surfaceview和activity的状态机之间的关系

surfaceview和activity的生命周期

从图中可以看出,当activity被创建时,会触发surfaceview的创建;当activity被恢复时,会触发surfaceview的改变;当activity被暂停时,会触发surfaceview的销毁。因此,在这些事件中,我们需要做一些相应的处理,例如:

  • 启动/停止子线程:当surface被创建或者销毁时,我们需要启动或者停止子线程,并根据需要调整view的大小或位置。如果我们不及时地启动或者停止子线程,可能会导致内存泄漏或者空指针异常。
  • 保存/恢复状态:当activity被暂停时,我们需要从子线程中提取状态,并保存到bundle中;当activity被恢复时,我们需要从bundle中恢复状态,并传递给子线程。如果我们不及时地保存或者恢复状态,可能会导致数据丢失或者不一致。

5. surfaceview和glsurfaceview

在上面的内容中,我们介绍了如何使用surfaceview来实现一些高性能、高帧率、高画质的应用。但是,如果我们想要实现一些更加复杂和精美的3D图形效果,例如光照、阴影、纹理、动画等,那么我们就需要使用opengl es来进行渲染。opengl es是一种跨平台的图形库,它可以利用gpu加速来提高渲染效率。

为了方便我们使用opengl es进行渲染,安卓提供了一种专门用于渲染opengl es内容的surfaceview:glsurfaceview 。glsurfaceview是一种继承自surfaceview的组件,它在底层封装了egl上下文、线程间通信以及与activity生命周期交互等功能。使用glsurfaceview时,我们无需自己创建和管理子线程,只需实现glsurfaceview.renderer接口,并设置给glsurfaceview对象即可。

下图展示了glsurfaceview和普通surfaceview在屏幕上的显示效果:

glsurfaceview和普通surfaceview

从图中可以看出,glsurfaceview和普通surfaceview都是直接绘制到屏幕上的一个透明区域,但是glsurfaceview可以使用opengl es来绘制一些更加复杂和精美的3D图形效果。

6. glsurfaceview的创建和使用

了解了glsurfaceview和普通surfaceview的区别之后,我们就可以开始创建和使用glsurfaceview了。创建自定义的glsurfaceview需要以下几个步骤:

  • 继承glsurfaceview:首先,我们需要创建一个自定义的类,继承自glsurfaceview,并在构造方法中初始化相关对象。
  • 设置渲染器:其次,我们需要实现glsurfaceview.renderer接口,并设置给glsurfaceview对象。渲染器是一个用于绘制opengl es内容的类,它提供了三个方法:onSurfaceCreated、onSurfaceChanged和onDrawFrame。
  • 设置渲染模式:然后,我们需要设置glsurfaceview的渲染模式,有两种可选:RENDERMODE_CONTINUOUSLY和RENDERMODE_WHEN_DIRTY。前者表示持续地刷新页面,后者表示只有在调用requestRender方法时才刷新页面。
  • 获取opengl es对象:最后,我们需要在渲染器的方法中获取opengl es对象,并使用它来进行绘制操作。opengl es对象是一个用于操作图形数据的类,它提供了一系列的方法来创建、加载、绘制、变换、释放等图形资源。

下面给出一个简单的示例代码,实现了一个简单的3D立方体效果:

java 复制代码
//自定义类继承自GLSurfaceView,并在构造方法中初始化相关对象
public class MyGLSurfaceView extends GLSurfaceView {

    //声明渲染器对象
    private MyRenderer mRenderer;

    //构造方法,初始化相关对象
    public MyGLSurfaceView(Context context) {
        super(context);
        //设置opengl es版本为2.0
        setEGLContextClientVersion(2);
        //创建并设置渲染器对象
        mRenderer = new MyRenderer();
        setRenderer(mRenderer);
        //设置渲染模式为持续刷新
        setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
    }

    //自定义类实现GLSurfaceView.Renderer接口,并实现三个方法
    private class MyRenderer implements GLSurfaceView.Renderer {

        //声明opengl es对象
        private GLES20 gl;

        //声明顶点着色器代码
        private final String vertexShaderCode =
                "attribute vec4 vPosition;" +
                "uniform mat4 uMVPMatrix;" +
                "void main() {" +
                "  gl_Position = uMVPMatrix * vPosition;" +
                "}";

        //声明片元着色器代码
        private final String fragmentShaderCode =
                "precision mediump float;" +
                "uniform vec4 vColor;" +
                "void main() {" +
                "  gl_FragColor = vColor;" +
                "}";

        //声明顶点坐标数组
        private final float[] vertexCoords = {
                -0.5f, -0.5f, -0.5f,   // front bottom left
                0.5f, -0.5f, -0.5f,   // front bottom right
                0.5f,  0.5f, -0.5f,   // front top right
                -0.5f,  0.5f, -0.5f,  // front top left
                -0.5f, -0.5f,  0.5f,   // back bottom left
                0.5f, -0.5f,  0.5f,   // back bottom right
                0.5f,  0.5f,  0.5f,   // back top right
                -0.5f,  0.5f,  0.5f   // back top left
        };

        //声明顶点索引数组
        private final short[] drawOrder = {
                0, 1, 2,   // front face
                0, 2, 3,
                4, 5, 6,   // back face
                4, 6, 7,
                0, 4, 7,   // left face
                0, 7, 3,
                1, 5, 6,   // right face
                1, 6, 2,
                3, 2, 6,   // top face
                3, 6, 7,
                0, 1, 5,   // bottom face
                0, 5, 4
        };

        //声明颜色数组
        private final float[] colors = {
                1.0f, 0.0f, 0.0f, 1.0f, // red
                0.0f, 1.0f, 0.0f, 1.0f, // green
                0.0f, 0.0f, 1.0f, 1.0f, // blue
                1.0f, 1.0f, 0.0f, 1.0f, // yellow
                1.0f, 0.0f, 1.0f, 1.0f, // magenta
                0.0f, 1.0f, 1.0f, 1.0f // cyan
        };

        //声明顶点缓冲对象
        private FloatBuffer vertexBuffer;
        //声明索引缓冲对象
        private ShortBuffer drawListBuffer;
        //声明颜色缓冲对象
        private FloatBuffer colorBuffer;

        //声明顶点着色器对象
        private int vertexShader;
        //声明片元着色器对象
        private int fragmentShader;
        //声明程序对象
        private int program;

        //声明顶点位置属性的句柄
        private int positionHandle;
        //声明颜色属性的句柄
        private int colorHandle;
        //声明投影矩阵属性的句柄
        private int mvpMatrixHandle;

        //声明模型矩阵对象
        private float[] modelMatrix = new float[16];
        //声明视图矩阵对象
        private float[] viewMatrix = new float[16];
        //声明投影矩阵对象
        private float[] projectionMatrix = new float[16];
        //声明模型视图投影矩阵对象
        private float[] mvpMatrix = new float[16];

        //当Surface被创建时,初始化opengl es对象,并加载和编译着色器,创建和绑定图形数据,设置相机位置和投影方式等操作
        @Override
        public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
            //获取opengl es对象,用于后续的绘制操作
            gl = (GLES20) gl10;

            //设置背景颜色为黑色,用于清除屏幕时使用
            gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

            //加载和编译顶点着色器,返回一个句柄,用于后续的链接操作
            vertexShader = loadShader(GLES20.GL_VERTEX_SHADER,
                    vertexShaderCode);

            //加载和编译片元着色器,返回一个句柄,用于后续的链接操作
            fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER,
fragmentShaderCode);

            //创建一个空的程序对象,返回一个句柄,用于后续的链接操作
            program = GLES20.glCreateProgram();

            //将顶点着色器和片元着色器附加到程序对象上
            GLES20.glAttachShader(program, vertexShader);
            GLES20.glAttachShader(program, fragmentShader);

            //链接程序对象,生成最终的可执行程序
            GLES20.glLinkProgram(program);

            //使用程序对象,激活相关的属性和统一变量
            GLES20.glUseProgram(program);

            //获取顶点位置属性的句柄,用于后续的绑定操作
            positionHandle = GLES20.glGetAttribLocation(program, "vPosition");

            //获取颜色属性的句柄,用于后续的绑定操作
            colorHandle = GLES20.glGetUniformLocation(program, "vColor");

            //获取投影矩阵属性的句柄,用于后续的绑定操作
            mvpMatrixHandle = GLES20.glGetUniformLocation(program, "uMVPMatrix");

            //将顶点坐标数组转换为字节缓冲对象,用于后续的传输操作
            ByteBuffer bb = ByteBuffer.allocateDirect(
                    vertexCoords.length * 4);
            bb.order(ByteOrder.nativeOrder());
            vertexBuffer = bb.asFloatBuffer();
            vertexBuffer.put(vertexCoords);
            vertexBuffer.position(0);

            //将顶点索引数组转换为字节缓冲对象,用于后续的传输操作
            ByteBuffer dlb = ByteBuffer.allocateDirect(
                    drawOrder.length * 2);
            dlb.order(ByteOrder.nativeOrder());
            drawListBuffer = dlb.asShortBuffer();
            drawListBuffer.put(drawOrder);
            drawListBuffer.position(0);

            //将颜色数组转换为字节缓冲对象,用于后续的传输操作
            ByteBuffer cb = ByteBuffer.allocateDirect(
                    colors.length * 4);
            cb.order(ByteOrder.nativeOrder());
            colorBuffer = cb.asFloatBuffer();
            colorBuffer.put(colors);
            colorBuffer.position(0);

            //设置相机位置和朝向,生成视图矩阵
            Matrix.setLookAtM(viewMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);

        }

        //当Surface被改变时,调整视口大小,并设置投影方式,生成投影矩阵
        @Override
        public void onSurfaceChanged(GL10 gl10, int width, int height) {
            //设置视口大小为Surface的大小
            GLES20.glViewport(0, 0, width, height);

            //设置投影方式为透视投影,并根据视口宽高比计算投影矩阵
            float ratio = (float) width / height;
            Matrix.frustumM(projectionMatrix, 0, -ratio, ratio, -1, 1, 3, 7);

        }

        //当Surface被绘制时,清除屏幕,并旋转模型矩阵,生成模型视图投影矩阵,并传输和绘制图形数据
        @Override
        public void onDrawFrame(GL10 gl10) {
            //清除屏幕颜色缓冲区
            GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);

            //设置模型矩阵为单位矩阵,并根据系统时间旋转模型矩阵
            Matrix.setIdentityM(modelMatrix, 0);
            Matrix.rotateM(modelMatrix, 0, (float) SystemClock.uptimeMillis() / 1000 * 30f, 1.0f, 1.0f, 1.0f);

            //将模型矩阵、视图矩阵和投影矩阵相乘,生成模型视图投影矩阵
            Matrix.multiplyMM(mvpMatrix, 0, viewMatrix, 0, modelMatrix, 0);
            Matrix.multiplyMM(mvpMatrix, 0, projectionMatrix, 0, mvpMatrix, 0);

            //将模型视图投影矩阵传输到顶点着色器中,并激活该属性
            GLES20.glUniformMatrix4fv(mvpMatrixHandle, 1, false, mvpMatrix, 0);

            //将顶点坐标数据传输到顶点着色器中,并激活该属性
            GLES20.glVertexAttribPointer(positionHandle, 3,
                    GLES20.GL_FLOAT, false,
                    0, vertexBuffer);
            GLES20.glEnableVertexAttribArray(positionHandle);

            //使用循环为每个面设置不同的颜色,并绘制三角形
            for (int i = 0; i < 6; i++) {
                //将颜色数据传输到片元着色器中,并激活该属性
                colorBuffer.position(i * 4);
                GLES20.glUniform4fv(colorHandle, 1, colorBuffer);
                //绘制三角形,使用顶点索引数组来确定顶点的顺序
                drawListBuffer.position(i * 6);
                GLES20.glDrawElements(
                        GLES20.GL_TRIANGLES, 6,
                        GLES20.GL_UNSIGNED_SHORT, drawListBuffer);
            }

        }

        //定义一个加载和编译着色器的方法,接收一个着色器类型和一个着色器代码,返回一个着色器句柄
        public int loadShader(int type, String shaderCode) {

            //创建一个空的着色器对象,返回一个句柄,用于后续的编译操作
            int shader = GLES20.glCreateShader(type);

            //将着色器代码传输到着色器对象中
            GLES20.glShaderSource(shader, shaderCode);

            //编译着色器对象
            GLES20.glCompileShader(shader);

            //返回着色器句柄
            return shader;
        }
    }
}

glsurfaceview的使用方式

什么是glsurfaceview

glsurfaceview是一种专门用于渲染opengl es内容的surfaceview。opengl es是一种用于嵌入式设备上的3D图形渲染API。glsurfaceview类提供了用于管理egl上下文、在线程间通信以及与activity生命周期交互的辅助程序类。使用glsurfaceview时,无需自己创建和管理子线程,只需实现glsurfaceview.renderer接口,并设置给glsurfaceview对象即可。

glsurfaceview和surfaceview的区别

glsurfaceview和surfaceview都是继承自surfaceview的类,都可以在子线程中直接操作surface进行绘制。但是glsurfaceview相比surfaceview有以下的优势:

  • glsurfaceview可以自动创建和管理egl上下文,无需自己处理egl的初始化、销毁、切换等操作。
  • glsurfaceview可以自动创建和管理子线程,无需自己处理线程的启动、停止、同步等操作。
  • glsurfaceview可以自动处理与activity生命周期的交互,无需自己处理activity的暂停、恢复、保存状态等操作。
  • glsurfaceview可以提供多种渲染模式,可以根据需要调整渲染频率,避免过度绘制或掉帧。

glsurfaceview的创建和使用

创建自定义的glsurfaceview继承glsurfaceview,并在构造方法中设置opengl es版本、渲染器对象和渲染模式。创建自定义的渲染器实现glsurfaceview.renderer接口,并在回调方法中进行初始化、视口设置和绘图操作。

以下是一个简单的示例代码:

java 复制代码
// 自定义的glsurfaceview类
public class MyGLSurfaceView extends GLSurfaceView {

    // 构造方法
    public MyGLSurfaceView(Context context) {
        super(context);
        // 设置opengl es版本为2.0
        setEGLContextClientVersion(2);
        // 设置渲染器对象
        setRenderer(new MyRenderer());
        // 设置渲染模式为连续模式
        setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
    }

    // 自定义的渲染器类
    private class MyRenderer implements GLSurfaceView.Renderer {

        // 渲染器创建时的回调方法
        @Override
        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
            // 在这里进行一些初始化操作,比如设置清屏颜色为黑色
            GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        }

        // 渲染器改变时的回调方法
        @Override
        public void onSurfaceChanged(GL10 gl, int width, int height) {
            // 在这里进行一些视口设置操作,比如设置视口大小为surface的大小
            GLES20.glViewport(0, 0, width, height);
        }

        // 渲染器绘制时的回调方法
        @Override
        public void onDrawFrame(GL10 gl) {
            // 在这里进行一些绘图操作,比如清屏
            GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
        }
    }
}

glsurfaceview和activity的生命周期

当使用glsurfaceview时,无需自己处理与activity生命周期的交互,glsurfaceview会自动根据activity的状态来暂停或恢复渲染器。但是如果需要保存或恢复一些重要的数据或状态,可以在activity的onSaveInstanceState和onRestoreInstanceState方法中进行操作。

以下是一个示意图,展示了glsurfaceview和activity的生命周期之间的关系:

glsurfaceview和activity的生命周期

从图中可以看出,当activity创建时,会触发glsurfaceview的onSurfaceCreated回调方法,这时会创建渲染器对象,并调用渲染器的onSurfaceCreated回调方法。当activity恢复时,会触发glsurfaceview的onDrawFrame回调方法,这时会恢复渲染器的绘制操作,并调用渲染器的onDrawFrame回调方法。当activity暂停时,会触发glsurfaceview的onDrawFrame回调方法,这时会暂停渲染器的绘制操作,并调用渲染器的onDrawFrame回调方法。当activity销毁时,会触发glsurfaceview的onSurfaceCreated回调方法,这时会销毁渲染器对象,并调用渲染器的onSurfaceCreated回调方法。

在这个过程中,需要注意以下几点:

  • 在activity的onSaveInstanceState和onRestoreInstanceState方法中,可以保存或恢复一些重要的数据或状态,比如使用一个bundle对象来存储或获取一些opengl es相关的对象或参数。

  • 在glsurfaceview的onSurfaceChanged回调方法中,可以根据surface的宽高调整视口大小或投影方式,比如使用glviewport或glfrustum等方法来设置视口或投影矩阵。

  • 在glsurfaceview的setRenderMode方法中,可以设置不同的渲染模式,比如使用RENDERMODE_CONTINUOUSLY或RENDERMODE_WHEN_DIRTY来设置连续模式或按需模式。

👀关注公众号:Android老皮!!!欢迎大家来找我探讨交流👀

相关推荐
openinstall全渠道统计13 分钟前
免填邀请码工具:赋能六大核心场景,重构App增长新模型
android·ios·harmonyos
双鱼大猫35 分钟前
一句话说透Android里面的ServiceManager的注册服务
android
双鱼大猫1 小时前
一句话说透Android里面的View的绘制流程和实现原理
android
双鱼大猫2 小时前
一句话说透Android里面的Window的内部机制
android
双鱼大猫2 小时前
一句话说透Android里面的为什么要设计Window?
android
双鱼大猫2 小时前
一句话说透Android里面的主线程创建时机,frameworks层面分析
android
苏金标2 小时前
android 快速定位当前页面
android
雾里看山6 小时前
【MySQL】内置函数
android·数据库·mysql
风浅月明6 小时前
[Android]页面间传递model列表
android
法迪6 小时前
Android自带的省电模式主要做什么呢?
android·功耗