GLSurfaceView inside fragment not rendering when restarted
Asked Answered
P

5

61

I have a GLSurfaceView set up and rendering as expected using a GLSurfaceView.Renderer. My App uses fragments from the android support package. When I navigate to a new fragment surfaceDestroyed is called but when I come back to the fragment via the backstack the GLSurfaceView will not render, calls to requestRender do not result in an onDraw call.

I am aware that I need to call onResume and onPause on the surface view and I am doing this from the hosting fragment but it doesn't seem to solve the issue. All examples about htis method refer to the activity, could this be the issue? And if so how do you use a GLSurfaceView inside a fragment.

Any insight greatly appreciated, I'm happy to post code but it seems to be more of a general question to me,

Thanks

Prologue answered 17/5, 2012 at 2:5 Comment(4)
That's a great question, I am looking for an answer too (will update if I'll find a solution).Onomastic
I haven't try to use GLSurfaceView in fragment but from my experience with activity-based examples available on the Internet, they are mostly incomplete or outdated. I think that main problem is that you need to feee all OpenGL resources (textures, shaders, VBOs IIRC) in onPause and re-create them in onResume. I'll check my old code lately. Have you tried to put your exact code to the activity?Osborn
@Osborn The Code Worked fine in activities, it was when I decided to move it into fragments that I came across the issues. I don't have a lot of experience with OpenGL so if you could point me in the right direction regarding freeing all resources on pause I will give it a go.Prologue
For each glCreateShader(), glCreateProgram(), glGetTextures(), glGenBuffers() and glGenFramebuffers() in onResume() there should be glDeleteShader() glDeleteProgram(), glDeleteTextures(), glDeleteBuffers() and glDeleteFramebuffers() in onPause(). I see that I have also glBindFramebuffer(GL_FRAMEBUFFER, 0) in onPause(). Another difference is that I was using native C OpenGL ES library while (as I understand) you are using Java bindings.Osborn
T
4

Here's how I have my GLSurfaceView setup in a fragment:

onCreateView() {
    glSurfaceView = new GLSurfaceView(getActivity());
   ...
}

onPause() {
    if (glSurfaceView != null) { glSurfaceView.onPause(); }
    ...
}

onResume() {
    if (glSurfaceView != null) { glSurfaceView.onResume(); }
    ...
}

}

So, similar to what you'd do in an activity. This works in my use case, so it seems like they do work in fragments. It's hard to say more without knowing what your code looks like.

Toniatonic answered 22/2, 2013 at 1:8 Comment(0)
B
4

I know it's too late but it may be useful for others This is my answer as i have already implemented it and it works well in both emulator and in device as well.i have use fragment and supportV4.Hope you will like it.

import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.example.opengles20.glsurfaceview.GlSurfaceViewClass;
import com.example.opengles20.renderer.RendererClass;

public class MYGlclass extends Fragment {
private GlSurfaceViewClass mGLView;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
    if (container == null) {
        return null;
    }
    View view=inflater.inflate(R.layout.main, container, false);
    mGLView=(GlSurfaceViewClasss)view.findViewById(R.id.gl_surface_view);
    mGLView.setEGLContextClientVersion(2);
    RendererClass rendererclass=new RendererClass(getActivity());
    mGLView.setRenderer(rendererclass);
    mGLView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
    return view;
}
}
Bowers answered 21/3, 2013 at 13:26 Comment(0)
S
3

A couple of things you need to realize when using fragments with a glsurfaceview. It gets a little tricky but stay with me. When you navigate to a new fragment, ondestroyview gets called on your fragment with the glsurfaceview automatically, which destroys your view (the glsurfaceview that you created and returned in oncreateview).

So when you navigate to a new fragment then onpause, onstop, ondestroyview gets called automatically, without any work on your part. when you come back to that fragment with the glsurfaceview then oncreateview, onactivitycreated, onstart and onresume gets called automatically, without any work on your part.

The key to your question is understanding the fragment lifecycle which can be found at the android developer website, and understanding how glsurfaceview functions as well.

Now, using the glsurfaceview you have to implement the renderer, using onsurfacecreated, onsurfacechanged, and ondrawframe. Navigating to another fragment and then coming back to your fragment with the glsurfaceview will result in onsurfacecreated being called again, since your glsurfaceview was destroyed in ondestroyview, your context has been lost and you need to reload all of your resources on the gl thread.

Lastly, it looks from your question that ondrawframe is not being called, which is probably due to you not recreating your view, set the renderer, and returning your view from oncreateview.

so in oncreateview you need something like this

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle     
savedInstanceState) 
{
    View mView = inflater.inflate(R.layout.fragment_layout,  null);

    surface = (GLSurfaceView) mView.findViewById (R.id.glsurfaceview); 
    surface.setEGLContextClientVersion(2); 

    //return your view here
    return mView; 
}


@Override
public void onActivityCreated(Bundle mState) 
{
    super.onActivityCreated(mState);

    //setting your renderer here causes onSurfaceCreated to be called
    //if you set your renderer here then you have a context to load resources
    surface.setRenderer( shader );
}

You don't want to create your rendering class (shader) in oncreateview unless you want your rendering class to 'start over' every time you come back to your fragment with the glsurfaceview. Instead create your rendering class in your Fragments onCreate, that way if you have things set up then you will start right where you left since simply setting the renderer will provide you with the surface which will cause the onsurfacecreated, onsurfacechanged, and ondrawframe to be invoked automatically. Make sure you reload whatever resources you were last using in onsurfacecreated, on in the ondrawframe prior to drawing them.

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

    shader = new Shader(this); 

    Log.d("Fragment", "OnCreate"); 

}

when it comes to pausing and resuming your fragment, then in most cases this is automatically handled when you dynamically replace a fragment and add it to the backstack, so just simply put surface.onpause() and surface.onResume() in the right spots and you are good to go.

To make things crystal clear try putting log statements in the methods revolving around the fragment lifecycle and glsurfaceview renderer and you will be able to see what is and isn't happening.

Showily answered 3/8, 2014 at 18:24 Comment(0)
E
1

I not work with fragments but if the glsurface was destroyed, perhaps have to create an instance again of the OpenGLRenderer and reassing to the glsurface, this code work for me in activities when change orientation and recreate all the screen, in this case I have to set the contentview of layout again for reset the glsurfaceview:

view.onPause(); 
setContentView(R.layout.slidegl);
view = (GLSurfaceView) this.findViewById(R.id.glSurface);
renderer = new OpenGLRenderer();
view.setRenderer(renderer); 
view.onResume();

If you wan´t restart and setup all content of view try creating a new object of GLSurface:

this.view = new GLSurfaceView(); 
Etty answered 26/9, 2012 at 9:38 Comment(0)
L
1

Im not an expert with OpenGL ES, but I have fought my way around fragments and their lifecycle enough. What I suggest you do is set onCreateView for your fragment, tell the renderer to start drawing again, and if that doesn't work try doing it from the onResume from the fragment. There should be no need to do anything from the activity level when it comes to the drawing of the GL surface in the fragment.

Letty answered 20/1, 2013 at 16:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.