FileDocCategorySizeDatePackage
ImageViewTouchBase.javaAPI DocAndroid 1.5 API19513Wed May 06 22:42:42 BST 2009com.android.camera

ImageViewTouchBase

public abstract class ImageViewTouchBase extends android.widget.ImageView

Fields Summary
private static final String
TAG
private final boolean
USE_PERFECT_FIT_OPTIMIZATION
protected android.graphics.Matrix
mBaseMatrix
protected android.graphics.Matrix
mSuppMatrix
private android.graphics.Matrix
mDisplayMatrix
private float[]
mMatrixValues
protected android.graphics.Bitmap
mBitmapDisplayed
protected android.graphics.Bitmap
mThumbBitmap
private android.graphics.Bitmap
mFullBitmap
protected android.graphics.Bitmap
mPerfectFitBitmap
protected boolean
mBitmapIsThumbnail
protected boolean
mIsZooming
protected android.graphics.Paint
mPaint
static boolean
sNewZoomControl
int
mThisWidth
int
mThisHeight
float
mMaxZoom
protected android.os.Handler
mHandler
protected int
mLastXTouchPos
protected int
mLastYTouchPos
private Runnable
mOnLayoutRunnable
static final float
sPanRate
static final float
sScaleRate
Constructors Summary
public ImageViewTouchBase(android.content.Context context)

        super(context);
        init();
    
public ImageViewTouchBase(android.content.Context context, android.util.AttributeSet attrs)

        super(context, attrs);
        init();
    
Methods Summary
protected voidcenter(boolean vertical, boolean horizontal, boolean animate)

        if (mBitmapDisplayed == null)
            return;

        Matrix m = getImageViewMatrix();

        float [] topLeft  = new float[] { 0, 0 };
        float [] botRight = new float[] { mBitmapDisplayed.getWidth(), mBitmapDisplayed.getHeight() };

        translatePoint(m, topLeft);
        translatePoint(m, botRight);

        float height = botRight[1] - topLeft[1];
        float width  = botRight[0] - topLeft[0];

        float deltaX = 0, deltaY = 0;

        if (vertical) {
            int viewHeight = getHeight();
            if (height < viewHeight) {
                deltaY = (viewHeight - height)/2 - topLeft[1];
            } else if (topLeft[1] > 0) {
                deltaY = -topLeft[1];
            } else if (botRight[1] < viewHeight) {
                deltaY = getHeight() - botRight[1];
            }
        }

        if (horizontal) {
            int viewWidth = getWidth();
            if (width < viewWidth) {
                deltaX = (viewWidth - width)/2 - topLeft[0];
            } else if (topLeft[0] > 0) {
                deltaX = -topLeft[0];
            } else if (botRight[0] < viewWidth) {
                deltaX = viewWidth - botRight[0];
            }
        }

        postTranslate(deltaX, deltaY);
        if (animate) {
            Animation a = new TranslateAnimation(-deltaX, 0, -deltaY, 0);
            a.setStartTime(SystemClock.elapsedRealtime());
            a.setDuration(250);
            setAnimation(a);
        }
        setImageMatrix(getImageViewMatrix());
    
public voidclear()

        mBitmapDisplayed = null;
        recycleBitmaps();
    
public voidcopyFrom(com.android.camera.ImageViewTouchBase other)

        mSuppMatrix.set(other.mSuppMatrix);
        mBaseMatrix.set(other.mBaseMatrix);
        
        if (mThumbBitmap != null)
            mThumbBitmap.recycle();
        
        if (mFullBitmap != null) 
            mFullBitmap.recycle();
        
        // copy the data
        mThumbBitmap       = other.mThumbBitmap;
        mFullBitmap        = null;
        
        if (other.mFullBitmap != null)
            other.mFullBitmap.recycle();
        
        // transfer "ownership"
        other.mThumbBitmap = null;
        other.mFullBitmap  = null;
        other.mBitmapIsThumbnail = true;

        setImageMatrix(other.getImageMatrix());
        setScaleType(other.getScaleType());
        
        setImageBitmapResetBase(mThumbBitmap, true, true);
    
private java.lang.Stringdescribe(android.graphics.Bitmap b)

        StringBuilder sb = new StringBuilder();
        if (b == null) {
            sb.append("NULL");
        } else if (b.isRecycled()) {
            sb.append(String.format("%08x: RECYCLED", b.hashCode()));
        } else {
            sb.append(String.format("%08x: LIVE", b.hashCode()));
            sb.append(String.format("%d x %d (size == %d)", b.getWidth(), b.getHeight(), b.getWidth()*b.getHeight()*2));
        }
        return sb.toString();
    
protected booleandoesScrolling()

    
       
        return true;
    
public voiddump()

        if (Config.LOGV) {
            Log.v(TAG, "dump ImageViewTouchBase " + this);
            Log.v(TAG, "... mBitmapDisplayed  = " + describe(mBitmapDisplayed));
            Log.v(TAG, "... mThumbBitmap      = " + describe(mThumbBitmap));
            Log.v(TAG, "... mFullBitmap       = " + describe(mFullBitmap));
            Log.v(TAG, "... mPerfectFitBitmap = " + describe(mPerfectFitBitmap));
            Log.v(TAG, "... mIsThumb          = " + mBitmapIsThumbnail);
        }
    
protected android.graphics.MatrixgetImageViewMatrix()

        mDisplayMatrix.set(mBaseMatrix);
        mDisplayMatrix.postConcat(mSuppMatrix);
        return mDisplayMatrix;
    
protected floatgetScale(android.graphics.Matrix matrix)

        return getValue(matrix, Matrix.MSCALE_X);
    
protected floatgetScale()

        return getScale(mSuppMatrix);
    
protected floatgetTranslateX()

        return getValue(mSuppMatrix, Matrix.MTRANS_X);
    
protected floatgetTranslateY()

        return getValue(mSuppMatrix, Matrix.MTRANS_Y);
    
protected floatgetValue(android.graphics.Matrix matrix, int whichValue)

        matrix.getValues(mMatrixValues);
        return mMatrixValues[whichValue];
    
private voidinit()

        setScaleType(ImageView.ScaleType.MATRIX);
        mPaint.setDither(true);
        mPaint.setFilterBitmap(true);
    
static intmapXPoint(android.graphics.Matrix matrix, int point)

        // Matrix's mapPoints takes an array of x/y coordinates.
        // That's why we have to allocte an array of length two
        // even though we don't use the y coordinate.
        float [] xy = new float[2];
        xy[0] = point;
        xy[1] = 0F;
        matrix.mapPoints(xy);
        return (int) xy[0];
    
protected floatmaxZoom()


    // Sets the maximum zoom, which is a scale relative to the base matrix. It is calculated to show
    // the image at 400% zoom regardless of screen or image orientation. If in the future we decode
    // the full 3 megapixel image, rather than the current 1024x768, this should be changed down to
    // 200%.
       
        if (mBitmapDisplayed == null)
            return 1F;
        
        float fw = (float) mBitmapDisplayed.getWidth()  / (float)mThisWidth;
        float fh = (float) mBitmapDisplayed.getHeight() / (float)mThisHeight;
        float max = Math.max(fw, fh) * 4;
//        Log.v(TAG, "Bitmap " + mBitmapDisplayed.getWidth() + "x" + mBitmapDisplayed.getHeight() +
//                " view " + mThisWidth + "x" + mThisHeight + " max zoom " + max);
        return max;
    
public booleanonKeyDown(int keyCode, android.view.KeyEvent event)

        if (keyCode == KeyEvent.KEYCODE_BACK && getScale() > 1.0f) {
            // If we're zoomed in, pressing Back jumps out to show the entire image, otherwise Back
            // returns the user to the gallery.
            zoomTo(1.0f);
            return true;
        }
        return super.onKeyDown(keyCode, event);
    
protected voidonLayout(boolean changed, int left, int top, int right, int bottom)

    
    
                
        super.onLayout(changed, left, top, right, bottom);
        mThisWidth = right - left;
        mThisHeight = bottom - top;
        Runnable r = mOnLayoutRunnable;
        if (r != null) {
            mOnLayoutRunnable = null;
            r.run();
        }
        if (mBitmapDisplayed != null) {
            setBaseMatrix(mBitmapDisplayed, mBaseMatrix);
            setImageMatrix(getImageViewMatrix());
        }
    
private voidonZoom()

        mIsZooming = true;
        if (mFullBitmap != null && mFullBitmap != mBitmapDisplayed) {
            setImageBitmapResetBase(mFullBitmap, false, mBitmapIsThumbnail);
        }
    
protected voidpanBy(float dx, float dy)

        postTranslate(dx, dy);
        setImageMatrix(getImageViewMatrix());
    
protected voidpostTranslate(float dx, float dy)

        mSuppMatrix.postTranslate(dx, dy);
    
public voidrecycleBitmaps()

        if (mFullBitmap != null) {
            if (Config.LOGV)
                Log.v(TAG, "recycling mFullBitmap " + mFullBitmap + "; this == " + this.hashCode());
            mFullBitmap.recycle();
            mFullBitmap = null;
        }
        if (mThumbBitmap != null) {
            if (Config.LOGV)
                Log.v(TAG, "recycling mThumbBitmap" + mThumbBitmap + "; this == " + this.hashCode());
            mThumbBitmap.recycle();
            mThumbBitmap = null;
        }

        // mBitmapDisplayed is either mPerfectFitBitmap or mFullBitmap (in the case of zooming)
        setImageBitmap(null, true);
    
private voidsetBaseMatrix(android.graphics.Bitmap bitmap, android.graphics.Matrix matrix)

        float viewWidth = getWidth();
        float viewHeight = getHeight();

        matrix.reset();
        float widthScale = Math.min(viewWidth / (float)bitmap.getWidth(), 1.0f);
        float heightScale = Math.min(viewHeight / (float)bitmap.getHeight(), 1.0f);
        float scale;
        if (widthScale > heightScale) {
            scale = heightScale;
        } else {
            scale = widthScale;
        }
        matrix.setScale(scale, scale);
        matrix.postTranslate(
                (viewWidth  - ((float)bitmap.getWidth()  * scale))/2F, 
                (viewHeight - ((float)bitmap.getHeight() * scale))/2F);
    
public voidsetImageBitmap(android.graphics.Bitmap bitmap)

        throw new NullPointerException();
    
public voidsetImageBitmap(android.graphics.Bitmap bitmap, boolean isThumbnail)

        super.setImageBitmap(bitmap);
        Drawable d = getDrawable();
        if (d != null) 
            d.setDither(true);
        mBitmapDisplayed = bitmap;
        mBitmapIsThumbnail = isThumbnail;
    
public voidsetImageBitmapResetBase(android.graphics.Bitmap bitmap, boolean resetSupp, boolean isThumb)

    
               
        if ((bitmap != null) && (bitmap == mPerfectFitBitmap)) {
            // TODO: this should be removed in production
            throw new IllegalArgumentException("bitmap must not be mPerfectFitBitmap");
        }

        final int viewWidth = getWidth();
        final int viewHeight = getHeight();

        if (viewWidth <= 0)  {
            mOnLayoutRunnable = new Runnable() {
                public void run() {
                    setImageBitmapResetBase(bitmap, resetSupp, isThumb);
                }
            };
            return;
        }
        
        if (isThumb && mThumbBitmap != bitmap) {
            if (mThumbBitmap != null) {
                mThumbBitmap.recycle();
            }
            mThumbBitmap = bitmap;
        } else if (!isThumb && mFullBitmap != bitmap) {
            if (mFullBitmap != null) {
                mFullBitmap.recycle();
            }
            mFullBitmap = bitmap;
        }
        mBitmapIsThumbnail = isThumb;
        
        if (bitmap != null) {
            if (!usePerfectFitBitmap()) {
                setScaleType(ImageView.ScaleType.MATRIX);
                setBaseMatrix(bitmap, mBaseMatrix);
                setImageBitmap(bitmap, isThumb);
            } else {
                Matrix matrix = new Matrix();
                setBaseMatrix(bitmap, matrix);
                if ((mPerfectFitBitmap == null) || 
                        mPerfectFitBitmap.getWidth() != mThisWidth || 
                        mPerfectFitBitmap.getHeight() != mThisHeight) {
                    if (mPerfectFitBitmap != null) {
                        if (Config.LOGV)
                            Log.v(TAG, "recycling mPerfectFitBitmap " + mPerfectFitBitmap.hashCode());
                        mPerfectFitBitmap.recycle();
                    }
                    mPerfectFitBitmap = Bitmap.createBitmap(mThisWidth, mThisHeight, Bitmap.Config.RGB_565);
                }
                Canvas canvas = new Canvas(mPerfectFitBitmap);
                // clear the bitmap which may be bigger than the image and
                // contain the the previous image.
                canvas.drawColor(0xFF000000);

                final int bw = bitmap.getWidth();
                final int bh = bitmap.getHeight();
                final float widthScale  = Math.min(viewWidth / (float)bw, 1.0f);
                final float heightScale = Math.min(viewHeight/ (float)bh, 1.0f);
                int translateX, translateY;
                if (widthScale > heightScale) {
                    translateX = (int)((viewWidth -(float)bw*heightScale)*0.5f);
                    translateY = (int)((viewHeight-(float)bh*heightScale)*0.5f);
                } else {
                    translateX = (int)((viewWidth -(float)bw*widthScale)*0.5f);
                    translateY = (int)((viewHeight-(float)bh*widthScale)*0.5f);
                }

                android.graphics.Rect src = new android.graphics.Rect(0, 0, bw, bh);
                android.graphics.Rect dst = new android.graphics.Rect(
                        translateX, translateY,  
                        mThisWidth - translateX, mThisHeight - translateY);
                canvas.drawBitmap(bitmap, src, dst, mPaint);
                
                setImageBitmap(mPerfectFitBitmap, isThumb);
                setScaleType(ImageView.ScaleType.MATRIX);
                setImageMatrix(null);
            }
        } else {
            mBaseMatrix.reset();
            setImageBitmap(null, isThumb);
        }

        if (resetSupp)
            mSuppMatrix.reset();
        setImageMatrix(getImageViewMatrix());
        mMaxZoom = maxZoom();
    
public voidsetImageDrawable(android.graphics.drawable.Drawable d)

        super.setImageDrawable(d);
    
private static voidtranslatePoint(android.graphics.Matrix matrix, float[] xy)

        matrix.mapPoints(xy);
    
protected booleanusePerfectFitBitmap()

        return USE_PERFECT_FIT_OPTIMIZATION && !mIsZooming;
    
protected voidzoomIn()

        zoomIn(sScaleRate);
    
protected voidzoomIn(float rate)

        if (getScale() >= mMaxZoom) {
            return;     // Don't let the user zoom into the molecular level.
        }
        if (mBitmapDisplayed == null) {
            return;
        }
        float width = getWidth();
        float height = getHeight();

        mSuppMatrix.postScale(rate, rate, width/2F, height/2F);
        setImageMatrix(getImageViewMatrix());

        onZoom();
    
protected voidzoomOut()

        zoomOut(sScaleRate);
    
protected voidzoomOut(float rate)

        if (mBitmapDisplayed == null) {
            return;
        }
        
        float width = getWidth();
        float height = getHeight();

        Matrix tmp = new Matrix(mSuppMatrix);
        tmp.postScale(1F/sScaleRate, 1F/sScaleRate, width/2F, height/2F);
        if (getScale(tmp) < 1F) {
            mSuppMatrix.setScale(1F, 1F, width/2F, height/2F);
        } else {
            mSuppMatrix.postScale(1F/rate, 1F/rate, width/2F, height/2F);
        }
        setImageMatrix(getImageViewMatrix());
        center(true, true, false);

        onZoom();
    
protected voidzoomTo(float scale, float centerX, float centerY)

        if (scale > mMaxZoom) {
            scale = mMaxZoom;
        }
        onZoom();
        
        float oldScale = getScale();
        float deltaScale = scale / oldScale;

        mSuppMatrix.postScale(deltaScale, deltaScale, centerX, centerY);
        setImageMatrix(getImageViewMatrix());
        center(true, true, false);
    
protected voidzoomTo(float scale, float centerX, float centerY, float durationMs)

        final float incrementPerMs = (scale - getScale()) / durationMs;
        final float oldScale = getScale();
        final long startTime = System.currentTimeMillis();
        
        mHandler.post(new Runnable() {
            public void run() {
                long now = System.currentTimeMillis();
                float currentMs = Math.min(durationMs, (float)(now - startTime));
                float target = oldScale + (incrementPerMs * currentMs);
                zoomTo(target, centerX, centerY);
                
                if (currentMs < durationMs) {
                    mHandler.post(this);
                }
            }
        }); 
    
protected voidzoomTo(float scale)

        float width = getWidth();
        float height = getHeight();
        
        zoomTo(scale, width/2F, height/2F);