I have "solved" it with an ugly hack. This is not a solution but a
workaround until I find a better way:
private GLSurfaceView mGLSurfaceView;
private static boolean firstCreate = true;
/*
* Constructors
*/
/*
* Methods
*/
@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);
if (firstCreate) {
setRequestedOrientation
(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
setRequestedOrientation
(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setRequestedOrientation
(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
firstCreate = false;
}
}
On Apr 1, 4:10 pm, Ulrich Scheller <[email protected]> wrote:
> 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
-~----------~----~----~----~------~----~------~--~---