Reputation: 944
I have created a custom SurfaceView which I use to draw shapes, and the user can zoom and pan the view to interact with the shapes. This all works great, but the problem now is that the shapes will always be drawn on top of everything on my screen, also on top of other views such as buttons and textviews. I have tried ordering the custom view and my buttons in different ways in the XML layout, tried parenting the views to FrameLayouts and RelativeLayouts (as suggested here).
I would like to have buttons and other layout elements floating on top of my custom view. As seen in the image below, when I pan my view the rectangle will obscure the buttons.
How can I prevent my custom SurfaceView from drawing on top of everything?
My drawing code runs in a separate thread in the SurfaceView class:
public class CustomShapeView extends SurfaceView implements Runnable, SurfaceHolder.Callback {
/* Other implementation stuff */
@Override
public void run() {
while (isDrawing) {
if (!holder.getSurface().isValid())
continue; //wait till it becomes valid
Canvas canvas = null;
try {
canvas = holder.lockCanvas(null);
synchronized (holder) {
try {
lock.lock();
canvas.concat(sampleMatrix);
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
drawShapes(canvas);
} finally {
lock.unlock();
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (canvas != null) {
holder.unlockCanvasAndPost(canvas);
}
}
}
}
}
My test XML layout, which I would expect to create a button (Left) which is behind the custom view and a button (Right) which is floating above the custom view, is:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Left"
android:id="@+id/button"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"/>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.example.CustomShapeView
android:id="@+id/floor_plan_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</FrameLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Right"
android:id="@+id/button2"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"/>
</RelativeLayout>
</RelativeLayout>
Upvotes: 4
Views: 3485
Reputation: 944
In my code I was calling the function setZOrderOnTop(true);
which made sure that the surface view would always be drawn on top of everything. This piece of code was left in by the previous developer in order to have a custom background for the canvas instead of only a solid color. With SurfaceView it is a tradeoff between having a custom background for the canvas or being able to draw views on top of the canvas.
The options are
setZOrderOnTop(true);
there can be custom backgrounds but no overlaying views.setZOrderOnTop(false);
there can be overlaying views but only solid color backgrounds for the canvas (by calling e.g. canvas.drawColor(Color.WHITE);
to clear between draw calls).I chose the second option as I value overlaying views over custom backgrounds.
Upvotes: 7
Reputation: 502
I'm facing a similar issue. I want to put transparent SurfaceView
between another views which are being animated in RelativeLayout
. When I do so, during animation views sometimes dissapear, sometimes only part of them is visible, it is weird. Then I discovered if I put SurfaceView
to the layout first (so at the bottom) and then I call SurfaceView.setZOrderMediaOverlay(true)
, the content of SurfaceView
is drawn at the bottom of the window, view are drawn on the top. If I put SurfaceView anywhere in the layout and then call SurfaceView.setZOrderOnTop(true)
, the content is always drawn on the top of the window (and also on the top of the views). If I put SurfaceView
between views, its behavior is crazy, maybe it's because content of SurfaceView
is drawn from separated thread and views are drawn from GUI (main) thread so they are "fighting" and that's why this behavior. Only way I found is drawing at the bottom or at the top. Try to create a new Fragment
with transparent background where the SurfaceView
is at the bottom as I described in first case and put that buttons you want to have above on top of it (so add them later in some RelativeLayout
or FrameLayout
) then show this fragment on the top of activity which holds content you want to have below it. Hopefully I helped a bit.
Upvotes: 2