I'm a newbie to OpenGL on Android and last week I described a problem failing to get the renderer to start for a GLSurfaceView declared in the layout. It starts fine if I declare the renderer in the Activity class and setContentView to it. Here's a simplifed version with all the source code. What am I doing wrong?
Layout . . .
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="#+id/dummy"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="a Button" />
<FrameLayout
android:id="#+id/framelay"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<com.test.rendertest.RTSurface
android:id="#+id/RTSurfaceView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
</FrameLayout>
</LinearLayout>
Activity class. UNcomment the //A's, and comment-out the //B's and the renderer runs. But as shown below the renderer does not run even though its constructor gets called.
public class RenderTest extends Activity {
RTSurface myView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// myView = new RTSurface(this); // A
// setContentView(myView); //A
setContentView(R.layout.main); // B
myView = (com.test.rendertest.RTSurface)findViewById(R.id.RTSurfaceView); //B
}
#Override
protected void onPause() {
super.onPause();
myView.onPause();
}
#Override
protected void onResume() {
super.onResume();
myView.onResume();
}
}
GLSurfaceView . . .
class RTSurface extends GLSurfaceView {
private final RTRenderer renderer;
public RTSurface(Context context) {
super(context);
Log.i("rendertest", "RTSurface constructor - Default Form");
renderer = new RTRenderer();
setRenderer(renderer);
}
public RTSurface(Context context, AttributeSet attrs) {
super(context, attrs);
Log.i("rendertest", "RTSurface constructor - Layout Form");
renderer = new RTRenderer();
setRenderer(renderer);
}
}
. . . and the Renderer (just stubs)
class RTRenderer implements GLSurfaceView.Renderer {
public RTRenderer () {
// a constructor just to have somewhere to set
// breakpoints and logcat messages
Log.i("rendertest", "RTRenderer Constructor");
}
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
Log.i("rendertest", "onSurfaceCreated in RTRenderer");
}
public void onSurfaceChanged(GL10 gl, int width, int height) {
Log.i("rendertest", "onSurfaceChanged in RTRenderer");
}
public void onDrawFrame(GL10 gl) {
Log.i("rendertest", "onDrawFrame in RTRenderer");
}
}
Thanks in advance!!
You didn't specify your LinearLayout orientation, so it is set to horizontal by default. This means your GLSurfaceView is outside of the screen (because you set your button width to fill_parent).
Just add the following attribute to your LinearLayout :
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
Related
I have some methods for drawing simple objects using canvas. I need use use it together with xml file storing layout. Here is my code:
public class MainActivity extends Activity {
/**
* Called when the activity is first created.
*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new yourCustomView(getApplicationContext());
}
private class yourCustomView extends View {
public yourCustomView(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.RED);
}
}
}
and there is my layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<package.name.here.yourCustomView
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
Why it does not working properly?
I am quite new to Android and OpenGL ES. I have to create a GUI in OpenGL and I would like to use it as a Fragment in the main activity. In order to learn how to do this, I tried 2 tutorials - this Fragment tutorial and the Android developer tutorial on OpenGL ES.
But still I don't understand how exactly do I include an OpenGL view in a Fragment. OpenGL doesn't use XML layout files so this process is quite confusing for me. I would like to do something like this: inside the main activity from the Fragment tutorial I want to include a third Fragment with OpenGL. Go easy on me I am a beginner :)
If the developer tutorial is anything to go by, then the following setup would work:
Activity:
public class MainActivity extends FragmentActivity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getSupportFragmentManager().addOnBackStackChangedListener(new OnBackStackChangedListener()
{
public void onBackStackChanged()
{
int backCount = getSupportFragmentManager().getBackStackEntryCount();
if (backCount == 0)
{
finish();
}
}
});
if (savedInstanceState == null)
{
getSupportFragmentManager().beginTransaction().add(R.id.main_container, new OpenGLFragment()).addToBackStack(null).commit();
}
}
}
Activity XML (activity_main.xml):
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/main_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Fragment:
public class OpenGLFragment extends Fragment
{
private GLSurfaceView mGLView;
public OpenGLFragment()
{
super();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
mGLView = new MyGLSurfaceView(this.getActivity()); //I believe you may also use getActivity().getApplicationContext();
return mGLView;
}
}
And I guess you need to make your own GLSurfaceView as the tutorial says:
class MyGLSurfaceView extends GLSurfaceView {
public MyGLSurfaceView(Context context){
super(context);
setEGLContextClientVersion(2);
// Set the Renderer for drawing on the GLSurfaceView
setRenderer(new MyRenderer());
}
}
And as the tutorial says, make your renderer:
public class MyGLRenderer implements GLSurfaceView.Renderer {
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
// Set the background frame color
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
}
public void onDrawFrame(GL10 unused) {
// Redraw background color
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
}
public void onSurfaceChanged(GL10 unused, int width, int height) {
GLES20.glViewport(0, 0, width, height);
}
}
This is the java part of the code
public class MainActivity extends Activity
{
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
class surf extends View {
public surf(Context c,AttributeSet as) {
super(c,as);
}
protected void onDraw(Canvas canvas) {
Paint p = new Paint();
p.setColor(Color.MAGENTA);
canvas.drawLine(20, 20,200, 20,p);
}
}
}
And the resource file for the same without other views
<view
class="tictac.Tictac.MainActivity$surf"
android:id="#+id/graphics"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
</LinearLayout>
It closes as soon as it opens with a error, I have been following the tutorial in the android website
Try this code. Here I am adding own view as main view.
public class MainActivity extends Activity
{
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(new surf(this));
}
class surf extends View {
public surf(Context c)
{
super(c);
}
public surf(Context c,AttributeSet as) {
super(c,as);
}
protected void onDraw(Canvas canvas) {
Paint p = new Paint();
p.setColor(Color.MAGENTA);
canvas.drawLine(20, 20,200, 20,p);
}
}
}
Or you can add your view in XML
<com.my.package.MyClass
android:id="#+id/graphics"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
I'm struggling to find any tutorial to help me put a SurfaceView in a box. A pointer in the right direction would be amazing - not looking for a hand hold through it.
I'd like to be able to apportion an area at the top of the screen for example to do buttons etc and then have a surfaceview filling the rest. However, when I try to modify the code I've used for full screen surfaceView to reference my xml, it all goes a bit wrong.
My code for my main activity is as follows (I've stripped out a lot of the bits I think aren't relevant).
package com.example.mylistviewidea;
import android.os.Bundle;
etc...
public class MainActivity extends Activity implements OnTouchListener {
MyListView myListView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myListView = (MyListView) findViewById(R.id.surfaceView1);
}
class MyListView extends SurfaceView implements Runnable {
SurfaceHolder myHolder;
Thread myThread = null;
boolean isRunning = false;
// My Variables
long dT;
int myRed;
public MyListView(Context context) {
super(context);
myHolder = getHolder();
}
#Override
public void run() {
// TODO Auto-generated method stub
while (isRunning) {
if (!myHolder.getSurface().isValid())
continue;
Canvas canvas = myHolder.lockCanvas();
canvas.drawARGB(128, 0, 0, 0);
dT = SystemClock.uptimeMillis();
myRed = (int) ((int) 128*(1+Math.sin((double) dT/1000)));
}
}
}
}
My XML is:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<SurfaceView
android:id="#+id/surfaceView1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_alignParentLeft="true" />
<Button
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:text="#string/add_entries" />
<Button
android:id="#+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_below="#+id/button1"
android:text="#string/remove_entries" />
</RelativeLayout>
The error I'm getting is:
02-08 11:44:17.885: E/AndroidRuntime(10523): java.lang.RuntimeException: Unable
to start activity ComponentInfo{com.example.mylistviewidea/com.example.mylistviewidea.MainActivity}:
java.lang.ClassCastException: android.view.SurfaceView cannot be cast to
com.example.mylistviewidea.MyListView
Thanks in advance for your help!
Cheers,
Mike
You should have a custom view and use it in your xml.
First create this view
class MyListView extends SurfaceView implements Runnable {
SurfaceHolder myHolder;
Thread myThread = null;
boolean isRunning = true;
// My Variables
long dT;
int myRed;
public MyListView(Context context) {
super(context);
init();
}
public MyListView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public MyListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
myHolder = getHolder();
myHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
#Override
public void run() {
// TODO Auto-generated method stub
while (isRunning) {
if (!myHolder.getSurface().isValid())
continue;
Canvas canvas = myHolder.lockCanvas();
canvas.drawARGB(128, 128, 0, 0);
dT = SystemClock.uptimeMillis();
myRed = (int) ((int) 128 * (1 + Math.sin((double) dT / 1000)));
myHolder.unlockCanvasAndPost(canvas);
}
}
}
Change your xml to this
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<com.example.testant.MyListView
android:id="#+id/surfaceView1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_alignParentLeft="true" />
<Button
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:text="add_entries" />
<Button
android:id="#+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_below="#+id/button1"
android:text="remove_entries" />
and change main activity
public class MainActivity extends Activity {
MyListView myListView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myListView = (MyListView) findViewById(R.id.surfaceView1);
new Thread(myListView).start();
}
}
Note that you also have a bug with lock canvas, but this should work fine.
It might be a bit trivial. But try to clean project and then do a rebuild.
OR
Declare a MyListView in a separate java class file and the reference it in your xml layout file,
My question about Android UI.
When we work with XML-layout we write (for example)
setContentView(R.layout.main);
And when we work with 2d-graphics we write
Draw2D d = new Draw2D(this);
setContentView(d);
So what if I want to use both? I need to use layout-xml and a part of a screen is fir painting (Canvas). I read about surfaceView, but what about simple using Canvas?
You can actually inflate your layout from an XML file and then retrieve any view to draw on it. SurfaceView are especially convenient for drawing.
You can find below an example:
main.xml:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<SurfaceView
android:id="#+id/surface"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
TestActivity.java:
public class TestActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
SurfaceView surface = (SurfaceView) findViewById(R.id.surface);
surface.getHolder().addCallback(new Callback() {
#Override
public void surfaceCreated(SurfaceHolder holder) {
// Do some drawing when surface is ready
Canvas canvas = holder.lockCanvas();
canvas.drawColor(Color.RED);
holder.unlockCanvasAndPost(canvas);
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
});
}
}