Combine Canvas and layout (Android) - android

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

Related

How to load TextureView as a part of view

I would like to load TextureView as a part of my layout. I saw before some examples that it uses TextureView inside setContentView function.
...
TextureView textureView = new TextureView(this);
textureView.setSurfaceTextureListener(this);
setContentView(textureView);
but I want to load this textureView as a part of a xml layout. how can I do?
Try this your can directly add TextureView in your layout like this
<LinearLayout
android:id="#+id/rootView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/colorPrimaryDark"
android:orientation="vertical">
<TextureView
android:id="#+id/textureView1"
android:layout_width="350dp"
android:layout_height="350dp"
android:layout_below="#+id/textView1" />
</LinearLayout>
or your can dynamically add TextureView in your layout like this
public class MainActivity extends AppCompatActivity implements TextureView.SurfaceTextureListener {
LinearLayout rootLinearLayout;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
rootLinearLayout = findViewById(R.id.rootView);
TextureView textureView = new TextureView(this);
textureView.setSurfaceTextureListener(this);
rootLinearLayout.addView(textureView);
}
#Override
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int i, int i1) {
}
#Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int i, int i1) {
}
#Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
return false;
}
#Override
public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
}
}
You can use it in layout directly with TextureView and get it with findViewById().
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextureView
android:id="#+id/texture_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>

Android OpenGL ES 2: How to use an OpenGL activity as a fragment in the main activity

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

TextureView in a Fragment never calls onSurfaceTextureAvailable

I've seen lots of examples using TextureView in a main Activity but I'm trying to put it into a Fragment.
I've created the simplest example possible and its onCreateView is being called, onActivityCreated as well but onSurfaceTextureAvailable isn't being called after passing back the TextureView.
What am I missing ?
Thanks
G
public class FullscreenActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fullscreen);
}
}
public class TextureViewFragment extends Fragment
implements TextureView.SurfaceTextureListener {
private TextureView mTextureView;
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
mTextureView = new TextureView(getActivity());
mTextureView.setSurfaceTextureListener(this);
mTextureView.setOpaque(false);
return mTextureView;
}
#Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
}
#Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
// TODO Auto-generated method stub
return false;
}
#Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width,
int height) {
// TODO Auto-generated method stub
}
#Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
// TODO Auto-generated method stub
}
}
activity_fullscreen.xml:
<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"
android:background="#0099cc"
tools:context=".FullscreenActivity" >
<fragment class="com.example.test.TextureViewFragment"
android:id="#+id/graphTextureView"
android:layout_width="0px" android:layout_height="match_parent" />
</FrameLayout>
This is because the TextureView must have a nonzero size and be visible. Based on the comment, the layout_width was set to 0px. Change that to a nonzero value.
(Old question but posting answer for posterity)
You should add 'android:hardwareAccelerated=true' to your AndroidManifest.xml.
TextureView needs hardware acceleration.
Your TextureView should be in a layout xml file. Instead of using new to instantiate it you should get it using something like this:
View view = inflater.inflate(R.layout.layout_video, container, false);
mTextureView = (TextureView) view.findViewById(R.id.video_texture_view);
Then in your layout_video.xml file you need a TextureView with id video_texture_view

SurfaceView and overlaying View with Fragments in Android

In my application I was using a custom SurfaceView to retrieve a picture from the camera, and then a custom View for printing it on screen after drawing additional features over it using a canvas. These two classes were created and added to the layout in the activity:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
myView = new MyView(this);
mySurfaceView = new MySurfaceView(this, myView);
setContentView(mySurfaceView);
addContentView(myView, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
}
and then myView was called back from mySurfaceView using the
surfaceChanged and surfaceCreated methods:
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
Camera.Parameters parameters = mCamera.getParameters();
...
mCamera.setParameters(parameters);
mCamera.startPreview();
}
public void surfaceCreated(SurfaceHolder holder) {
mCamera = Camera.open();
try{
mCamera.setPreviewDisplay(holder);
mCamera.setPreviewCallback(new Camera.PreviewCallback() {
public void onPreviewFrame(byte[] data, Camera camera) {
...
System.arraycopy(data, 0, myView.cameraData, 0, data.length);
myView.invalidate();
}
});
}catch(Exception e){
mCamera.release();
mCamera = null;
}
}
Everything was working fine... until I decided to put all of the above in a Fragment in order to use two different views in the same app. Following a tutorial, I put the two classes in the Fragment instead of the Activity:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
myView = new MyView(this);
mySurfaceView = new MySurfaceView(this, myView);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mySurfaceView = (MySurfaceView)inflater.inflate(R.layout.camera_view, null);
return mySurfaceView;
}
while the XML layout is:
camera_fragment (I'm not concerning about the other fragment, yet):
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<!-- "Fragment A" -->
<fragment
android:id="#+id/camera_frag"
android:name="com.example.mypackage.CameraFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
camera_view:
<?xml version="1.0" encoding="utf-8"?>
<view xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.example.mypackage.MySurfaceView" >
</view>
With the fragment version of the code I can see the SurfaceView with ease, but I don't seem able to print on screen the features that should be drawn by the View. No error code is reported, but the program seems to never enter the setPreviewCallback, and then the onPreviewFrame method. I don't know if it is because of how I instantiate the classes, or how I declare the layout, or both.

Trying to start renderer from GLSurfaceView declared in layout

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">

Categories

Resources