Android Open Source - tdpforce Gesture Image View






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/* ww  w. j a  v a 2  s .  c  o  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.polites.android;

import java.io.InputStream;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import android.content.Context;
import android.content.res.Configuration;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Matrix;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.provider.MediaStore;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.widget.ImageView;

public class GestureImageView extends ImageView  {

  public static final String GLOBAL_NS = "http://schemas.android.com/apk/res/android";
  public static final String LOCAL_NS = "http://schemas.polites.com/android";

  private final Semaphore drawLock = new Semaphore(0);
  private Animator animator;

  private Drawable drawable;

  private float x = 0, y = 0;

  private boolean layout = false;

  private float scaleAdjust = 1.0f;
  private float startingScale = -1.0f;

  private float scale = 1.0f;
  private float maxScale = 5.0f;
  private float minScale = 0.75f;
  private float fitScaleHorizontal = 1.0f;
  private float fitScaleVertical = 1.0f;
  private float rotation = 0.0f;

  private float centerX;
  private float centerY;
  
  private Float startX, startY;

  private int hWidth;
  private int hHeight;

  private int resId = -1;
  private boolean recycle = false;
  private boolean strict = false;

  private int displayHeight;
  private int displayWidth;

  private int alpha = 255;
  private ColorFilter colorFilter;

  private int deviceOrientation = -1;
  private int imageOrientation;

  private GestureImageViewListener gestureImageViewListener;
  private GestureImageViewTouchListener gestureImageViewTouchListener;
  
  private OnTouchListener customOnTouchListener;
  private OnClickListener onClickListener;

  public GestureImageView(Context context, AttributeSet attrs, int defStyle) {
    this(context, attrs);
  }

  public GestureImageView(Context context, AttributeSet attrs) {
    super(context, attrs);
    
    String scaleType = attrs.getAttributeValue(GLOBAL_NS, "scaleType");
    
    if(scaleType == null || scaleType.trim().length() == 0) {
      setScaleType(ScaleType.CENTER_INSIDE);
    }
    
    String strStartX = attrs.getAttributeValue(LOCAL_NS, "start-x");
    String strStartY = attrs.getAttributeValue(LOCAL_NS, "start-y");
    
    if(strStartX != null && strStartX.trim().length() > 0) {
      startX = Float.parseFloat(strStartX);
    }
    
    if(strStartY != null && strStartY.trim().length() > 0) {
      startY = Float.parseFloat(strStartY);
    }
    
    setStartingScale(attrs.getAttributeFloatValue(LOCAL_NS, "start-scale", startingScale));
    setMinScale(attrs.getAttributeFloatValue(LOCAL_NS, "min-scale", minScale));
    setMaxScale(attrs.getAttributeFloatValue(LOCAL_NS, "max-scale", maxScale));
    setStrict(attrs.getAttributeBooleanValue(LOCAL_NS, "strict", strict));
    setRecycle(attrs.getAttributeBooleanValue(LOCAL_NS, "recycle", recycle));
    
    initImage();
  }

  public GestureImageView(Context context) {
    super(context);
    setScaleType(ScaleType.CENTER_INSIDE);
    initImage();
  }

  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

    if(drawable != null) {
      int orientation = getResources().getConfiguration().orientation;
      if(orientation == Configuration.ORIENTATION_LANDSCAPE) {
        displayHeight = MeasureSpec.getSize(heightMeasureSpec);

        if(getLayoutParams().width == LayoutParams.WRAP_CONTENT) {
          float ratio = (float) getImageWidth() / (float) getImageHeight();
          displayWidth = Math.round( (float) displayHeight * ratio) ;
        }
        else {
          displayWidth = MeasureSpec.getSize(widthMeasureSpec);
        }
      }
      else {
        displayWidth = MeasureSpec.getSize(widthMeasureSpec);
        if(getLayoutParams().height == LayoutParams.WRAP_CONTENT) {
          float ratio = (float) getImageHeight() / (float) getImageWidth();
          displayHeight = Math.round( (float) displayWidth * ratio) ;
        }
        else {
          displayHeight = MeasureSpec.getSize(heightMeasureSpec);
        }        
      }
    }
    else {
      displayHeight = MeasureSpec.getSize(heightMeasureSpec);
      displayWidth = MeasureSpec.getSize(widthMeasureSpec);
    }

    setMeasuredDimension(displayWidth, displayHeight);
  }

  @Override
  protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    super.onLayout(changed, left, top, right, bottom);
    if(changed || !layout) {
      setupCanvas(displayWidth, displayHeight, getResources().getConfiguration().orientation);
    }
  }

  protected void setupCanvas(int measuredWidth, int measuredHeight, int orientation) {

    if(deviceOrientation != orientation) {
      layout = false;
      deviceOrientation = orientation;
    }

    if(drawable != null && !layout) {
      int imageWidth = getImageWidth();
      int imageHeight = getImageHeight();

      hWidth = Math.round(((float)imageWidth / 2.0f));
      hHeight = Math.round(((float)imageHeight / 2.0f));
      
      measuredWidth -= (getPaddingLeft() + getPaddingRight());
      measuredHeight -= (getPaddingTop() + getPaddingBottom());
      
      computeCropScale(imageWidth, imageHeight, measuredWidth, measuredHeight);
      
      if(startingScale <= 0.0f) {
        computeStartingScale(imageWidth, imageHeight, measuredWidth, measuredHeight);
      }

      scaleAdjust = startingScale;

      this.centerX = (float) measuredWidth / 2.0f;
      this.centerY = (float) measuredHeight / 2.0f;
      
      if(startX == null) {
        x = centerX;
      }
      else {
        x = startX;
      }

      if(startY == null) {
        y = centerY;
      }
      else {
        y = startY;
      }  

      gestureImageViewTouchListener = new GestureImageViewTouchListener(this, measuredWidth, measuredHeight);
      
      if(isLandscape()) {
        gestureImageViewTouchListener.setMinScale(minScale * fitScaleHorizontal);
      }
      else {
        gestureImageViewTouchListener.setMinScale(minScale * fitScaleVertical);
      }
      
      
      gestureImageViewTouchListener.setMaxScale(maxScale * startingScale);
      
      gestureImageViewTouchListener.setFitScaleHorizontal(fitScaleHorizontal);
      gestureImageViewTouchListener.setFitScaleVertical(fitScaleVertical);
      gestureImageViewTouchListener.setCanvasWidth(measuredWidth);
      gestureImageViewTouchListener.setCanvasHeight(measuredHeight);
      gestureImageViewTouchListener.setOnClickListener(onClickListener);

      drawable.setBounds(-hWidth,-hHeight,hWidth,hHeight);

      super.setOnTouchListener(new OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
          if(customOnTouchListener != null) {
            customOnTouchListener.onTouch(v, event);
          }
          return gestureImageViewTouchListener.onTouch(v, event);
        }
      });  

      layout = true;
    }
  }
  
  protected void computeCropScale(int imageWidth, int imageHeight, int measuredWidth, int measuredHeight) {
    fitScaleHorizontal = (float) measuredWidth / (float) imageWidth;
    fitScaleVertical = (float) measuredHeight / (float) imageHeight;
  }
  
  protected void computeStartingScale(int imageWidth, int imageHeight, int measuredWidth, int measuredHeight) {
    switch(getScaleType()) {
      case CENTER:
        // Center the image in the view, but perform no scaling.
        startingScale = 1.0f;
        break;
        
      case CENTER_CROP:
        // Scale the image uniformly (maintain the image's aspect ratio) so that both dimensions
        // (width and height) of the image will be equal to or larger than the corresponding dimension of the view (minus padding).
        startingScale = Math.max((float) measuredHeight / (float) imageHeight, (float) measuredWidth/ (float) imageWidth);
        break;
        
      case CENTER_INSIDE:

        // Scale the image uniformly (maintain the image's aspect ratio) so that both dimensions
        // (width and height) of the image will be equal to or less than the corresponding dimension of the view (minus padding).
        float wRatio = (float) imageWidth / (float) measuredWidth;
        float hRatio = (float) imageHeight / (float) measuredHeight;

        if(wRatio > hRatio) {
          startingScale = fitScaleHorizontal;
        }
        else {
          startingScale = fitScaleVertical;
        }

        break;
    }
  }

  protected boolean isRecycled() {
    if(drawable != null && drawable instanceof BitmapDrawable) {
      Bitmap bitmap = ((BitmapDrawable)drawable).getBitmap();
      if(bitmap != null) {
        return bitmap.isRecycled();
      }
    }
    return false;
  }

  protected void recycle() {
    if(recycle && drawable != null && drawable instanceof BitmapDrawable) {
      Bitmap bitmap = ((BitmapDrawable)drawable).getBitmap();
      if(bitmap != null) {
        bitmap.recycle();
      }
    }
  }
  
  @Override
  protected void onDraw(Canvas canvas) {
    if(layout) {
      if(drawable != null && !isRecycled()) {
        canvas.save();
        
        float adjustedScale = scale * scaleAdjust;

        canvas.translate(x, y);

        if(rotation != 0.0f) {
          canvas.rotate(rotation);
        }

        if(adjustedScale != 1.0f) {
          canvas.scale(adjustedScale, adjustedScale);
        }

        drawable.draw(canvas);

        canvas.restore();
      }

      if(drawLock.availablePermits() <= 0) {
        drawLock.release();
      }
    }
  }

  /**
   * Waits for a draw
   * @param max time to wait for draw (ms)
   * @throws InterruptedException
   */
  public boolean waitForDraw(long timeout) throws InterruptedException {
    return drawLock.tryAcquire(timeout, TimeUnit.MILLISECONDS);
  }

  @Override
  protected void onAttachedToWindow() {
    animator = new Animator(this, "GestureImageViewAnimator");
    animator.start();

    if(resId >= 0 && drawable == null) {
      setImageResource(resId);
    }

    super.onAttachedToWindow();
  }

  public void animationStart(Animation animation) {
    if(animator != null) {
      animator.play(animation);
    }
  }

  public void animationStop() {
    if(animator != null) {
      animator.cancel();
    }
  }

  @Override
  protected void onDetachedFromWindow() {
    if(animator != null) {
      animator.finish();
    }
    if(recycle && drawable != null && !isRecycled()) {
      recycle();
      drawable = null;
    }
    super.onDetachedFromWindow();
  }

  protected void initImage() {
    if(this.drawable != null) {
      this.drawable.setAlpha(alpha);
      this.drawable.setFilterBitmap(true);
      if(colorFilter != null) {
        this.drawable.setColorFilter(colorFilter);
      }
    }
    
    if(!layout) {
      requestLayout();
      redraw();
    }
  }

  public void setImageBitmap(Bitmap image) {
    this.drawable = new BitmapDrawable(getResources(), image);
    initImage();
  }

  @Override
  public void setImageDrawable(Drawable drawable) {
    this.drawable = drawable;
    initImage();
  }

  public void setImageResource(int id) {
    if(this.drawable != null) {
      this.recycle();
    }
    if(id >= 0) {
      this.resId = id;
      setImageDrawable(getContext().getResources().getDrawable(id));
    }
  }

  public int getScaledWidth() {
    return Math.round(getImageWidth() * getScale());
  }
  
  public int getScaledHeight() {
    return Math.round(getImageHeight() * getScale());
  }
  
  public int getImageWidth() {
    if(drawable != null) {
      return drawable.getIntrinsicWidth();
    }
    return 0;
  }

  public int getImageHeight() {
    if(drawable != null) {
      return drawable.getIntrinsicHeight();
    }
    return 0;
  }

  public void moveBy(float x, float y) {
    this.x += x;
    this.y += y;
  }

  public void setPosition(float x, float y) {
    this.x = x;
    this.y = y;
  }

  public void redraw() {
    postInvalidate();
  }

  public void setMinScale(float min) {
    this.minScale = min;
    if(gestureImageViewTouchListener != null) {
      gestureImageViewTouchListener.setMinScale(min * fitScaleHorizontal);
    }
  }

  public void setMaxScale(float max) {
    this.maxScale = max;
    if(gestureImageViewTouchListener != null) {
      gestureImageViewTouchListener.setMaxScale(max * startingScale);
    }
  }

  public void setScale(float scale) {
    scaleAdjust = scale;
  }

  public float getScale() {
    return scaleAdjust;
  }

  public float getImageX() {
    return x;
  }

  public float getImageY() {
    return y;
  }

  public boolean isStrict() {
    return strict;
  }

  public void setStrict(boolean strict) {
    this.strict = strict;
  }

  public boolean isRecycle() {
    return recycle;
  }

  public void setRecycle(boolean recycle) {
    this.recycle = recycle;
  }

  public void reset() {
    x = centerX;
    y = centerY;
    scaleAdjust = startingScale;
    if (gestureImageViewTouchListener != null) {
        gestureImageViewTouchListener.reset();
    }
    redraw();
  }

  public void setRotation(float rotation) {
    this.rotation = rotation;
  }

  public void setGestureImageViewListener(GestureImageViewListener pinchImageViewListener) {
    this.gestureImageViewListener = pinchImageViewListener;
  }

  public GestureImageViewListener getGestureImageViewListener() {
    return gestureImageViewListener;
  }

  @Override
  public Drawable getDrawable() {
    return drawable;
  }

  @Override
  public void setAlpha(int alpha) {
    this.alpha = alpha;
    if(drawable != null) {
      drawable.setAlpha(alpha);
    }
  }

  @Override
  public void setColorFilter(ColorFilter cf) {
    this.colorFilter = cf;
    if(drawable != null) {
      drawable.setColorFilter(cf);
    }
  }

  @Override
  public void setImageURI(Uri mUri) {
    if ("content".equals(mUri.getScheme())) {
      try {
        String[] orientationColumn = {MediaStore.Images.Media.ORIENTATION};
        
        Cursor cur = getContext().getContentResolver().query(mUri, orientationColumn, null, null, null);
        
        if (cur != null && cur.moveToFirst()) {
          imageOrientation = cur.getInt(cur.getColumnIndex(orientationColumn[0]));
        }  
        
        InputStream in = null;
        
        try {
          in = getContext().getContentResolver().openInputStream(mUri);
          Bitmap bmp = BitmapFactory.decodeStream(in);
          
          if(imageOrientation != 0) {
            Matrix m = new Matrix();
            m.postRotate(imageOrientation);
            Bitmap rotated = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), m, true);
            bmp.recycle();
            setImageDrawable(new BitmapDrawable(getResources(), rotated));
          }
          else {
            setImageDrawable(new BitmapDrawable(getResources(), bmp));
          }
        }
        finally {
          if(in != null) {
            in.close();
          }
          
          if(cur != null) {
            cur.close();
          }
        }
      }
      catch (Exception e) {
        Log.w("GestureImageView", "Unable to open content: " + mUri, e);
      }
    }
    else {
      setImageDrawable(Drawable.createFromPath(mUri.toString()));
    }

    if (drawable == null) {
      Log.e("GestureImageView", "resolveUri failed on bad bitmap uri: " + mUri);
      // Don't try again.
      mUri = null;
    }
  }

  @Override
  public Matrix getImageMatrix() {
    if(strict) {
      throw new UnsupportedOperationException("Not supported");
    }    
    return super.getImageMatrix();
  }

  @Override
  public void setScaleType(ScaleType scaleType) {
    if(scaleType == ScaleType.CENTER ||
      scaleType == ScaleType.CENTER_CROP ||
      scaleType == ScaleType.CENTER_INSIDE) {
      
      super.setScaleType(scaleType);
    }
    else if(strict) {
      throw new UnsupportedOperationException("Not supported");
    }
  }

  @Override
  public void invalidateDrawable(Drawable dr) {
    if(strict) {
      throw new UnsupportedOperationException("Not supported");
    }
    super.invalidateDrawable(dr);
  }

  @Override
  public int[] onCreateDrawableState(int extraSpace) {
    if(strict) {
      throw new UnsupportedOperationException("Not supported");
    }
    return super.onCreateDrawableState(extraSpace);
  }

  @Override
  public void setAdjustViewBounds(boolean adjustViewBounds) {
    if(strict) {
      throw new UnsupportedOperationException("Not supported");
    }
    super.setAdjustViewBounds(adjustViewBounds);
  }

  @Override
  public void setImageLevel(int level) {
    if(strict) {
      throw new UnsupportedOperationException("Not supported");
    }
    super.setImageLevel(level);
  }

  @Override
  public void setImageMatrix(Matrix matrix) {
    if(strict) {
      throw new UnsupportedOperationException("Not supported");
    }
  }

  @Override
  public void setImageState(int[] state, boolean merge) {
    if(strict) {
      throw new UnsupportedOperationException("Not supported");
    }
  }

  @Override
  public void setSelected(boolean selected) {
    if(strict) {
      throw new UnsupportedOperationException("Not supported");
    }
    super.setSelected(selected);
  }

  @Override
  public void setOnTouchListener(OnTouchListener l) {
    this.customOnTouchListener = l;
  }
  
  public float getCenterX() {
    return centerX;
  }
  
  public float getCenterY() {
    return centerY;
  }
  
  public boolean isLandscape() {
    return getImageWidth() >= getImageHeight();
  }
  
  public boolean isPortrait() {
    return getImageWidth() <= getImageHeight();
  }
  
  public void setStartingScale(float startingScale) {
    this.startingScale = startingScale;
  }
  
  public void setStartingPosition(float x, float y) {
    this.startX = x;
    this.startY = y;
  }

  @Override
  public void setOnClickListener(OnClickListener l) {
    this.onClickListener = l;
    
    if(gestureImageViewTouchListener != null) {
      gestureImageViewTouchListener.setOnClickListener(l);
    }
  }

  /**
   * Returns true if the image dimensions are aligned with the orientation of the device.
   * @return
   */
  public boolean isOrientationAligned() {
    if(deviceOrientation == Configuration.ORIENTATION_LANDSCAPE) {
      return isLandscape();
    }
    else if(deviceOrientation == Configuration.ORIENTATION_PORTRAIT) {
      return isPortrait();
    }
    return true;
  }
  
  public int getDeviceOrientation() {
    return deviceOrientation;
  }
}




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