title: Surface View will not display content
body: <p>We are trying to learn how to use dynamic animation and this code is from 
  <a href="https://github.com/google-developer-training/android-advanced/tree/master/SurfaceViewExample" rel="nofollow noreferrer">Github Code</a> We have tried many changes but the code just displays nothing.
  We will post the three Class's and no XML file is included in the link</p>
  
  <p>My question is can anyone see what is missing or perhaps wrong with the code.
  YES We have included this in the build.gradle</p>
  
  <pre><code>implementation "com.android.support:support-dynamic-animation:26.1.0"
  </code></pre>
  
  <p>Here is the Main Activity</p>
  
  <pre><code>import android.content.pm.ActivityInfo;
  import android.os.Bundle;
  import android.support.v7.app.AppCompatActivity;
  
  import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
  
  /**
  * This app demonstrates how to use a SurfaceView to render an image from a
  * separate thread.
  * In addition, it shows how you can use clipping for animation,
  * and implements a basic game loop. This is not intended as a production
  * quality app. Rather, it demonstrates the basics
  * of these techniques, so you can dive deeper on your own.
  *
  * Note that SurfaceView offers trade-offs you must consider:
  *
  * * Offers a lover level drawing surface with more control without the need
  * for learning OpenGL or the NDK.
  *     * You can draw on it same as a canvas.
  *     * Draw from a separate thread, not the UI thread.
  *     * Does not have built-in hardware acceleration, e.g. for 
   transformations.
  *  Monitor performance carefully, especially if you are doing animations.
  *
  * Game play:
  *
  * The user is presented with a dark surface with a white circle.
  * This represents a wall shone at with a flashlight cone. Touching the
  * surface hides an android image. The light cone then follows continuous
  * motion. If the image of an android is discovered, the screen lights up and
  * the word "WIN!" appears. To restart lift finger and touch screen again.
  *
  * The following limitations are imposed to keep the code focused.
  *
  *     * No startup screen or any other functionality other than game play.
  *     * No saving of state, game, or user data.
  *     * No acrobatics to handle edge cases.
  */
  
  public class MainActivity extends AppCompatActivity {
  
  private GameView mGameView;
  
  @Override
  protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
  
      // Lock orientation into landscape.
      setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
  
      // Create a GameView and bind it to this activity.
      // You don't need a ViewGroup to fill the screen, because the system
      // has a FrameLayout to which this will be added.
      mGameView = new GameView(this);
      // Android 4.1 and higher simple way to request fullscreen.
      mGameView.setSystemUiVisibility(SYSTEM_UI_FLAG_FULLSCREEN);
      setContentView(mGameView);
      }
  
     /**
     * Pauses game when activity is paused.
     */
     @Override
    protected void onPause() {
      super.onPause();
      mGameView.pause();
    }
  
   /**
    * Resumes game when activity is resumed.
    */
   @Override
   protected void onResume() {
      super.onResume();
      mGameView.resume();
    }
  </code></pre>
  
  <p>And the FlashlightCone Class</p>
  
  <pre><code>/**
  * Cone of flashlight that the user moves around the view.
  </code></pre>
  
  <p>*/</p>
  
  <pre><code>public class FlashlightCone {
  
  private int mX;
  private int mY;
  private int mRadius;
  
  public FlashlightCone(int viewWidth, int viewHeight) {
      mX = viewWidth / 2;
      mY = viewHeight / 2;
      // Adjust the radius for the narrowest view dimension.
      mRadius = ((viewWidth &lt;= viewHeight) ? mX / 3 : mY / 3);
   }
  
  /**
   * Update the coordinates of the flashlight cone.
   *
   * @param newX Changed value for x coordinate.
   * @param newY Changed value for y coordinate.
   */
   public void update(int newX, int newY) {
      mX = newX;
      mY = newY;
   }
  
  public int getX() {
      return mX;
  }
  
  public int getY() {
      return mY;
  }
  
  public int getRadius() {
      return mRadius;
  }
  </code></pre>
  
  <p>And the  GameView</p>
  
  <pre><code>import android.content.Context;
  import android.graphics.Bitmap;
  import android.graphics.BitmapFactory;
  import android.graphics.Canvas;
  import android.graphics.Color;
  import android.graphics.Paint;
  import android.graphics.Path;
  import android.graphics.RectF;
  import android.graphics.Region;
  import android.util.AttributeSet;
  import android.view.MotionEvent;
  import android.view.SurfaceHolder;
  import android.view.SurfaceView;
  
  /**
  * This class demonstrates the following interactive game basics:
  *
  *   * Manages a rendering thread that draws to a SurfaceView.
  *   * Basic game loop that sleeps to conserve resources.
  *   * Processes user input to update game state.
  *   * Uses clipping as a means of animation.
  *
  * Note that these are basic versions of these techniques.
  * Non-fatal edge cases are not handled.
  * Error handling is minimal. No logging. App assumes and uses a single 
   thread.
  * Additional thread management would otherwise be necessary. See code 
   comments.
  */
  
  public class GameView extends SurfaceView implements Runnable {
  
  private boolean mRunning;
  private Thread mGameThread = null;
  private Path mPath;
  
  private Context mContext;
  
  private FlashlightCone mFlashlightCone;
  
  private Paint mPaint;
  private Bitmap mBitmap;
  private RectF mWinnerRect;
  private int mBitmapX;
  private int mBitmapY;
  private int mViewWidth;
  private int mViewHeight;
  private SurfaceHolder mSurfaceHolder;
  
  public GameView(Context context) {
      this(context, null);
  }
  
  public GameView(Context context, AttributeSet attrs) {
      super(context, attrs);
      mContext = context;
      mSurfaceHolder = getHolder();
      mPaint = new Paint();
      mPaint.setColor(Color.DKGRAY);
      mPath = new Path();
  }
  
   /**
    * We cannot get the correct dimensions of views in onCreate because
    * they have not been inflated yet. This method is called every time the
    * size of a view changes, including the first time after it has been
    * inflated.
    *
    * @param w Current width of view.
    * @param h Current height of view.
    * @param oldw Previous width of view.
    * @param oldh Previous height of view.
    */
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
      super.onSizeChanged(w, h, oldw, oldh);
  
      mViewWidth = w;
      mViewHeight = h;
  
      mFlashlightCone = new FlashlightCone(mViewWidth, mViewHeight);
  
      // Set font size proportional to view size.
      mPaint.setTextSize(mViewHeight / 5);
  
      mBitmap = BitmapFactory.decodeResource(
              mContext.getResources(), R.drawable.android);
      setUpBitmap();
      }
  
    /**
     * Runs in a separate thread.
     * All drawing happens here.
     */
    public void run() {
  
      Canvas canvas;
  
      while (mRunning) {
          // If we can obtain a valid drawing surface...
          if (mSurfaceHolder.getSurface().isValid()) {
  
              // Helper variables for performance.
              int x = mFlashlightCone.getX();
              int y = mFlashlightCone.getY();
              int radius = mFlashlightCone.getRadius();
  
              // Lock the canvas. Note that in a more complex app, with
              // more threads, you need to put this into a try/catch block
              // to make sure only one thread is drawing to the surface.
              // Starting with O, you can request a hardware surface with
              //    lockHardwareCanvas().
              // See https://developer.android.com/reference/android/view/
              //    SurfaceHolder.html#lockHardwareCanvas()
              canvas = mSurfaceHolder.lockCanvas();
  
              // Fill the canvas with white and draw the bitmap.
              canvas.save();
              canvas.drawColor(Color.WHITE);
              canvas.drawBitmap(mBitmap, mBitmapX, mBitmapY, mPaint);
  
              // Add clipping region and fill rest of the canvas with black.
              mPath.addCircle(x, y, radius, Path.Direction.CCW);
              canvas.clipPath(mPath, Region.Op.DIFFERENCE);
              canvas.drawColor(Color.BLACK);
  
              // If the x, y coordinates of the user touch are within a
              //  bounding rectangle, display the winning message.
              if (x &gt; mWinnerRect.left &amp;&amp; x &lt; mWinnerRect.right
                      &amp;&amp; y &gt; mWinnerRect.top &amp;&amp; y &lt; mWinnerRect.bottom) {
                  canvas.drawColor(Color.WHITE);
                  canvas.drawBitmap(mBitmap, mBitmapX, mBitmapY, mPaint);
                  canvas.drawText(
                          "WIN!", mViewWidth / 3, mViewHeight / 2, mPaint);
              }
              // Clear the path data structure.
              mPath.rewind();
              // Restore the previously saved (default) clip and matrix state.
              canvas.restore();
              // Release the lock on the canvas and show the surface's
              // contents on the screen.
              mSurfaceHolder.unlockCanvasAndPost(canvas);
           }
       }
     }
  
     /**
      * Updates the game data.
      * Sets new coordinates for the flashlight cone.
      *
      * @param newX New x position of touch event.
      * @param newY New y position of touch event.
      */
     private void updateFrame(int newX, int newY) {
      mFlashlightCone.update(newX, newY);
     }
  
     /**
      * Calculates a randomized location for the bitmap
      * and the winning bounding rectangle.
      */
    private void setUpBitmap() {
      mBitmapX = (int) Math.floor(
              Math.random() * (mViewWidth - mBitmap.getWidth()));
      mBitmapY = (int) Math.floor(
              Math.random() * (mViewHeight - mBitmap.getHeight()));
      mWinnerRect = new RectF(mBitmapX, mBitmapY,
              mBitmapX + mBitmap.getWidth(),
              mBitmapY + mBitmap.getHeight());
     }
  
    /**
     * Called by MainActivity.onPause() to stop the thread.
     */
    public void pause() {
      mRunning = false;
      try {
          // Stop the thread == rejoin the main thread.
          mGameThread.join();
      } catch (InterruptedException e) {
      }
    }
  
    /**
     * Called by MainActivity.onResume() to start a thread.
     */
    public void resume() {
      mRunning = true;
      mGameThread = new Thread(this);
      mGameThread.start();
    }
  
     @Override
     public boolean onTouchEvent(MotionEvent event) {
      float x = event.getX();
      float y = event.getY();
  
      // Invalidate() is inside the case statements because there are
      // many other motion events, and we don't want to invalidate
      // the view for those.
      switch (event.getAction()) {
          case MotionEvent.ACTION_DOWN:
              setUpBitmap();
              // Set coordinates of flashlight cone.
              updateFrame((int) x, (int) y);
              invalidate();
              break;
          case MotionEvent.ACTION_MOVE:
              // Updated coordinates for flashlight cone.
              updateFrame((int) x, (int) y);
              invalidate();
              break;
          default:
              // Do nothing.
        }
         return true;
      }
  </code></pre>