Android Open Source - lib_base Mica Surface View






From Project

Back to project page lib_base.

License

The source code is released under:

Apache License

If you think the Android project lib_base listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*
 * Copyright 2013 MicaByte Systems//from w  ww  .  ja va  2 s . co m
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
package com.micabyte.android.graphics;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Point;
import android.graphics.PointF;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.v7.appcompat.BuildConfig;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.SurfaceHolder;
import android.widget.Scroller;

/**
 * MicaSurfaceView encapsulates all of the logic for handling 2D game maps. Pass it a
 * SurfaceListener to receive touch events and a SurfaceRenderer to handle the drawing.
 * 
 * @author micabyte
 */
public class MicaSurfaceView extends android.view.SurfaceView implements SurfaceHolder.Callback, GestureDetector.OnGestureListener {
  private static final String TAG = MicaSurfaceView.class.getName();
  /** The Game Controller. This where we send UI events other than scroll and pinch-zoom in order to be handled */
  private SurfaceListener listener_ = null;
  /** The Game Renderer. This handles all of the drawing duties to the Surface view */
    private SurfaceRenderer renderer_ = null;
  // The Touch Handlers
  private TouchHandler touch_;
  private GestureDetector gesture_;
  private ScaleGestureDetector scaleGesture_;
  private long lastScaleTime_ = 0;
    // Rendering Thread
  private GameSurfaceViewThread thread_ = null;
  //private Runnable threadEvent_ = null;

  public MicaSurfaceView(Context context) {
    super(context);
        // This ensures that we don't get errors when using it in Eclipse layout editing
        if (isInEditMode()) return;
        touch_ = new TouchHandler(context);
    initialize(context);
  }

  public MicaSurfaceView(Context context, AttributeSet attrs) {
    super(context, attrs);
        // This ensures that we don't get errors when using it in Eclipse layout editing
        if (isInEditMode()) return;
        touch_ = new TouchHandler(context);
    initialize(context);
  }

  public MicaSurfaceView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
        // This ensures that we don't get errors when using it in Eclipse layout editing
        if (isInEditMode()) return;
        touch_ = new TouchHandler(context);
    initialize(context);
  }

  private void initialize(Context context) {
    // Set SurfaceHolder callback
    getHolder().addCallback(this);
    // Initialize touch handlers
        gesture_ = new GestureDetector(context, this);
        scaleGesture_ = new ScaleGestureDetector(context, new ScaleListener());
    // Allow focus
    setFocusable(true);
  }

  /** Sets the surface view listener */
  public void setListener(SurfaceListener l) {
        listener_ = l;
  }

  /** Sets the renderer and creates the rendering thread */
  public void setRenderer(SurfaceRenderer r) {
        renderer_ = r;
  }

  // Return the position of the current view (center)
  public Point getViewPosition() {
    final Point ret = new Point();
        renderer_.getViewPosition(ret);
    return ret;
  }

    public void setViewPort(int w, int h) {
        renderer_.setViewSize(w, h);
    }

  public void setViewPosition(Point p) {
        renderer_.setViewPosition(p.x, p.y);
  }

    public void setMapPosition(Point p) {
        renderer_.setMapPosition(p.x, p.y); }

  public void centerViewPosition() {
        final Point viewportSize = new Point();
        final Point sceneSize = renderer_.getBackgroundSize();
        renderer_.getViewSize(viewportSize);

        final int x = (sceneSize.x - viewportSize.x) / 2;
        final int y = (sceneSize.y - viewportSize.y) / 2;
        renderer_.setViewPosition(x, y);
  }

  public Point getViewSize() {
    final Point ret = new Point();
        renderer_.getViewPosition(ret);
    return ret;
  }

  public float getZoom() {
    return renderer_.getZoom();
  }

  public void setZoom(float z, PointF center) {
        renderer_.zoom(z, center);
  }

  @Override
  public void surfaceCreated(SurfaceHolder holder) {
        thread_ = new GameSurfaceViewThread(holder);
        thread_.setName("drawThread");
        thread_.setRunning(true);
        thread_.start();
        renderer_.start();
        touch_.start();
    // Required to ensure thread has focus
    //if (this.thread_ != null)
    //  this.thread_.onWindowFocusChanged(true);
  }

  @Override
  public void surfaceDestroyed(SurfaceHolder holder) {
        touch_.stop();
        renderer_.stop();
        thread_.setRunning(false);
    //this.thread_.surfaceDestroyed();
    boolean retry = true;
    while (retry) {
      try {
                thread_.join();
        retry = false;
      }
      catch (InterruptedException e) {
        // Repeat until success
      }
    }
  }

  @Override
  public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        renderer_.setViewSize(w, h);
    // Recheck scale factor and reset position to prevent out of bounds
        final Point p = new Point();
        renderer_.getViewPosition(p);
        setZoom(getZoom(), new PointF(p.x, p.y));
    //Point p = new Point();
    //this.renderer_.getViewPosition(p);
        renderer_.setViewPosition(p.x, p.y);
    // Debug
    Log.d(TAG, "surfaceChanged; new dimensions: w=" + w + ", h= " + h);
    // Required to ensure thread has focus
    //if (this.thread_ != null)
    //  this.thread_.onWindowFocusChanged(true);

  }
  
  @Override
  public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);
    //this.thread_.onWindowFocusChanged(hasFocus);
    if (BuildConfig.DEBUG) Log.d(TAG, "onWindowFocusChanged");
  }

  // Set a Runnable to be run on the rendering thread.
  /*public void setEvent(Runnable r) {
    this.threadEvent_ = r;
    if (this.thread_ != null)
      this.thread_.setEvent(r);
  }*/

  // Clears the runnable event, if any, from the rendering thread.
  /*public void clearEvent() {
    this.thread_.clearEvent();
  }*/

  // ----------------------------------------------------------------------

  /** The Rendering thread for the MicaSurfaceView */
  class GameSurfaceViewThread extends Thread {
    private final SurfaceHolder surfaceHolder_;
    private boolean running_ = false;

    public GameSurfaceViewThread(SurfaceHolder surfaceHolder) {
      setName("GameSurfaceViewThread");
            surfaceHolder_ = surfaceHolder;
    }

    public void setRunning(boolean b) {
            running_ = b;
    }

    @SuppressWarnings("MagicNumber")
        @Override
    public void run() {
            Canvas canvas;
            // Handle issue 58385 in Android 4.3
            int delayMillis = 5;
            if (Build.VERSION.SDK_INT == 18) delayMillis = 475;
            try {
                Thread.sleep(delayMillis);
            }
            catch (InterruptedException e) {
                // NOOP
            }
      // This is the rendering loop; it goes until asked to quit.
      while (running_) {
        // CPU timeout - help keep things cool
        try {
          Thread.sleep(5);
        }
        catch (InterruptedException e) {
          // NOOP
        }
        // Render Graphics
        canvas = null;
        try {
          canvas = surfaceHolder_.lockCanvas();
          if (canvas != null) {
            synchronized (surfaceHolder_) {
                            renderer_.draw(canvas);
            }
          }
        }
        finally {
          if (canvas != null) {
                        surfaceHolder_.unlockCanvasAndPost(canvas);
          }
        }
      }

    }

    /*
    public void onWindowFocusChanged(boolean hasFocus) {
      synchronized (this) {
        this.hasFocus_ = hasFocus;
        if (this.hasFocus_ == true) {
          notify();
        }
      }
    }

    public void surfaceDestroyed() {
      synchronized (this) {
        this.running_ = false;
      }
    }

    // Queue an "event_" to be run on the rendering thread.
    public void setEvent(Runnable r) {
      synchronized (this) {
        this.event_ = r;
      }
    }

    public void clearEvent() {
      synchronized (this) {
        this.event_ = null;
      }
    }
    */

  }

  // ----------------------------------------------------------------------

  /** Handle Touch Events */
  @SuppressWarnings("MagicNumber")
    @Override
  public boolean onTouchEvent(@NonNull MotionEvent event) {
    final boolean consumed = gesture_.onTouchEvent(event);
    if (consumed) return true;
        scaleGesture_.onTouchEvent(event);
    // Calculate actual event_ position in background view
    final Point c = new Point();
        renderer_.getViewPosition(c);
    final float s = renderer_.getZoom();
    final int x = (int) (c.x + (event.getX() * s));
    final int y = (int) (c.y + (event.getY() * s));
    // Resolve events
    switch (event.getAction() & MotionEvent.ACTION_MASK) {
      case MotionEvent.ACTION_DOWN:
                listener_.onTouchDown(x, y);
        return touch_.down(event);
      case MotionEvent.ACTION_MOVE:
                final long SCALE_MOVE_GUARD = 500;
                if (scaleGesture_.isInProgress() || System.currentTimeMillis()- lastScaleTime_ < SCALE_MOVE_GUARD)
          break;
        return touch_.move(event);
      case MotionEvent.ACTION_UP:
                listener_.onTouchUp(x, y);
        return touch_.up(event);
      case MotionEvent.ACTION_CANCEL:
        return touch_.cancel(event);
    }
    return super.onTouchEvent(event);
  }

  @Override
  public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
    return touch_.fling(e1, e2, velocityX, velocityY);
  }

  @Override
  public boolean onDown(MotionEvent e) {
    // NOOP
    return false;
  }

  @Override
  public void onLongPress(MotionEvent e) {
    // NOOP
  }

  @Override
  public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
    // NOOP
    return false;
  }

  @Override
  public void onShowPress(MotionEvent e) {
    // NOOP
  }

  @Override
  public boolean onSingleTapUp(MotionEvent e) {
    // NOOP
    return false;
  }

  /**
   * Scale Listener Used to change the scale factor on the GameSurfaceRenderer
   */
  private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
        private final PointF screenFocus = new PointF();

        public ScaleListener() {
        }

    @Override
    public boolean onScale(ScaleGestureDetector detector) {
            float scaleFactor = detector.getScaleFactor();
            if (scaleFactor!=0f && scaleFactor!=1.0f){
                scaleFactor = 1/scaleFactor;
                screenFocus.set(detector.getFocusX(), detector.getFocusY());
                renderer_.zoom(
                        scaleFactor,
                        screenFocus);
                invalidate();
            }
            lastScaleTime_ = System.currentTimeMillis();
            return true;
     }
  }

  enum TouchState {
    NO_TOUCH, IN_TOUCH, ON_FLING, IN_FLING
  }

  class TouchHandler {
    // Current Touch State
    TouchState state_ = TouchState.NO_TOUCH;
    // Point initially touched
    private final Point touchDown_ = new Point(0, 0);
    // View Center onTouchDown
    private final Point viewCenterAtDown_ = new Point(0, 0);
    // View Center onFling
    private final Point viewCenterAtFling_ = new Point();
    // View Center onFling
    private final Point viewSizeAtFling_ = new Point();
    // View Center onFling
    private Point backgroundSizeAtFling_ = new Point();
    // Scroller
    final Scroller scroller_;
    // Thread for handling
    TouchHandlerThread touchThread_;

    TouchHandler(Context context) {
            scroller_ = new Scroller(context);
    }

    void start() {
            touchThread_ = new TouchHandlerThread(this);
            touchThread_.setName("touchThread");
            touchThread_.start();
    }

    void stop() {
            touchThread_.isRunning_ = false;
            touchThread_.interrupt();
      boolean retry = true;
      while (retry) {
        try {
                    touchThread_.join();
          retry = false;
        }
        catch (InterruptedException e) {
          // Wait until done
        }
      }
            touchThread_ = null;
    }

    /** Handle a down event_ */
        @SuppressWarnings("SameReturnValue")
        boolean down(MotionEvent event) {
      // Cancel rendering suspension
            renderer_.suspend(false);
      // Get position
      synchronized (this) {
                state_ = TouchState.IN_TOUCH;
                touchDown_.x = (int) event.getX();
                touchDown_.y = (int) event.getY();
        final Point p = new Point();
                renderer_.getViewPosition(p);
                viewCenterAtDown_.set(p.x, p.y);
      }
      return true;
    }

    /** Handle a move event_ */
    boolean move(MotionEvent event) {
      if (state_ == TouchState.IN_TOUCH) {
        final float zoom = renderer_.getZoom();
        final float deltaX = (event.getX() - touchDown_.x) * zoom;
        final float deltaY = (event.getY() - touchDown_.y) * zoom;
        final float newX = viewCenterAtDown_.x - deltaX;
        final float newY = viewCenterAtDown_.y - deltaY;
                renderer_.setViewPosition((int) newX, (int) newY);
                invalidate();
        return true;
      }
      return false;
    }

    /** Handle an up event_ */
        @SuppressWarnings({"SameReturnValue", "UnusedParameters"})
        boolean up(MotionEvent event) {
      if (state_ == TouchState.IN_TOUCH) {
                state_ = TouchState.NO_TOUCH;
      }
      return true;
    }

    /** Handle a cancel event_ */
        @SuppressWarnings({"SameReturnValue", "UnusedParameters"})
        boolean cancel(MotionEvent event) {
      if (state_ == TouchState.IN_TOUCH) {
                state_ = TouchState.NO_TOUCH;
      }
      return true;
    }

    @SuppressWarnings({"SameReturnValue", "UnusedParameters"})
        boolean fling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            renderer_.getViewPosition(viewCenterAtFling_);
            renderer_.getViewSize(viewSizeAtFling_);
            backgroundSizeAtFling_ = renderer_.getBackgroundSize();
      synchronized (this) {
                state_ = TouchState.ON_FLING;
                renderer_.suspend(true);
                scroller_.fling(viewCenterAtFling_.x, viewCenterAtFling_.y, (int) -velocityX, (int) -velocityY, 0, backgroundSizeAtFling_.x - viewSizeAtFling_.x, 0, backgroundSizeAtFling_.y - viewSizeAtFling_.y);
                touchThread_.interrupt();
      }
      return true;
    }

    /**
     * Touch Handler Thread
     */
    class TouchHandlerThread extends Thread {
      private final TouchHandler touchHandler_;
      boolean isRunning_ = false;

      TouchHandlerThread(TouchHandler touch) {
                touchHandler_ = touch;
        setName("touchThread");
      }

      @Override
      public void run() {
                isRunning_ = true;
        while (isRunning_) {
          while ((touchHandler_.state_ != TouchState.ON_FLING) && (touchHandler_.state_ != TouchState.IN_FLING)) {
            try {
              Thread.sleep(Integer.MAX_VALUE);
            }
            catch (InterruptedException e) {
              // NOOP
            }
            if (!isRunning_) return;
          }
          synchronized (touchHandler_) {
            if (touchHandler_.state_ == TouchState.ON_FLING) {
                            touchHandler_.state_ = TouchState.IN_FLING;
            }
          }
          if (touchHandler_.state_ == TouchState.IN_FLING) {
                        scroller_.computeScrollOffset();
                        renderer_.setViewPosition(scroller_.getCurrX(), scroller_.getCurrY());
            if (scroller_.isFinished()) {
                            renderer_.suspend(false);
              synchronized (touchHandler_) {
                                touchHandler_.state_ = TouchState.NO_TOUCH;
                try {
                  Thread.sleep(5);
                }
                catch (InterruptedException e) {
                  // NOOP
                }
              }
            }
          }
        }
      }

      public void setRunning(boolean b) {
                isRunning_ = b;
      }

    }

  }

}




Java Source Code List

com.micabyte.android.ApplicationTest.java
com.micabyte.android.BaseObject.java
com.micabyte.android.app.BalloonPopup.java
com.micabyte.android.app.BaseActivity.java
com.micabyte.android.app.BaseFragment.java
com.micabyte.android.app.Popup.java
com.micabyte.android.graphics.BitmapSurfaceRenderer.java
com.micabyte.android.graphics.HexMapSurfaceRenderer.java
com.micabyte.android.graphics.ImageHandler.java
com.micabyte.android.graphics.MicaSurfaceView.java
com.micabyte.android.graphics.SurfaceListener.java
com.micabyte.android.graphics.SurfaceRenderer.java
com.micabyte.android.graphics.TileMapSurfaceRenderer.java
com.micabyte.android.map.HexMap.java
com.micabyte.android.map.TileMapZone.java
com.micabyte.android.map.TileMap.java
com.micabyte.android.math.Polygon.java
com.micabyte.android.media.MusicHandler.java
com.micabyte.android.util.GameHelperUtils.java
com.micabyte.android.util.GameHelper.java
com.micabyte.android.util.GameUtils.java
com.micabyte.android.util.RandomHandler.java
com.micabyte.android.util.StringHandler.java