Daniel
Daniel

Reputation: 944

Prevent custom SurfaceView from drawing on top of other views

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?

enter image description here

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

Answers (2)

Daniel
Daniel

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

  1. With setZOrderOnTop(true); there can be custom backgrounds but no overlaying views.
  2. With 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

Tinko
Tinko

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

Related Questions