Android Open Source - tdpforce Gesture Image View Touch Listener






From Project

Back to project page tdpforce.

License

The source code is released under:

Apache License

If you think the Android project tdpforce 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 (c) 2012 Jason Polites//from  w  ww .  ja  v a 2 s. com
 * 
 * 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.polites.android;

import android.content.res.Configuration;
import android.graphics.PointF;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;

public class GestureImageViewTouchListener implements OnTouchListener {
  
  private GestureImageView image;
  private OnClickListener onClickListener;
  
  private final PointF current = new PointF();
  private final PointF last = new PointF();
  private final PointF next = new PointF();
  private final PointF midpoint = new PointF();
  
  private final VectorF scaleVector = new VectorF();
  private final VectorF pinchVector = new VectorF();
  
  private boolean touched = false;
  private boolean inZoom = false;
  
  private float initialDistance;
  private float lastScale = 1.0f;
  private float currentScale = 1.0f;
  
  private float boundaryLeft = 0;
  private float boundaryTop = 0;
  private float boundaryRight = 0;
  private float boundaryBottom = 0;
  
  private float maxScale = 5.0f;
  private float minScale = 0.25f;
  private float fitScaleHorizontal = 1.0f;
  private float fitScaleVertical = 1.0f;
  
  private int canvasWidth = 0;
  private int canvasHeight = 0;
  
  private float centerX = 0;
  private float centerY = 0;
  
  private float startingScale = 0;
  
  private boolean canDragX = false;
  private boolean canDragY = false;
  
  private boolean multiTouch = false;
  
  private int displayWidth;
  private int displayHeight;
  
  private int imageWidth;
  private int imageHeight;
  
  private FlingListener flingListener;
  private FlingAnimation flingAnimation;
  private ZoomAnimation zoomAnimation;
  private MoveAnimation moveAnimation;
  private GestureDetector tapDetector;
  private GestureDetector flingDetector;
  private GestureImageViewListener imageListener;

  public GestureImageViewTouchListener(final GestureImageView image, int displayWidth, int displayHeight) {
    super();
    
    this.image = image;
    
    this.displayWidth = displayWidth;
    this.displayHeight = displayHeight;
    
    this.centerX = (float) displayWidth / 2.0f;
    this.centerY = (float) displayHeight / 2.0f;
    
    this.imageWidth = image.getImageWidth();
    this.imageHeight = image.getImageHeight();
    
    startingScale = image.getScale();
    
    currentScale = startingScale;
    lastScale = startingScale;
    
    boundaryRight = displayWidth;
    boundaryBottom = displayHeight;
    boundaryLeft = 0;
    boundaryTop = 0;
    
    next.x = image.getImageX();
    next.y = image.getImageY();
    
    flingListener = new FlingListener();
    flingAnimation = new FlingAnimation();
    zoomAnimation = new ZoomAnimation();
    moveAnimation = new MoveAnimation();
    
    flingAnimation.setListener(new FlingAnimationListener() {
      @Override
      public void onMove(float x, float y) {
        handleDrag(current.x + x, current.y + y);
      }

      @Override
      public void onComplete() {}
    });
    
    zoomAnimation.setZoom(2.0f);
    zoomAnimation.setZoomAnimationListener(new ZoomAnimationListener() {
      @Override
      public void onZoom(float scale, float x, float y) {
        if(scale <= maxScale && scale >= minScale) {
          handleScale(scale, x, y);
        }
      }

      @Override
      public void onComplete() {
        inZoom = false;
        handleUp();
      }
    });
    
    moveAnimation.setMoveAnimationListener(new MoveAnimationListener() {
      
      @Override
      public void onMove(float x, float y) {
        image.setPosition(x, y);
        image.redraw();
      }
    });
    
    tapDetector = new GestureDetector(image.getContext(), new SimpleOnGestureListener() {
      @Override
      public boolean onDoubleTap(MotionEvent e) {
        startZoom(e);
        return true;
      }

      @Override
      public boolean onSingleTapConfirmed(MotionEvent e) {
        if(!inZoom) {
          if(onClickListener != null) {
            onClickListener.onClick(image);
            return true;
          }
        }

        return false;
      }
    });
    
    flingDetector = new GestureDetector(image.getContext(), flingListener);
    imageListener = image.getGestureImageViewListener();
    
    calculateBoundaries();
  }
  
  private void startFling() {
    flingAnimation.setVelocityX(flingListener.getVelocityX());
    flingAnimation.setVelocityY(flingListener.getVelocityY());
    image.animationStart(flingAnimation);
  }
  
  private void startZoom(MotionEvent e) {
    inZoom = true;
    zoomAnimation.reset();
    
    float zoomTo;
    
    if(image.isLandscape()) {
      if(image.getDeviceOrientation() == Configuration.ORIENTATION_PORTRAIT) {
        int scaledHeight = image.getScaledHeight();
        
        if(scaledHeight < canvasHeight) {
          zoomTo = fitScaleVertical / currentScale;
          zoomAnimation.setTouchX(e.getX());
          zoomAnimation.setTouchY(image.getCenterY());
        }
        else {
          zoomTo = fitScaleHorizontal / currentScale;
          zoomAnimation.setTouchX(image.getCenterX());
          zoomAnimation.setTouchY(image.getCenterY());
        }
      }
      else {
        int scaledWidth = image.getScaledWidth();
        
        if(scaledWidth == canvasWidth) {
          zoomTo = currentScale*4.0f;
          zoomAnimation.setTouchX(e.getX());
          zoomAnimation.setTouchY(e.getY());
        }
        else if(scaledWidth < canvasWidth) {
          zoomTo = fitScaleHorizontal / currentScale;
          zoomAnimation.setTouchX(image.getCenterX());
          zoomAnimation.setTouchY(e.getY());
        }
        else {
          zoomTo = fitScaleHorizontal / currentScale;
          zoomAnimation.setTouchX(image.getCenterX());
          zoomAnimation.setTouchY(image.getCenterY());
        }
      }
    }
    else {
      if(image.getDeviceOrientation() == Configuration.ORIENTATION_PORTRAIT) {
        
        int scaledHeight = image.getScaledHeight();
        
        if(scaledHeight == canvasHeight) {
          zoomTo = currentScale*4.0f;
          zoomAnimation.setTouchX(e.getX());
          zoomAnimation.setTouchY(e.getY());
        }
        else if(scaledHeight < canvasHeight) {
          zoomTo = fitScaleVertical / currentScale;
          zoomAnimation.setTouchX(e.getX());
          zoomAnimation.setTouchY(image.getCenterY());
        }
        else {
          zoomTo = fitScaleVertical / currentScale;
          zoomAnimation.setTouchX(image.getCenterX());
          zoomAnimation.setTouchY(image.getCenterY());
        }
      }
      else {
        int scaledWidth = image.getScaledWidth();
        
        if(scaledWidth < canvasWidth) {
          zoomTo = fitScaleHorizontal / currentScale;
          zoomAnimation.setTouchX(image.getCenterX());
          zoomAnimation.setTouchY(e.getY());
        }
        else {
          zoomTo = fitScaleVertical / currentScale;
          zoomAnimation.setTouchX(image.getCenterX());
          zoomAnimation.setTouchY(image.getCenterY());
        }
      }
    }
    
    zoomAnimation.setZoom(zoomTo);
    image.animationStart(zoomAnimation);
  }
  
  
  private void stopAnimations() {
    image.animationStop();
  }
  
  @Override
  public boolean onTouch(View v, MotionEvent event) {

    if(!inZoom) {
      
      if(!tapDetector.onTouchEvent(event)) {
        if(event.getPointerCount() == 1 && flingDetector.onTouchEvent(event)) {
          startFling();
        }
         
        if(event.getAction() == MotionEvent.ACTION_UP) {
          handleUp();
        }
        else if(event.getAction() == MotionEvent.ACTION_DOWN) {
          stopAnimations();
          
          last.x = event.getX();
          last.y = event.getY();
          
          if(imageListener != null) {
            imageListener.onTouch(last.x, last.y);
          }
          
          touched = true;
        }
        else if(event.getAction() == MotionEvent.ACTION_MOVE) {
          if(event.getPointerCount() > 1) {
            multiTouch = true;
            if(initialDistance > 0) {
              
              pinchVector.set(event);
              pinchVector.calculateLength();
              
              float distance = pinchVector.length;
              
              if(initialDistance != distance) {
                
                float newScale = (distance / initialDistance) * lastScale;
                
                if(newScale <= maxScale) {
                  scaleVector.length *= newScale;
                  
                  scaleVector.calculateEndPoint();
                  
                  scaleVector.length /= newScale;
                  
                  float newX = scaleVector.end.x;
                  float newY = scaleVector.end.y;
                  
                  handleScale(newScale, newX, newY);
                }
              }
            }
            else {
              initialDistance = MathUtils.distance(event);
              
              MathUtils.midpoint(event, midpoint);
              
              scaleVector.setStart(midpoint);
              scaleVector.setEnd(next);
              
              scaleVector.calculateLength();
              scaleVector.calculateAngle();
              
              scaleVector.length /= lastScale;
            }
          }
          else {
            if(!touched) {
              touched = true;
              last.x = event.getX();
              last.y = event.getY();
              next.x = image.getImageX();
              next.y = image.getImageY();
            }
            else if(!multiTouch) {
              if(handleDrag(event.getX(), event.getY())) {
                image.redraw();
              }
            }
          }
        }
      }
    }
    
    return true;
  }
  
  protected void handleUp() {
    
    multiTouch = false;
    
    initialDistance = 0;
    lastScale = currentScale;
    
    if(!canDragX) {
      next.x = centerX;
    }
    
    if(!canDragY) {
      next.y = centerY;
    }
    
    boundCoordinates();
    
    if(!canDragX && !canDragY) {
      
      if(image.isLandscape()) {
        currentScale = fitScaleHorizontal;
        lastScale = fitScaleHorizontal;
      }
      else {
        currentScale = fitScaleVertical;
        lastScale = fitScaleVertical;
      }      
    }

    image.setScale(currentScale);
    image.setPosition(next.x, next.y);
    
    if(imageListener != null) {
      imageListener.onScale(currentScale);
      imageListener.onPosition(next.x, next.y);
    }  
    
    image.redraw();
  }
  
  protected void handleScale(float scale, float x, float y) {
    
    currentScale = scale;

    if(currentScale > maxScale) {
      currentScale = maxScale;  
    }
    else if (currentScale < minScale) {
      currentScale = minScale;
    }
    else {
      next.x = x;
      next.y = y;
    }
    
    calculateBoundaries();
    
    image.setScale(currentScale);
    image.setPosition(next.x, next.y);
    
    if(imageListener != null) {
      imageListener.onScale(currentScale);
      imageListener.onPosition(next.x, next.y);
    }
    
    image.redraw();
  }
  
  protected boolean handleDrag(float x, float y) {
    current.x = x;
    current.y = y;
    
    float diffX = (current.x - last.x);
    float diffY = (current.y - last.y);
    
    if(diffX != 0 || diffY != 0) {
      
      if(canDragX) next.x += diffX;
      if(canDragY) next.y += diffY;
      
      boundCoordinates();
      
      last.x = current.x;
      last.y = current.y;
      
      if(canDragX || canDragY) {
        image.setPosition(next.x, next.y);
        
        if(imageListener != null) {
          imageListener.onPosition(next.x, next.y);
        }          
        
        return true;
      }
    }
    
    return false;
  }
  
  public void reset() {
    currentScale = startingScale;
    next.x = centerX;
    next.y = centerY;
    calculateBoundaries();
    image.setScale(currentScale);
    image.setPosition(next.x, next.y);
    image.redraw();
  }
  
  
  public float getMaxScale() {
    return maxScale;
  }

  public void setMaxScale(float maxScale) {
    this.maxScale = maxScale;
  }

  public float getMinScale() {
    return minScale;
  }

  public void setMinScale(float minScale) {
    this.minScale = minScale;
  }
  
  public void setOnClickListener(OnClickListener onClickListener) {
    this.onClickListener = onClickListener;
  }

  protected void setCanvasWidth(int canvasWidth) {
    this.canvasWidth = canvasWidth;
  }
  
  protected void setCanvasHeight(int canvasHeight) {
    this.canvasHeight = canvasHeight;
  }

  protected void setFitScaleHorizontal(float fitScale) {
    this.fitScaleHorizontal = fitScale;
  }

  protected void setFitScaleVertical(float fitScaleVertical) {
    this.fitScaleVertical = fitScaleVertical;
  }

  protected void boundCoordinates() {
    if(next.x < boundaryLeft) {
      next.x = boundaryLeft;
    }
    else if(next.x > boundaryRight) {
      next.x = boundaryRight;
    }

    if(next.y < boundaryTop) { 
      next.y = boundaryTop;
    }
    else if(next.y > boundaryBottom) {
      next.y = boundaryBottom;
    }
  }
  
  protected void calculateBoundaries() {
    
    int effectiveWidth = Math.round( (float) imageWidth * currentScale );
    int effectiveHeight = Math.round( (float) imageHeight * currentScale );
    
    canDragX = effectiveWidth > displayWidth;
    canDragY = effectiveHeight > displayHeight;
    
    if(canDragX) {
      float diff = (float)(effectiveWidth - displayWidth) / 2.0f;
      boundaryLeft = centerX - diff;
      boundaryRight = centerX + diff;
    }
    
    if(canDragY) {
      float diff = (float)(effectiveHeight - displayHeight) / 2.0f;
      boundaryTop = centerY - diff;
      boundaryBottom = centerY + diff;
    }
  }
}




Java Source Code List

com.etsy.android.grid.ClassLoaderSavedState.java
com.etsy.android.grid.ExtendableListView.java
com.etsy.android.grid.HeaderViewListAdapter.java
com.etsy.android.grid.StaggeredGridView.java
com.etsy.android.grid.util.DynamicHeightImageView.java
com.etsy.android.grid.util.DynamicHeightTextView.java
com.polites.android.Animation.java
com.polites.android.Animator.java
com.polites.android.FlingAnimationListener.java
com.polites.android.FlingAnimation.java
com.polites.android.FlingListener.java
com.polites.android.GestureImageViewListener.java
com.polites.android.GestureImageViewTouchListener.java
com.polites.android.GestureImageView.java
com.polites.android.MathUtils.java
com.polites.android.MoveAnimationListener.java
com.polites.android.MoveAnimation.java
com.polites.android.VectorF.java
com.polites.android.ZoomAnimationListener.java
com.polites.android.ZoomAnimation.java
com.readystatesoftware.systembartint.SystemBarTintManager.java
org.telugudesam.cadre.App.java
org.telugudesam.cadre.Config.java
org.telugudesam.cadre.Constants.java
org.telugudesam.cadre.activity.BaseTdpActivity.java
org.telugudesam.cadre.activity.ConversionCounterActivity.java
org.telugudesam.cadre.activity.ConversionRegistrationActivity.java
org.telugudesam.cadre.activity.PreviewActivity.java
org.telugudesam.cadre.activity.TdpMainActivity.java
org.telugudesam.cadre.adapters.DevelopmentCardsAdapter.java
org.telugudesam.cadre.components.MemCache.java
org.telugudesam.cadre.database.DbHelper.java
org.telugudesam.cadre.database.DbUtils.java
org.telugudesam.cadre.database.Tdp_cadre_boxFactory.java
org.telugudesam.cadre.fragments.DevelopmentCardsFragment.java
org.telugudesam.cadre.objects.DevelopmentCard.java
org.telugudesam.cadre.objects.Events.java
org.telugudesam.cadre.objects.Section.java
org.telugudesam.cadre.objects.dao.DevelopmentCardDao.java
org.telugudesam.cadre.objects.dao.DevelopmentCardTable.java
org.telugudesam.cadre.util.L.java
org.telugudesam.cadre.util.SystemUiHiderBase.java
org.telugudesam.cadre.util.SystemUiHiderHoneycomb.java
org.telugudesam.cadre.util.SystemUiHider.java
org.telugudesam.cadre.util.Utils.java