I am using a SurfaceView to show a camera preview in my activity.
The SurfaceView is placed inside a fragment.
When this fragment is shown the Surfaceview is punching a hole in my activity,
letting the user to see whatever is behind my activity.
After the camera is fully loaded, everything is fine. But when I want to switch back to another
fragment the same thing happens.
How can I prevent this behavior?
Thanks,
Shuky
SurfaceViews have two parts, the "surface" part and the "view" part.
The "view" part is a transparent hole that gets laid out with all the other views. All of the views are composited onto a single layer. The "surface" part is a completely separate layer that is rendered behind the view layer. It has the same position and size as the "view" part, but appears behind all views.
If you need the "view" interaction to be a bit less weird, and you're using API 14 or later, you can use TextureView for your Camera preview instead.
Related
Is there a good way to draw standard Android View objects on top of a GLSurfaceView and synchronize the movement between the two layers?
My layout looks like
<FrameLayout>
<RelativeLayout>
<MyCustomGLSurfaceView>
</FrameLayout>
The MyCustomGLSurfaceView implements a camera and this camera is moved when the user touches the screen appropriately.
At the end of the onDrawFrame() callback in the renderer I invoke a callback to tell the RelativeLayout that the camera has moved and that it should update the location of any subviews as necessary.
To try and synchronize the two layers I am doing the following:
Set renderMode = RENDERMODE_WHEN_DIRTY within MyCustomGLSurfaceView
Having MyCustomGLSurfaceView implement Choreographer.FrameCallback and in the doFrame(frameTimeNanos: Long) method calling requestRender()
onDrawFrame invokes a callback to indicate the camera has moved and the RelativeLayout updates the position of it’s subviews as necessary.
Delaying the position of the camera 1 frame within the MyCustomGLSurfaceView since it appears that the Android View objects aren't updated on the screen until the following frame whereas the MyCustomGLSurfaceView was updated immediately.
The theory is that by listening for Choreographer callbacks, the rendering of the MyCustomGLSurfaceView is happening at the same time as when the application is re-drawing the standard UI elements.
This seems to work reasonably well but there are noticeable stutters within the Android View object layer. Also positioning the elements in the Android View layer has some odd behaviour when doing it via setting margins (views get destroyed when they're off screen and don't always reappear and sometimes stop being updated at all). I have tried positioning them using translationX / translationY but then the synchronization between the two layers isn't as good.
Is there a good approach for achieving this synchronization?
I have thought that it might be better to use a TextureView as opposed to a GLSurfaceView since the TextureView is composited into the view hierarchy rather than being a separate surface, so I thought maybe synchronization will fall out naturally from that.
I have a dynamic loaded fragment inside the activity. The fragment contains one SurfaceView in the bottom and it contains some text image. I used ExploreByTouchHelper for Talkback. It can read the context but can't draw the green square on SurfaceView. But if the activity contains some components(like buttons) below this SurfaceView then the green square can be drawn.
So it seems that the Application doesn't know that the space on screen for SurfaceView belongs to this Application. If some components added below, only then it knows that itself owns that space.
So anything I can do to get this green square back?
Many thanks.
It seems that SurfaceView by default will not enable drawing on it's surface so the talkback can't draw on it's surface. To enable this you have to call setWillNotDraw(false).
I am creating a layout of type FrameLayout, in which I am adding two views. Two views are objects of GLSurfaceView and SurfaceView respectively. According to Android Developers Documentation regarding SurfaceView,
"The surface is Z ordered so that it is behind the window holding its SurfaceView; the SurfaceView punches a hole in its window to allow its surface to be displayed."
It works well for me and SurfaceView always stays behind my GLSurfaceView (used for opneGL drawings). But resuming after external event the behavior is odd for a following configuration,
Android Version: 4.3
Device Model Number : Nexus 7
Kernel Version 3.4.0.g1f57c39
Jun 13
Build Number: JWR66N
For this configuration, resuming after external event puts my GLSurfaceView behind SurfaceView. In other words, SurfaceView is placed at top in ZOrder and my OpenGL drawings are no more visible. On versions greater that Android 4.3, this behavior is not seen.
I can replicate this behavior on all versions by calling SurfaceView's following method with true as a parameter.
void setZOrderOnTop
Is this known issue. Anybody can help me on this?
Regards,
Sumedh
SurfaceViews have two parts, the Surface and the View. The Surface is a completely independent layer. The View is there so the UI layout code has something to work with. Generally the View is just transparent black, so you can see through to whatever is behind it.
GLSurfaceView is just SurfaceView with some code to manage EGL contexts and threading. Underneath it's just a SurfaceView. So if you have both a SurfaceView and a GLSurfaceView, and they have the same dimensions and Z-order, then one of them is going to "win" and the other is going to "lose" because they're trying to occupy the same space at the same time. There is no defined value for which one will "win", so inconsistent behavior is expected.
One way to avoid clashes is to leave one set to the default Z, and call setZOrderMediaOverlay() on the other. The "media overlay" is still behind the UI, but above the default Surface position. If you use setZOrderOnTop(), the Surface will be positioned above the UI as well.
The upper Surface will need to be rendered with transparent pixels if you want to see something behind it (the same way that the View needs to be transparent to see the Surface).
The most efficient way to avoid this issue is to not have this issue: use one SurfaceView for everything, rendering all of your non-UI-element content to it. This requires a bit more work (and probably a SurfaceTexture) if you're rendering video or showing a camera preview on one of the Surfaces.
You can find some examples in Grafika. The "multi-surface exerciser" demonstrates three overlapping SurfaceViews rendered in software, overlapping with UI elements. Other activities show ways to work with Surfaces, GLES, the camera, and video.
See also the Android System-Level Graphics Architecture doc, which explains all this in much greater detail.
Dont use "setZOrderOnTop" as true. That will get it over all the other layouts.
If you are using multiple surfaceviews. use this for each surfaceview
yourSurfaceView.setZOrderMediaOverlay(true);
then set this setZOrderOnTop as false for the surfaceview you initiated later and wanted it to get back to the other surfaceviews
secondSurfaceview.setZOrderOnTop(false);
Just a general question: I have a Fragment which has a SurfaceView. When I hide the Fragment by the FragmentManager, the SurfaceView is still visible.
I found a workaround by just setting the View to INVISIBLE/GONE/VISIBLE when the Fragment is hidden/visible - but I'm wondering: Why is the SurfaceView still shown? Is it because the SurfaceView basically is a "punched hole", which means, it's not in the Layout-Hierarchy of the Fragment and therefore can't be hidden when the Layout is hidden?
What is the hiding-procedure doing with the Fragment? Does it simply sets the created view to gone?
A SurfaceView creates an entirely separate graphics layer, composited by the system. The "hole" is included in the view hierarchy so the layout comes out right. There's a lot of things that don't quite work right when SurfaceView is involved.
A TextureView, by contrast, is actually part of the View itself, and will act appropriately. The disadvantage is that there's an extra step where pixels are copied from an off-screen buffer into the view. This is done by the GPU, so it's very fast, but if you're trying to animate a large portion of the screen at 60fps or minimize battery drain while playing a full-length movie then SurfaceView may be a better choice.
If using a TextureView is an option, doing so may make your life a bit simpler.
I have a FrameLayout containing first a SurfaceView, and second a RelativeLayout which in turn contians various Buttons and TextViews.
Upon the canvas of the SurfaceView I am drawing numerous Bitmaps, and, via Touch and Motion Events am allowing the user to drag them around.
These Bitmaps, when dragged around pass underneath the Buttons etc that are inside the RelativeLayout.
Now, it's my (possibly mistaken) understanding that the "Z-level" of the SurfaceView, or whatever it has that passes for it, is entirely unrelated to the actual Z-level of the rest of the Layout. Is this the case? If so, how may I get around it, so that dragged Bitmaps are drawn ontop of other Views? Or what other way can I implement a full-screen canvas and yet not have my buttons etc act like the controls of an overlay.
I guess what I actually need is an underlay, where touch events can still be picked up by the Buttons etc underneath. But I don't know how to achieve this, as, when redrawing my Canvas, I have to also redraw the background.
Can I swap the order of the RelativeLayout and the SurfaceView inside the FrameLayout, and then make the background of the Canvas transparent? If so how? Will touch events still "fall through" to the buttons underneath?
Thanks for bearing with me, I know I'm a bit of a waffler.
SurfaceView doesn't work that way.
SurfaceView has two parts, the "view" part and the "surface" part. The "view" part is a transparent hole that is laid out with the other view elements, and composited onto the view layer. The "surface" part is a completely separate layer that will be positioned and scaled to match the "view" part, and composited by the system compositor rather than the app.
You can control the Z-order of the "surface" layer when the SurfaceView is first created, but it's going to be above or below all View elements. It can't go above some Views and below others because they're completely independent layers. The "surface" layer does not catch input events, so having it on top (via SurfaceView#setZOrderOnTop()) doesn't affect input focus.
For API 14+, you can use a TextureView, which behaves in a more View-friendly way.
Edit: for full details, see the much longer explanation.
Can you clarify a little bit more? In your SurfaceView do you have a background assigned? If not, you could probably also use AbsoluteView if your intention is to simply drag pieces around. If there is no background, you should be able to place the entire view above the RelativeView that you have and only have the various Buttons and such drawn on the view on top, which would be draggable and remain above everything else.