OpenGL ES之GLSurfaceView使用详解

一.GLSurfaceView概述

GLSurfaceView从Android 1.5(API level 3)开始加入,继承自SurfaceView,实现了SurfaceHolder.Callback2接口,拥有SurfaceView的全部特性,也有view所有的功能和属性,特别是处理事件的能力,它主要是在SurfaceView的基础上它加入了EGL的管理,并自带了一个GLThread绘制线程(EGLContext创建GL环境所在线程即为GL线程),绘制的工作直接通过OpenGL在绘制线程进行,不会阻塞主线程,绘制的结果输出到SurfaceView所提供的Surface上,这使得GLSurfaceView也拥有了OpenGlES所提供的图形处理能力,通过它定义的Render接口,使更改具体的Render的行为非常灵活性,只需要将实现了渲染函数的Renderer的实现类设置给GLSurfaceView即可。

 GLSurfaceView的特性:

1.管理一个surface,这个surface就是一块特殊的内存,能直接排版到android的视图view上。

2.管理一个EGL display,它能让opengl把内容渲染到上述的surface上。

3.用户自定义渲染器(render)。

4.让渲染器在独立的线程里运作,和UI线程分离。

5.支持按需渲染(on-demand)和连续渲染(continuous)。

6.可以封装、跟踪并且排查渲染器的问题。

 

二.GLSurfaceView的基本使用

1.创建GLSurfaceView

GLSurfaceView一般有两种创建方式:(1)一种是在android布局文件中配置;(2)直接在代码中new出来;

以下是第一种方式:xml文件中配置:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">


    <android.opengl.GLSurfaceView
        android:id="@+id/gl_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>


</RelativeLayout>

在上面的代码中,因为GLSurfaceView本身就是一个View控件,所以是可以直接在xml中添加该控件,然后就可以直接在Activity或者fragment中通过id找到该控件并操作它;

以下是通过new的方法直接创建GLSurfaceView的实例:

package com.yh.opengltext01;

import android.app.Activity;
import android.app.ActivityManager;
import android.content.pm.ConfigurationInfo;
import android.opengl.GLSurfaceView;
import android.os.Build;
import android.os.Bundle;
import android.widget.Toast;

public class MainActivity extends Activity {

    private GLSurfaceView mGLSurfaceView;
    private boolean mRendererSet = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mGLSurfaceView = new GLSurfaceView(this);

        final ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);

        final ConfigurationInfo configurationInfo = activityManager.getDeviceConfigurationInfo();

        final boolean supportsEs2 = configurationInfo.reqGlEsVersion >= 0x20000
                ||(Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1)
                &&(Build.FINGERPRINT.startsWith("generic"))
                || Build.FINGERPRINT.startsWith("unknow")
                || Build.MODEL.contains("google_sdk")
                || Build.MODEL.contains("Emulator")
                || Build.MODEL.contains("Android SDK built for x86");

        if (supportsEs2){
            mGLSurfaceView.setEGLContextClientVersion(2);
            mGLSurfaceView.setRenderer(new SimpleRenderer(this));
            mRendererSet = true;
        }else {
            Toast.makeText(this , "This device does not support OpenGL ES 2.0." ,
                    Toast.LENGTH_SHORT).show();
            return;
        }

        setContentView(mGLSurfaceView);
    }

    @Override
    protected void onPause() {
        if (mRendererSet){
            mGLSurfaceView.onPause();
        }
        super.onPause();
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (mRendererSet){
            mGLSurfaceView.onResume();
        }
    }
}
  • Activity的onCreate方法中首先我们就创建GLSurfaceView的实例:
mGLSurfaceView = new GLSurfaceView(this);
  • 第二步我们判断了当前设备是否支持OpenGL ES2,不支持我们直接retrun,并做相应的提示,后面“||”是为了判断设备是不是模拟器的情况:
final boolean supportsEs2 = configurationInfo.reqGlEsVersion >= 0x20000
        ||(Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1)
        &&(Build.FINGERPRINT.startsWith("generic"))
        || Build.FINGERPRINT.startsWith("unknow")
        || Build.MODEL.contains("google_sdk")
        || Build.MODEL.contains("Emulator")
        || Build.MODEL.contains("Android SDK built for x86");
  • 第三步如果支持,我们为我们GLSurfaceView配置一些简单的属性,并为它设置渲染器(Render),渲染器我们之后会解释:
mGLSurfaceView.setEGLContextClientVersion(2);
//设置渲染器
mGLSurfaceView.setRenderer(new SimpleRenderer(this));
  • 第四步,把我们GLSurfaceView的生命周期和Activity的生命周期绑定:
@Override
protected void onPause() {
    if (mRendererSet){
        mGLSurfaceView.onPause();
    }
    super.onPause();
}

@Override
protected void onResume() {
    if (mRendererSet){
        mGLSurfaceView.onResume();
    }
    super.onResume();
}

 

以上第二步到最后一步在使用id获取GLSurfaceView也是需要这样做的; 

这样一个简单初始化GLSurfaceView的初始化就做好了;

三.GLSurfaceView的事件处理

为了处理事件,一般都是继承GLSurfaceView类并重载它的事件方法,但是由于GLSurfaceView是多线程的,渲染器在独立的渲染线程里,你应该使用Java的跨线程机制跟渲染器通讯,GLSurfaceView提供的queueEvent(Runnable)方法就是一种相对简单的操作,queueEvent()方法被安全地用于在UI线程和渲染线程之间进行交流通信:

该方法的原型:

/**
 * Queue a runnable to be run on the GL rendering thread. This can be used
 * to communicate with the Renderer on the rendering thread.
 * Must not be called before a renderer has been set.
 * @param r the runnable to be run on the GL rendering thread.
 */
public void queueEvent(Runnable r) {
    mGLThread.queueEvent(r);
}

 简单的使用:

package com.yh.opengltext01;

import android.content.Context;
import android.opengl.GLSurfaceView;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.MotionEvent;

public class MyGLSurfaceView extends GLSurfaceView {
    public MyGLSurfaceView(Context context) {
        super(context);
    }

    public MyGLSurfaceView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {

        if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
            queueEvent(new Runnable() {
                // 这个方法会在渲染线程里被调用
                public void run() {
                    //todo 写自己的逻辑
                }
            });
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        queueEvent(new Runnable() {
            public void run() {
                //todo 做自己的逻辑
            }
        });
        return super.onTouchEvent(event);
    }
}

如果在UI线程里调用渲染器的方法,因为UI事件和渲染绘制是在不同的线程里,会收到“call to OpenGL ES API with no current context”的警告,典型的案例就是在键盘或触摸事件方法里直接调用opengl es的API。

以上就是OpenGLSurfaceView的简单使用;

展开阅读全文

没有更多推荐了,返回首页