I have an Activity that was created based on the TriangleRenderer
example. It works flawlessly as a standalone project, but when I use
it as the tab of a tabhost there is a problem. At startup the whole
activity is just black - I can get touch events and do everything else
but it doesnt show anything. When I change the orientation (and
therefore recreate the activity) it all works well.
Maybe the problem comes from the View focus. My SurfaceView is not
focused when created inside of the tabhost. I tried to fiddle around
with the methods available in my onCreate() and onPostCreate(), but
couldnt find a way to get the SurfaceView focused.
The following sourcecode blocks in the guardedRun() method because
needToWait() is true because !mHasFocus is true. If I exclude the !
mHasFocus, it goes on but shows a black View anyways.
Do you have any idea why it does work after orientation change, but
not at the first startup?
The Activity looks quite simple:
=======================
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Create our Preview view and set it as the content of our
// Activity
mGLSurfaceView = new GLSurfaceView(this);
mGLSurfaceView.setRenderer(new ContactsRenderer(this));
setContentView(mGLSurfaceView);
System.out.println("focused: " + mGLSurfaceView.isFocused() +
" focusable: " + mGLSurfaceView.isFocusable());
}
@Override
protected void onResume() {
// Ideally a game should implement onResume() and onPause()
// to take appropriate action when the activity looses focus
super.onResume();
mGLSurfaceView.onResume();
}
@Override
protected void onPause() {
// Ideally a game should implement onResume() and onPause()
// to take appropriate action when the activity looses focus
super.onPause();
mGLSurfaceView.onPause();
}
Thats my SurfaceView Class:
======================
/**
* An implementation of SurfaceView that uses the dedicated surface
for
* displaying an OpenGL animation. This allows the animation to run
in a
* separate thread, without requiring that it be driven by the update
mechanism
* of the view hierarchy.
*
* The application-specific rendering code is delegated to a
GLView.Renderer
* instance.
*/
public class GLSurfaceView extends SurfaceView implements
SurfaceHolder.Callback {
private float touch_x = 0;
private long touch_time = 0;
private int touch_selected = 0;
private Context contactScreen;
public GLSurfaceView(Context context) {
super(context);
this.contactScreen = context;
init();
}
public GLSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
setFocusable(true);
// Install a SurfaceHolder.Callback so we get notified when
the
// underlying surface is created and destroyed
mHolder = getHolder();
mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_GPU);
}
public SurfaceHolder getSurfaceHolder() {
return mHolder;
}
public void setGLWrapper(GLWrapper glWrapper) {
mGLWrapper = glWrapper;
}
public void setRenderer(Renderer renderer) {
mGLThread = new GLThread(renderer);
mGLThread.start();
}
public void surfaceCreated(SurfaceHolder holder) {
mGLThread.surfaceCreated();
}
public void surfaceDestroyed(SurfaceHolder holder) {
// Surface will be destroyed when we return
mGLThread.surfaceDestroyed();
}
public void surfaceChanged(SurfaceHolder holder, int format, int
w, int h) {
// Surface size or format has changed. This should not happen
in this
// example.
mGLThread.onWindowResize(w, h);
}
/**
* Inform the view that the activity is paused.
*/
public void onPause() {
mGLThread.onPause();
}
/**
* Inform the view that the activity is resumed.
*/
public void onResume() {
mGLThread.onResume();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
ContactsRenderer render = (ContactsRenderer) mGLThread.mRenderer;
if (event.getAction() == MotionEvent.ACTION_DOWN) {
touch_x = event.getX();
touch_time = System.currentTimeMillis();
touch_selected = render.getSelectedContact();
return true;
} else {
float diff = touch_x - event.getX();
long time_diff = System.currentTimeMillis() - touch_time;
if (event.getAction() == MotionEvent.ACTION_MOVE){
render.move(-diff/1000f);
touch_x = event.getX();
return true;
} else if (event.getAction() == MotionEvent.ACTION_UP) {
if (time_diff < 170 && touch_selected ==
render.getSelectedContact()) {
//
contactScreen.switchToProfile(render.getSelectedContact());
return true;
} else {
render.moveToClosestContact();
}
}
}
return false;
}
/**
* Inform the view that the window focus has changed.
*/
@Override public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
mGLThread.onWindowFocusChanged(hasFocus);
}
/**
* Queue an "event" to be run on the GL rendering thread.
* @param r the runnable to be run on the GL rendering thread.
*/
public void queueEvent(Runnable r) {
mGLThread.queueEvent(r);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mGLThread.requestExitAndWait();
}
//
----------------------------------------------------------------------
public interface GLWrapper {
GL wrap(GL gl);
}
//
----------------------------------------------------------------------
/**
* A generic renderer interface.
*/
public interface Renderer {
/**
* @return the EGL configuration specification desired by the
renderer.
*/
int[] getConfigSpec();
/**
* Surface created.
* Called when the surface is created. Called when the
application
* starts, and whenever the GPU is reinitialized. This will
* typically happen when the device awakes after going to
sleep.
* Set your textures here.
*/
void surfaceCreated(GL10 gl);
/**
* Surface changed size.
* Called after the surface is created and whenever
* the OpenGL ES surface size changes. Set your viewport here.
* @param gl
* @param width
* @param height
*/
void sizeChanged(GL10 gl, int width, int height);
/**
* Draw the current frame.
* @param gl
*/
void drawFrame(GL10 gl);
}
/**
* An EGL helper class.
*/
private class EglHelper {
public EglHelper() {
}
/**
* Initialize EGL for a given configuration spec.
* @param configSpec
*/
public void start(int[] configSpec){
/*
* Get an EGL instance
*/
mEgl = (EGL10) EGLContext.getEGL();
/*
* Get to the default display.
*/
mEglDisplay = mEgl.eglGetDisplay
(EGL10.EGL_DEFAULT_DISPLAY);
/*
* We can now initialize EGL for that display
*/
int[] version = new int[2];
mEgl.eglInitialize(mEglDisplay, version);
EGLConfig[] configs = new EGLConfig[1];
int[] num_config = new int[1];
mEgl.eglChooseConfig(mEglDisplay, configSpec, configs, 1,
num_config);
mEglConfig = configs[0];
/*
* Create an OpenGL ES context. This must be done only
once, an
* OpenGL context is a somewhat heavy object.
*/
mEglContext = mEgl.eglCreateContext(mEglDisplay,
mEglConfig,
EGL10.EGL_NO_CONTEXT, null);
mEglSurface = null;
}
/*
* Create and return an OpenGL surface
*/
public GL createSurface(SurfaceHolder holder) {
/*
* The window size has changed, so we need to create a
new
* surface.
*/
if (mEglSurface != null) {
/*
* Unbind and destroy the old EGL surface, if
* there is one.
*/
mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
}
/*
* Create an EGL surface we can render into.
*/
mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay,
mEglConfig, holder, null);
/*
* Before we can issue GL commands, we need to make sure
* the context is current and bound to a surface.
*/
mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
mEglContext);
GL gl = mEglContext.getGL();
if (mGLWrapper != null) {
gl = mGLWrapper.wrap(gl);
}
return gl;
}
/**
* Display the current render surface.
* @return false if the context has been lost.
*/
public boolean swap() {
mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
/*
* Always check for EGL_CONTEXT_LOST, which means the
context
* and all associated data were lost (For instance because
* the device went to sleep). We need to sleep until we
* get a new surface.
*/
return mEgl.eglGetError() != EGL11.EGL_CONTEXT_LOST;
}
public void finish() {
if (mEglSurface != null) {
mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
EGL10.EGL_NO_SURFACE,
EGL10.EGL_NO_CONTEXT);
mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
mEglSurface = null;
}
if (mEglContext != null) {
mEgl.eglDestroyContext(mEglDisplay, mEglContext);
mEglContext = null;
}
if (mEglDisplay != null) {
mEgl.eglTerminate(mEglDisplay);
mEglDisplay = null;
}
}
EGL10 mEgl;
EGLDisplay mEglDisplay;
EGLSurface mEglSurface;
EGLConfig mEglConfig;
EGLContext mEglContext;
}
/**
* A generic GL Thread. Takes care of initializing EGL and GL.
Delegates
* to a Renderer instance to do the actual drawing.
*
*/
class GLThread extends Thread {
GLThread(Renderer renderer) {
super();
mDone = false;
mWidth = 0;
mHeight = 0;
mRenderer = renderer;
setName("GLThread");
}
@Override
public void run() {
/*
* When the android framework launches a second instance
of
* an activity, the new instance's onCreate() method may
be
* called before the first instance returns from onDestroy
().
*
* This semaphore ensures that only one instance at a time
* accesses EGL.
*/
try {
try {
sEglSemaphore.acquire();
} catch (InterruptedException e) {
return;
}
guardedRun();
} catch (InterruptedException e) {
// fall thru and exit normally
} finally {
sEglSemaphore.release();
}
}
private void guardedRun() throws InterruptedException {
mEglHelper = new EglHelper();
/*
* Specify a configuration for our opengl session
* and grab the first configuration that matches is
*/
int[] configSpec = mRenderer.getConfigSpec();
mEglHelper.start(configSpec);
GL10 gl = null;
boolean tellRendererSurfaceCreated = true;
boolean tellRendererSurfaceChanged = true;
/*
* This is our main activity thread's loop, we go until
* asked to quit.
*/
while (!mDone) {
/*
* Update the asynchronous state (window size)
*/
int w, h;
boolean changed;
boolean needStart = false;
synchronized (this) {
Runnable r;
while ((r = getEvent()) != null) {
r.run();
}
if (mPaused) {
mEglHelper.finish();
needStart = true;
}
if(needToWait()) {
while (needToWait()) {
// this wait() is called because the surface is not focused
wait();
}
}
if (mDone) {
break;
}
changed = mSizeChanged;
w = mWidth;
h = mHeight;
mSizeChanged = false;
}
if (needStart) {
mEglHelper.start(configSpec);
tellRendererSurfaceCreated = true;
changed = true;
}
if (changed) {
gl = (GL10) mEglHelper.createSurface(mHolder);
tellRendererSurfaceChanged = true;
}
if (tellRendererSurfaceCreated) {
mRenderer.surfaceCreated(gl);
tellRendererSurfaceCreated = false;
}
if (tellRendererSurfaceChanged) {
mRenderer.sizeChanged(gl, w, h);
tellRendererSurfaceChanged = false;
}
if ((w > 0) && (h > 0)) {
/* draw a frame here */
mRenderer.drawFrame(gl);
/*
* Once we're done with GL, we need to call
swapBuffers()
* to instruct the system to display the rendered
frame
*/
mEglHelper.swap();
}
}
/*
* clean-up everything...
*/
mEglHelper.finish();
}
private boolean needToWait() {
return (mPaused || (! mHasFocus) || (! mHasSurface) ||
mContextLost)
&& (! mDone);
}
public void surfaceCreated() {
synchronized(this) {
mHasSurface = true;
mContextLost = false;
notify();
}
}
public void surfaceDestroyed() {
synchronized(this) {
mHasSurface = false;
notify();
}
}
public void onPause() {
synchronized (this) {
mPaused = true;
}
}
public void onResume() {
synchronized (this) {
mPaused = false;
notify();
}
}
public void onWindowFocusChanged(boolean hasFocus) {
synchronized (this) {
mHasFocus = hasFocus;
if (mHasFocus == true) {
notify();
}
}
}
public void onWindowResize(int w, int h) {
synchronized (this) {
mWidth = w;
mHeight = h;
mSizeChanged = true;
}
}
public void requestExitAndWait() {
// don't call this from GLThread thread or it is a
guaranteed
// deadlock!
synchronized(this) {
mDone = true;
notify();
}
try {
join();
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
/**
* Queue an "event" to be run on the GL rendering thread.
* @param r the runnable to be run on the GL rendering thread.
*/
public void queueEvent(Runnable r) {
synchronized(this) {
mEventQueue.add(r);
}
}
private Runnable getEvent() {
synchronized(this) {
if (mEventQueue.size() > 0) {
return mEventQueue.remove(0);
}
}
return null;
}
private boolean mDone;
private boolean mPaused;
private boolean mHasFocus;
private boolean mHasSurface;
private boolean mContextLost;
private int mWidth;
private int mHeight;
private Renderer mRenderer;
private ArrayList<Runnable> mEventQueue = new
ArrayList<Runnable>();
private EglHelper mEglHelper;
}
private static final Semaphore sEglSemaphore = new Semaphore(1);
private boolean mSizeChanged = true;
private SurfaceHolder mHolder;
private GLThread mGLThread;
private GLWrapper mGLWrapper;
}
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google
Groups "Android Developers" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/android-developers?hl=en
-~----------~----~----~----~------~----~------~--~---