GLSurfaceView inside fragment not rendering when restarted

AndroidOpengl EsAndroid FragmentsGlsurfaceview

Android Problem Overview


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

Android Solutions


Solution 1 - Android

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.

Solution 2 - Android

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;
}
}

Solution 3 - Android

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.

Solution 4 - Android

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(); 

Solution 5 - Android

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionIan EllisView Question on Stackoverflow
Solution 1 - AndroidLearn OpenGL ESView Answer on Stackoverflow
Solution 2 - AndroidraviView Answer on Stackoverflow
Solution 3 - AndroidSavageKingView Answer on Stackoverflow
Solution 4 - AndroidZeus MonoliticsView Answer on Stackoverflow
Solution 5 - AndroidShaunView Answer on Stackoverflow