Android Open Source - Resonos-Android-Framework Touch View Worker






From Project

Back to project page Resonos-Android-Framework.

License

The source code is released under:

Apache License

If you think the Android project Resonos-Android-Framework 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

package com.resonos.apps.library.util;
/*from ww w.j  a v a 2s .co  m*/
import android.content.pm.PackageManager;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.RectF;
import android.os.Bundle;
import android.util.FloatMath;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;

import com.resonos.apps.library.App;
import com.resonos.apps.library.model.Coord;
import com.resonos.apps.library.model.ImmutableCoord;

/**
 * This class works to provide a coordinate system backing any View.
 * It also handles touch events in order to allow panning, zooming, and even rotating.
 * @author Chris
 */
public class TouchViewWorker implements OnTouchListener {
  
  // constants and enums
  public static final float THRESHOLD_DEFAULT = 10f;
  public enum TouchMode {NONE, UNUSED, PAN, TWO_FINGER};
  
  /* Parameters for the TouchViewWorker */
  public enum Param {
    /** Use this parameter to have the y coordinate system start at
     * the bottom of the screen rather than the top. This is useful
     * for situations like OpenGL or mathematical graphing. */
    FLIPY,
    /** Allow panning */
    PAN,
    /** Allow rotating (this does not affect drawing or view coordinates)
     * Not supported on phones with broken touchscreens such as the Nexus One */
    ROTATE,
    /** Allow zooming using the pinch gesture */
    ZOOM,
    /** Allow panning with one finger after lifting the second */
    PAN_AFTER_TWO_FINGERS}
  
  // context
  private final App _app;
  private final View _v;
  
  // vars
  private TouchMode mTouchMode = TouchMode.NONE;
  private boolean isEnabled = true;
  private boolean isPanning = false, isZooming = false, isRotating = false;
  private boolean mIgnoreThisSequence = false, mRecordNextMoveAsStartOneFinger = false;
  
  private float mStartDist, mStartAngle, mCurScale = 1f, mCurAngle = 0f;
  private float mStartScale = 1f, mStartAtan = 0f;
  private float mNewScale = 1f;
  private float mThreshold = THRESHOLD_DEFAULT;

  private final Coord touchStartView = new Coord(), panStartModel = new Coord(), panStartView = new Coord(),
      touchCurView = new Coord(), touchCurModel = new Coord(), tCurPannedView = new Coord(),
      touchCenterStartView = new Coord(), touchCenterCurView = new Coord(), mCurPannedModel = new Coord(),
      touchStartModel = new Coord(), temp = new Coord(), drawCoord = new Coord(),
      mTotalPannedModel = new Coord(), tTotalPannedView = new Coord(), mStartScalePanModel = new Coord();
  
  private final RectF mWindow = new RectF(), mCoords = new RectF(), debugR = new RectF();
  private final Coord mScale = new Coord();
  
  // params
  public final boolean mSupportsTouch, mSupportsMultiTouch, mSupportsFingerTracking;
  private final ParameterList<Param> mParams;

  // objects
  private final TouchViewReceiver _tvr;
  private final Paint paintDebug;
  
  /**
   * This interface is the method of customizing the behavior of a TouchViewWorker.
   * Implement some or all of the functions based on the input parameters
   * you used for a variety of interesting behavior.
   * @author Chris
   */
  public interface TouchViewReceiver {
    /**
     * Called when the user touches the screen.
     * @param touchStartView : The touch coordinate in view coordinates
     * @param touchStartModel : The touch coordinate in model coordinates
     * @return true to handle this touch sequence on your own, false to let
     * the TouchViewWorker do it and try to start panning, etc...
     */
    public boolean onTouchDown(Coord touchStartView, Coord touchStartModel);
    /**
     * Called when the usere moves their finger. Unnecessary to implement
     * unless you need special behavior.
     * @param touchPointView : The touch coordinate in view coordinates
     * @param touchPointModel : The touch coordinate in model coordinates
     */
    public void onTouchMove(Coord touchPointView, Coord touchPointModel);
    /**
     * Called when a pointer past the first touch is put onto the screen.
     * Unnecessary to implement unless you need special behavior.
     */
    public void pointerDown();
    /**
     * Called when a pointer before the last one is removed from the screen.
     * Unnecessary to implement unless you need special behavior.
     * @param tm : The pending touch mode.
     */
    public void pointerUp(TouchMode tm);
    /**
     * Called when the touch sequence ends and the last finger leaves the screen.
     * Unnecessary to implement unless you need special behavior.
     */
    public void onTouchUp();
    /**
     * Called when the TouchViewWorker wants to initiate panning.
     * @param panModel : The current pan distance in model coordinates 
     * @return Return true to allow the panning to start,
     */
    public boolean startPan(Coord panModel);
    /**
     * Called when the TouchViewWorker wants to initiate rotating.
     * @param panModel : The current angle in radians 
     * @return Return true to allow the rotating to start,
     */
    public boolean startRotate(float startingAngle);
    /**
     * Called when the TouchViewWorker wants to initiate zooming.
     * @param curScale : The current scale ratio 
     * @return Return true to allow the zooming to start,
     */
    public boolean startScale(float curScale);
    /**
     * Called when the TouchViewWorker changes the pan amount
     * @param newPanModel : The new pan amount in model coordinates
     */
    public void changePan(Coord newPanModel);
    /**
     * Called when the TouchViewWorker changes the rotation amount
     * @param newAngle : The new angle in radians
     */
    public void changeRotate(float newAngle);
    /**
     * Called when the TouchViewWorker changes the zoom amount
     * @param newScale : The new scale ratio
     */
    public void changeScale(float newScale);
    /**
     * Called when the TouchViewWorker needs information about the coordinate system.
     * This is called after creation, as well as after updateWindowParamters is called.
     * It is recommended to use the fitSizeInWindow to assist in maintaining aspect ratio.
     * @param w : Fill the RectF with the default visible window of the model coordinate system.
     * @see com.resonos.apps.library.util.TouchViewWorker.fitSizeInWindow
     */
    public void getWindow(RectF w);
  }
  
  /**
   * Construct a new TouchViewWorker to work alongside a View
   * @param app : the global app object
   * @param v : the view to work with
   * @param tvr : A TouchViewReceiver responsible for interacting with the TouchViewWorker
   * @param params : The parameters to use
   * @see com.resonos.apps.library.util.TouchViewWorker.Param
   */
  public TouchViewWorker(App app, View v, Bundle inState, TouchViewReceiver tvr, Param... params) {
    _v = v;
    _app = app;
    _tvr = tvr;

    mSupportsTouch = _app.getContext().getPackageManager()
      .hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN);
    mSupportsMultiTouch = _app.getContext().getPackageManager()
      .hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH);
    mSupportsFingerTracking = _app.getContext().getPackageManager()
      .hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT);
    
    // screen out unavailable params
    mParams = new ParameterList<Param>(params);
//    mParams.set(Param.PAN, mParams.has(Param.PAN) && mSupportsTouch);
//    mParams.set(Param.ZOOM, mParams.has(Param.ZOOM) && mSupportsMultiTouch);
//    mParams.set(Param.PAN_AFTER_TWO_FINGERS, mParams.has(Param.PAN_AFTER_TWO_FINGERS) && mSupportsMultiTouch);
    mParams.set(Param.ROTATE, mParams.has(Param.ROTATE) && mSupportsFingerTracking);
    
    v.setOnTouchListener(this);
    
    paintDebug = new Paint();
    paintDebug.setTextSize(10);
    
    if (inState != null) {
      mCurPannedModel.set(inState.getFloat(STATE_PANX, 0),
          inState.getFloat(STATE_PANY, 0));
      mTotalPannedModel.set(mCurPannedModel);
      mCurScale = inState.getFloat(STATE_SCALE, 1);
      mCurAngle = inState.getFloat(STATE_ROTATE, 0);
    }
  }
  
  public static final String STATE_PANX = "panx", STATE_PANY = "pany",
      STATE_SCALE = "scale", STATE_ROTATE = "rotate";
  
  /**
   * Saves the state of this TouchView for configuration changes
   * @return a Bundle of information to save
   */
  public Bundle onSaveInstanceState() {
    Bundle outState = new Bundle();
    outState.putFloat(STATE_PANX, mCurPannedModel.x);
    outState.putFloat(STATE_PANY, mCurPannedModel.y);
    outState.putFloat(STATE_SCALE, mCurScale);
    outState.putFloat(STATE_ROTATE, mCurAngle);
    return outState;
  }
  
  ////////// WINDOW FUNCTIONS //////////
  
  /**
   * Call this function to trigger an invalidation of the window coordinate system.
   * This will cause the TouchViewReceiver's getWindow function to be called.
   */
  public void updateWindowParameters() {
    _tvr.getWindow(mCoords);
    mWindow.set(0, 0, getViewWidth(), getViewHeight());
    mScale.set(mWindow.width()/mCoords.width(),
        mWindow.height()/mCoords.height());
//    M.log("TouchViewWorker", "updateWindowParameters window: " + mWindow.toString() + ", coords: " + mCoords.toString() +", scale: "+mScale.toString());
  }
  
  /**
   * Call this function to reset any alterations to the window by the user.
   * This restores the rotation and panning to 0, and the zooming to 1.
   */
  public void resetAlteredWindow() {
    mCurPannedModel.set(0, 0);
    mTotalPannedModel.set(0, 0);
    panStartView.set(0, 0);
    panStartModel.set(0, 0);
    mNewScale = mCurScale = mStartScale = 1;
    mCurAngle = mStartAngle = 0;
  }
  
  /**
   * Fills in the input RectF with the current region visible in the View,
   * accounting for the zooming and panning.  Uses model coordinates.
   * @param r : a RectF to be filled
   * @return the same RectF, available for chaining
   */
  public RectF getRectModel(RectF r) {
    r = getRectView(r);
    temp.set(r.left, r.top);
    convertToModel(temp, false);
    r.left = temp.x; r.top = temp.y;
    temp.set(r.right, r.bottom);
    convertToModel(temp, false);
    r.right = temp.x; r.bottom = temp.y;
    return r;
  }
  
  /**
   * Fills in the input RectF with the current region visible in the View,
   * accounting for the zooming and panning.  Uses view coordinates.
   * @param r : a RectF to be filled
   * @return the same RectF, available for chaining
   */
  public RectF getRectView(RectF r) {
    convertToView(tCurPannedView.set(mCurPannedModel), true);
    r.left = (mWindow.left - mWindow.centerX() + tCurPannedView.x) * (1 / mCurScale) + mWindow.centerX();
    r.right = (mWindow.right - mWindow.centerX() + tCurPannedView.x) * (1 / mCurScale) + mWindow.centerX();
    r.top = (mWindow.top - mWindow.centerY() + tCurPannedView.y) * (1 / mCurScale) + mWindow.centerY();
    r.bottom = (mWindow.bottom - mWindow.centerY() + tCurPannedView.y) * (1 / mCurScale) + mWindow.centerY();

//    These are the formulas used for converting values:
//    o = (i - xc) * scale + xc - panx / scale
//    (o + panx / scale - xc) / scale + xc = i
    
    return r;
  }

  /**
   * Use this function to assist with your TouchViewReceiver.getWindow()
   * implementation. Fills in the input RectF with a suitable initial model
   * window given a center position and a size coordinate.
   * @param center : Coord containing the model's default center
   * @param size : Coord containing the model's default width and height
   * @param coords : a RectF to be filled
   * @return the same RectF, available for chaining
   */
  public RectF fitSizeInWindow(ImmutableCoord center, ImmutableCoord size, RectF coords) {
    float targetRatio = size.x/size.y;
    float availRatio = getViewWidth()/getViewHeight();
    if (targetRatio < availRatio) {
      float extra = (availRatio * size.y - size.x)/2f;
      coords.set(center.x - size.x/2f - extra, center.y - size.y/2f,
          center.x + size.x/2f + extra, center.y + size.y/2f);
    } else {
      float extra = (size.x / availRatio - size.y)/2f;
      coords.set(center.x - size.x/2f, center.y - size.y/2f - extra,
          center.x + size.x/2f, center.y + size.y/2f + extra);
    }
    return coords;
  }
  
  ////////// MATH FUNCTIONS //////////
  
  /**
   * Convert a model coordinate to a view coordinate
   * Returns the same object!
   * @param c : the Coord to be converted
   * @param dist : true if this is just a relative distance, rather than an absolute position
   * @return the same Coord, available for chaining
   */
  public Coord convertToView(Coord c, boolean dist) {
    if (!dist) {
      c.x -= mCoords.left;
      c.y -= mCoords.top;
    }
    c.x *= mScale.x;
    c.y *= mScale.y;
    if (!dist) {
      c.x += mWindow.left;
      c.y += mWindow.top;
    }
    return c;
  }

  /**
   * Convert a view coordinate to a model coordinate
   * Returns the same object!
   * @param c : the Coord to be converted
   * @param dist : true if this is just a relative distance, rather than an absolute position
   * @return the same Coord, available for chaining
   */
  public Coord convertToModel(Coord c, boolean dist) {
    if (!dist) {
      c.x -= mWindow.left;
      c.y -= mWindow.top;
    }
    c.x /= mScale.x;
    c.y /= mScale.y;
    if (!dist) {
      c.x += mCoords.left;
      c.y += mCoords.top;
    }
    return c;
  }
  
  ////////// DRAWING FUNCTIONS //////////
  
  /**
   * Use this function first in your View's onDraw method to apply
   * canvas transformations so that you can draw everything at the position it
   * actually is at!
   * Remember to call endDrawing() at the end of your View's onDraw method. 
   * @param c : the canvas
   */
  public void beginDrawing(Canvas c) {
    c.save();
    boolean flipy = mParams.has(Param.FLIPY);
    drawCoord.set(tCurPannedView).mult(-1);
    c.scale(mCurScale, flipy ? -mCurScale : mCurScale, getViewWidth()/2f, getViewHeight()/2f);
    c.translate(drawCoord.x / mCurScale, drawCoord.y / mCurScale);
  }

  /**
   * Use this function last in your View's onDraw method to restore canvas transformations.
   * @param c : the canvas
   */
  public void endDrawing(Canvas c) {
    c.restore();
  }
  
  /**
   * Call this function in your View's onDraw method to add debugging information
   * to the view.  DO NOT call this method in between beginDrawing and endDrawing.
   * @param c : the canvas to draw on
   * @param darkBackground : true if you are drawing on dark colored stuff.
   */
  public void debugDraw(Canvas c, boolean darkBackground) {
    boolean flipy = mParams.has(Param.FLIPY);
    getRectView(debugR);
    paintDebug.setColor(darkBackground ? Color.WHITE : Color.BLACK);
    paintDebug.setTextAlign(Align.LEFT);
    c.drawText("("+M.printFloat(debugR.left, 1)+", "+M.printFloat(debugR.top, 1)+")",
        mWindow.left + 10, flipy ? (mWindow.bottom - 5) : (mWindow.top + 15), paintDebug);
    c.drawText("("+M.printFloat(debugR.left, 1)+", "+M.printFloat(debugR.bottom, 1)+")",
        mWindow.left + 10, flipy ? (mWindow.top + 15) : (mWindow.bottom - 5), paintDebug);
    paintDebug.setTextAlign(Align.RIGHT);
    c.drawText("("+M.printFloat(debugR.right, 1)+", "+M.printFloat(debugR.top, 1)+")",
        mWindow.right - 10, flipy ? (mWindow.bottom - 5) : (mWindow.top + 15), paintDebug);
    c.drawText("("+M.printFloat(debugR.right, 1)+", "+M.printFloat(debugR.bottom, 1)+")",
        mWindow.right - 10, flipy ? (mWindow.top + 15) : (mWindow.bottom - 5), paintDebug);
    
    paintDebug.setTextAlign(Align.CENTER);
    convertToView(tCurPannedView.set(mCurPannedModel), true);
    c.drawText("("+M.printFloat(tCurPannedView.x, 1)+", "+M.printFloat(tCurPannedView.y, 1)+")",
        mWindow.centerX(), mWindow.top + 15, paintDebug);

    getRectModel(debugR);
    paintDebug.setColor(darkBackground ? 0xFFAAEECC : 0xFF551133);
    paintDebug.setTextAlign(Align.LEFT);
    c.drawText("("+M.printFloat(debugR.left, 3)+", "+M.printFloat(debugR.top, 3)+")",
        mWindow.left + 10, flipy ? (mWindow.bottom - 25) : (mWindow.top + 35), paintDebug);
    c.drawText("("+M.printFloat(debugR.left, 3)+", "+M.printFloat(debugR.bottom, 3)+")",
        mWindow.left + 10, flipy ? (mWindow.top + 35) : (mWindow.bottom - 25), paintDebug);
    paintDebug.setTextAlign(Align.RIGHT);
    c.drawText("("+M.printFloat(debugR.right, 3)+", "+M.printFloat(debugR.top, 3)+")",
        mWindow.right - 10, flipy ? (mWindow.bottom - 25) : (mWindow.top + 35), paintDebug);
    c.drawText("("+M.printFloat(debugR.right, 3)+", "+M.printFloat(debugR.bottom, 3)+")",
        mWindow.right - 10, flipy ? (mWindow.top + 35) : (mWindow.bottom - 25), paintDebug);
    
    paintDebug.setTextAlign(Align.CENTER);
    c.drawText("("+M.printFloat(mCurPannedModel.x, 3)+", "+M.printFloat(mCurPannedModel.y, 3)+")",
        mWindow.centerX(), mWindow.top + 35, paintDebug);

    paintDebug.setColor(darkBackground ? 0xFFDFDF00 : 0xFF2020FF);
    c.drawText("Scale: "+M.printFloat(mCurScale, 3),
        mWindow.centerX(), mWindow.top + 55, paintDebug);
  }
  
  ////////// SETTERS AND GETTERS //////////

  /**
   * Returns the current pan amount in Model coordinates.
   * @return a coordinate representing the pan distance
   */
  public ImmutableCoord getCurPanModel() {
    return mCurPannedModel;
  }
  
  /**
   * Returns the current pan amount in View coordinates.
   * @return a coordinate representing the pan distance
   */
  public ImmutableCoord getCurPanView() {
    convertToView(tCurPannedView.set(mCurPannedModel), true);
    return tCurPannedView;
  }
  
  /**
   * Set the current pan amount in View coordinates
   * @param c : a coordinate representing the pan distance
   */
  public void setCurPanView(ImmutableCoord c) {
    tCurPannedView.set(c);
    convertToModel(mCurPannedModel.set(tCurPannedView), true);
    mTotalPannedModel.set(mCurPannedModel);
  }
  
  /**
   * Returns the current scale ratio (1 being default).
   * @return a float representing the scale
   */
  public float getCurScale() {
    return mCurScale;
  }
  
  /**
   * Sets the current scale ratio.
   * @param s : a float representing the scale, 1 being default
   */
  public void setCurScale(float s) {
    mCurScale = s;
  }

  /**
   * Set whether the view responds to touch events
   * @param b : true to enable, false to disable the view
   */
  public synchronized void setEnabled(boolean b) {
    isEnabled = b;
  }

  /**
   * Get the view's width
   * @return the view width as a float
   */
  private float getViewWidth() {
    return (float)_v.getWidth();
  }

  /**
   * Get the view's height
   * @return the view height as a float
   */
  private float getViewHeight() {
    return (float)_v.getHeight();
  }
  
  ////////// STATE FUNCTIONS //////////
  
  /**
   * Is the user touching the screen (and the TouchViewWorker did not filter it out).
   * @return true if touching.
   */
  public boolean isTouchDown() {
    return mTouchMode != TouchMode.NONE;
  }
  
  /**
   * Is the user panning (and the TouchViewWorker did not filter it out).
   * @return true if panning.
   */
  public boolean isPanning() {
    return mTouchMode == TouchMode.PAN;
  }
  
  /**
   * Does the user have at least two fingers down (and the TouchViewWorker did not filter it out).
   * @return true if two or more fingers down.
   */
  public boolean isTwoFingersDown() {
    return mTouchMode == TouchMode.TWO_FINGER;
  }

  /**
   * Is the user zooming (and the TouchViewWorker did not filter it out).
   * @return true if zooming.
   */
  public boolean isScaling() {
    return mTouchMode == TouchMode.TWO_FINGER && mParams.has(Param.ZOOM);
  }
  
  /**
   * Is the user rotating (and the TouchViewWorker did not filter it out).
   * @return true if rotating.
   */
  public boolean isRotating() {
    return mTouchMode == TouchMode.TWO_FINGER && mParams.has(Param.ROTATE);
  }
  
  /**
   * @return true if the device has a touch screen
   */
  public boolean supportsTouch() {
    return mSupportsTouch;
  }
  
  /**
   * @return true if the device supports multitouch
   */
  public boolean supportsMultiTouch() {
    return mSupportsMultiTouch;
  }
  
  /**
   * @return true if the device supports independent finger tracking, e.g. NOT the Nexus One
   */
  public boolean supportsFingerTracking() {
    return mSupportsFingerTracking;
  }
  
  ////////// INTERNAL WORKINGS //////////

  @Override
  public boolean onTouch(View v, MotionEvent e) {
    synchronized (this) {
      if (!isEnabled)
        return true;
    }
    int action = e.getAction() & MotionEvent.ACTION_MASK;
    if (action != MotionEvent.ACTION_DOWN && mIgnoreThisSequence)
      return true;
    switch(action) {
    case MotionEvent.ACTION_DOWN:
      startOneFinger(e);
      mNewScale = 1f;
      if (_tvr.onTouchDown(touchStartView, touchStartModel)) {
        mIgnoreThisSequence = true;
        return true;
      }
      startPan();
      convertToView(tCurPannedView.set(mCurPannedModel), true);
//      M.log("TouchViewWorker", "onTouchDown pan: " + tCurPannedView.toString() + ", scale: " + mCurScale);
      mTouchMode = (mParams.has(Param.PAN) && isPanning) ? TouchMode.PAN : TouchMode.UNUSED;
      return true;
    case MotionEvent.ACTION_POINTER_DOWN:
      finishPan();
      mStartDist = spacing(e);
      center(e, touchCenterStartView);
      touchCenterCurView.set(touchCenterStartView);
      mStartScalePanModel.set(mCurPannedModel);
      if (mSupportsFingerTracking && mParams.has(Param.ROTATE))
        mStartAtan = angle(e);
      if (mStartDist > mThreshold) {
              _tvr.pointerDown();
        mTouchMode = TouchMode.TWO_FINGER;
        if (mParams.has(Param.ZOOM))
          isZooming = _tvr.startScale(mCurScale); // mPrevDist
        if (isZooming) {
          mStartScale = mCurScale;
        }
        if (mParams.has(Param.ROTATE))
          isRotating = _tvr.startRotate(mStartAngle);
        if (isRotating)
          mStartAngle = mCurAngle;
      }
      return true;
        case MotionEvent.ACTION_POINTER_UP:
            mTouchMode = (mParams.has(Param.PAN) && mParams.has(Param.PAN_AFTER_TWO_FINGERS)
                && e.getPointerCount() <= 2) ? TouchMode.PAN : TouchMode.UNUSED;
            _tvr.pointerUp(mTouchMode);
      mTotalPannedModel.set(mCurPannedModel);
            isRotating = false;
            isZooming = false;
            if (mTouchMode == TouchMode.PAN) {
              startOneFinger(e);
          startPan();
          if (!isPanning)
            mTouchMode = TouchMode.UNUSED;
          else
            mRecordNextMoveAsStartOneFinger = true;
            }
            break;
    case MotionEvent.ACTION_MOVE:
      if (mRecordNextMoveAsStartOneFinger) {
        startOneFinger(e);
        mRecordNextMoveAsStartOneFinger = false;
      }
      convertToView(tCurPannedView.set(mCurPannedModel), true);
      touchCurView.set(e.getX(), getEventY(e.getY())).add(tCurPannedView);
      convertToModel(touchCurModel.set(touchCurView), false);
      touchCurModel.sub(0.5f,0.5f).mult(1f/mCurScale).add(0.5f,0.5f);
      convertToView(touchCurView.set(touchCurModel), false);
      _tvr.onTouchMove(touchCurView, touchCurModel);
      switch (mTouchMode) {
      case UNUSED:
        break;
      case TWO_FINGER:
        float newDist = spacing(e);
        float newAtan = angle(e);
        center(e, touchCenterCurView);
        if (newDist > mThreshold  && isRotating) {
          mCurAngle = (newAtan - mStartAtan + mStartAngle + M.PI*2) % (M.PI*2);
          if (mParams.has(Param.ROTATE) && isRotating)
            _tvr.changeRotate(mCurAngle);
        }
        if (newDist > mThreshold  && isZooming) {
          mNewScale = newDist / mStartDist;
          mCurScale = mStartScale*mNewScale;
          
          // update pan
          mCurPannedModel.set(mStartScalePanModel).mult(mNewScale);
          _tvr.changePan(mCurPannedModel);
          if (mParams.has(Param.ZOOM) && isZooming)
            _tvr.changeScale(mCurScale);
        }
        break;
      case PAN:
        touchCenterCurView.set(e.getX(), getEventY(e.getY()));
        changePan();
            break;
      case NONE:
        break;
      }
      convertToView(tCurPannedView.set(mCurPannedModel), true);
//      M.log("TouchViewWorker", "onTouchMove pan: " + tCurPannedView.toString() + ", scale: " + mCurScale);
      return true;
    case MotionEvent.ACTION_UP:
      mIgnoreThisSequence = false;
      mTouchMode = TouchMode.NONE;
      finishPan();
      _tvr.onTouchUp();
      return true;
    }
    return false;
  }
  
  /**
   * Starts one finger movement.
   * @param e : the motion event
   */
  private void startOneFinger(MotionEvent e) {
    touchCenterStartView.set(e.getX(), getEventY(e.getY()));
    touchCenterCurView.set(touchCenterStartView);

    convertToView(tCurPannedView.set(mCurPannedModel), true);
    touchStartView.set(e.getX(), getEventY(e.getY())).add(tCurPannedView);
    convertToModel(touchStartModel.set(touchStartView), false);
    touchStartModel.sub(0.5f,0.5f).mult(1f/mCurScale).add(0.5f,0.5f);
    convertToView(touchStartView.set(touchStartModel), false);
  }

  /** Starts a pan */
  private void startPan() {
    isPanning = false;
    if (mParams.has(Param.PAN)) {
      isPanning = _tvr.startPan(mCurPannedModel);
      convertToView(tTotalPannedView.set(mTotalPannedModel), true);
      panStartView.set(tTotalPannedView);
      convertToModel(panStartModel.set(panStartView), true);
    }
  }

  /** Changes the pan amount */
  private void changePan() {
    if (mParams.has(Param.PAN) && isPanning) {
      convertToView(tTotalPannedView.set(mTotalPannedModel), true);
      tCurPannedView.set(tTotalPannedView.x - (touchCenterCurView.x - touchCenterStartView.x),
          tTotalPannedView.y - (touchCenterCurView.y - touchCenterStartView.y));
      convertToModel(mCurPannedModel.set(tCurPannedView), true);
      _tvr.changePan(mCurPannedModel);
    }
  }

  /** Finishes a pan */
  private void finishPan() {
    if (mParams.has(Param.PAN) && isPanning) {
      mTotalPannedModel.set(mCurPannedModel);
      isPanning = false;
    }
  }
  
  ////////// INTERNAL MATH FUNCTIONS //////////

  /**
   * @param y : the input Y coordinate from a touch event
   * @return Returns the actual Y coordinate of a touch event,
   * in case we have a flipped coordinate system.
   */
  private float getEventY(float y) {
    return (mParams.has(Param.FLIPY)) ? (mWindow.bottom - (y - mWindow.top)) : y;
  }

  /**
   * Get the finger spacing from a touch event
   * @param event : the touch event
   * @return The distance in View coordinates between the first two fingers
   */
  private float spacing(MotionEvent event) {
    float x = event.getX(0) - event.getX(1);
    float y = event.getY(0) - event.getY(1); // no need to flip y for gl
    return FloatMath.sqrt(x * x + y * y);
  }

  /**
   * Get the finger angle from a touch event
   * @param event : the touch event
   * @return The angle in radians between the first two fingers
   */
  private float angle(MotionEvent event) {
    float x = event.getX(1) - event.getX(0);
    float y = event.getY(1) - event.getY(0);
    if (mParams.has(Param.FLIPY)) // gl is flipped
      y *= -1;
    return (float)Math.atan2(y, x);
  }

  /**
   * Get the center point between two fingers from a touch event
   * @param event : the touch event
   * @param c : the Coord the put the center point in, in View coordinates
   * @return The same Coord for chaining
   */
  private Coord center(MotionEvent event, Coord c) {
    return c.set((event.getX(1) + getEventY(event.getX(0)))/2f,
        (event.getY(1) + getEventY(event.getY(0)))/2f);
  }
}




Java Source Code List

com.resonos.apps.library.Action.java
com.resonos.apps.library.AlertFragment.java
com.resonos.apps.library.App.java
com.resonos.apps.library.BaseFragment.java
com.resonos.apps.library.FragmentBaseActivity.java
com.resonos.apps.library.file.AltAndroidFileHandle.java
com.resonos.apps.library.file.AltAndroidFiles.java
com.resonos.apps.library.file.AltFileHandle.java
com.resonos.apps.library.file.FileCache.java
com.resonos.apps.library.media.AudioVisualizer.java
com.resonos.apps.library.media.BitmapMemoryCache.java
com.resonos.apps.library.media.HueColorFilter.java
com.resonos.apps.library.media.ImageLoader.java
com.resonos.apps.library.media.MediaScannerNotifier.java
com.resonos.apps.library.model.Coord.java
com.resonos.apps.library.model.ImmutableCoord.java
com.resonos.apps.library.tabviewpager.CustomViewPager.java
com.resonos.apps.library.tabviewpager.PageIndicator.java
com.resonos.apps.library.tabviewpager.TabPageIndicator.java
com.resonos.apps.library.tabviewpager.TabViewPagerAdapter.java
com.resonos.apps.library.tabviewpager.TabViewPagerFragment.java
com.resonos.apps.library.tabviewpager.TitleProvider.java
com.resonos.apps.library.util.AppUtils.java
com.resonos.apps.library.util.ErrorReporter.java
com.resonos.apps.library.util.LifecycleTaskQueue.java
com.resonos.apps.library.util.M.java
com.resonos.apps.library.util.NetworkClient.java
com.resonos.apps.library.util.NetworkRequest.java
com.resonos.apps.library.util.ParameterList.java
com.resonos.apps.library.util.SensorReader.java
com.resonos.apps.library.util.TouchViewWorker.java
com.resonos.apps.library.util.ViewServer.java
com.resonos.apps.library.widget.DashboardLayout.java
com.resonos.apps.library.widget.FormBuilder.java
com.resonos.apps.library.widget.FormElement.java
com.resonos.apps.library.widget.ListFormBuilder.java
com.resonos.apps.library.widget.PopupWindows3D.java
com.resonos.apps.library.widget.QuickAction3D.java
com.resonos.apps.library.widget.RangeSeekBar.java
com.resonos.apps.library.widget.SeekBar.java
com.resonos.apps.library.widget.ToolBarButton.java
com.resonos.apps.library.widget.ToolBar.java