一.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的简单使用;