Search code examples
androidsurfaceview

How do I draw 2D primtives to a SurfaceView that's a submember of my layout?


I'm just learning Android and Java. I'm familiar with graphics and programming in general in other languages.

I have a simple layout with some buttons, text views, and a SurfaceView all inside a RelativeLayout like this:

  <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Quit"
        android:id="@+id/buttonShutdown"
        android:layout_below="@+id/textView2"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:onClick="shutdown"/>
    <SurfaceView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/surfaceView"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_below="@+id/buttonShutdown"
        android:layout_alignParentRight="true"
        android:layout_alignParentEnd="true"/>

Then I have a simple MainActivity.java which is like this:

public class MainActivity extends AppCompatActivity
{
    SurfaceView surfaceView;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
        holder = surfaceView.getHolder();
        holder.getSurface();
     }

    protected void shutdown(View view)
    {
        System.exit(0);
    }

}

Obviously there's a little more to my code because I have a couple buttons and a couple textviews, but the point is I have a layout with several widgets and the SurfaceView is just one of them.

I've seen a number of examples of drawing to a surface view but they all seem to take over the whole screen and stuff.

How can I simply draw 2D lines and stuff to my SurfaceView as is without obliterating the rest of my code or layout widgets?

I've been working for days on this and it has never taken me this much effort in any other language/framework to simply draw some 2D lines!

I would be most grateful for any working examples that work with my existing layout.

UPDATE:

To clarify, the problem I'm having trouble with is how to get a handle on the surfaceView in order to get a handler in order to get a canvas on it. Once I get a canvas interface then I can lock, draw, and unlock. But I cannot figure out how to get the Canvas attached to the surfaceView.

UPDATE:

I think I have a holder for the surface view now, but how do I Get the canvas for it?

Thank you very much,

Jesse


Solution

  • Okay after much hair pulling I got it working.

    Now please a note to you grownups out there: this is intended to be a very very simple educational demonstration of the minimal code to draw lines on the screen in a Surface View. I also know that holder.getSurface(); takes some time to come alive and so drawing cannot happen right away. That's why it's in an onClick. I also know that SurfaceView rendering should be done from its own thread, and that it should be set up with 3 callbacks.

    This is just a simple sample so please do not flame me for not writing the ideal fully fledged app! Thank you kindly.

    My MainActivity.java:

    package com.gladeworks.gwscope ;
    
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.view.SurfaceHolder;
    import android.view.SurfaceView;
    import android.view.View;
    import android.widget.Button;
    import android.widget.TextView;
    
    public class MainActivity extends AppCompatActivity
    {
        TextView textView = null;
        SurfaceView surfaceView;
        SurfaceHolder holder;
        Canvas canvas;
        Paint paint;
    
        @Override
        protected void onCreate(Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            textView = (TextView) findViewById(R.id.textView);
            textView.setText("Press the Draw button!");
            surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
            holder = surfaceView.getHolder();
            holder.getSurface();
            paint = new Paint();
    
            final Button buttonDraw = (Button) findViewById(R.id.buttonDraw);
            buttonDraw.setOnClickListener(new View.OnClickListener()
            {
                public void onClick(View view)
                {
                    draw(view);
                }
            });
        }
    
    
        protected void draw(View view)  //This gets called when the Draw button is clicked.
        {
            paint.setColor(Color.GREEN);
            paint.setStrokeWidth(5);
    
            canvas = holder.lockCanvas();
            if (canvas == null) {
                textView.setText("Canvas is still null");
            } else {
                canvas.drawRGB(32, 32, 255);    //Clear the canvas to light blue
                canvas.drawLine(0,0,1000,1000,paint);//Draw a line from top left corner down and right.
                holder.unlockCanvasAndPost(canvas);
                textView.setText("Done drawing!");
            }
    
    
        }
    
    }
    

    And here's my activity_main.xml

    <?xml version="1.0" encoding="utf-8"?>
    <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"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        tools:context="com.gladeworks.gwscope.MainActivity">
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceLarge"
            android:text="Uninitialized"
            android:id="@+id/textView"
            android:layout_alignParentTop="true"
            android:layout_alignParentLeft="true"
            android:layout_alignParentStart="true"
            android:layout_alignBottom="@+id/buttonExit"
            android:layout_toLeftOf="@+id/buttonExit"
            android:layout_toStartOf="@+id/buttonExit"/>
    
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Quit"
            android:id="@+id/buttonExit"
            android:layout_alignTop="@+id/buttonDraw"
            android:layout_toLeftOf="@+id/buttonDraw"
            android:layout_toStartOf="@+id/buttonDraw"/>
    
    
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Draw"
            android:id="@+id/buttonDraw"
            android:layout_alignParentTop="true"
            android:layout_alignParentRight="true"
            android:layout_alignParentEnd="true"/>
    
        <SurfaceView
            android:id="@+id/surfaceView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@+id/buttonExit"
            android:layout_alignParentRight="true"
            android:layout_alignParentEnd="true"
            android:layout_alignParentBottom="true"
            android:layout_alignParentLeft="true"
            android:layout_alignParentStart="true"/>
    
    </RelativeLayout>
    

    Things to improve: The line drawing code should really be in its own thread so it doesn't block the user interface thread. The labels for buttons and stuff should really be defined int he resources file for easier translation efforts. The canvas and SurfaceView shuold be set up with callbacks for dealing with Creation, Change, and Destruction.

    But this should provide a clear path to beginners for understanding the needed sequence of steps to draw line graphics to a SurfaceView.