Android-相册效果(图片缩放 自由滑动)

先上图:

很多时候 我们会有这么一个需求:

展示一组图片 每个Item的图片 可以自由拉伸 滑动 焦点不冲突

网上有很多实现方法 通过自定义Gallery和ImageView来实现

个人不是很推荐 在这里推荐有ViewPager加ZoomImageView

ViewPager就不过多解释 主要是ZoomImageView

上代码:

public class ZoomImageView extends ImageView implements OnTouchListener, OnGestureListener{
    private int WIDTH;
    private int HEIGHT;
    private int mStatusHeight;
    
    private static final String TAG = ZoomImageView.class.getSimpleName();

    // This is the base transformation which is used to show the image
    // initially. The current computation for this shows the image in
    // it's entirety, letterboxing as needed. One could choose to
    // show the image as cropped instead.
    //
    // This matrix is recomputed when we go from the thumbnail image to
    // the full size image.
    protected Matrix mBaseMatrix = new Matrix();

    // This is the supplementary transformation which reflects what
    // the user has done in terms of zooming and panning.
    //
    // This matrix reHonagAnGalleryActivitys the same when we go from the
    // thumbnail image
    // to the full size image.
    protected Matrix mSuppMatrix = new Matrix();

    // This is the final matrix which is computed as the concatentation
    // of the base matrix and the supplementary matrix.
    private final Matrix mDisplayMatrix = new Matrix();

    // Temporary buffer used for getting the values out of a matrix.
    private final float[] mMatrixValues = new float[9];

    // The current bitmap being displayed.
    // protected final RotateBitmap mBitmapDisplayed = new RotateBitmap(null);
    protected Bitmap image = null;

    int mThisWidth = -1, mThisHeight = -1;

    float mMaxZoom = 4.0f;
    float mMinZoom = 0.1f;

    private int imageWidth;
    private int imageHeight;

    private float scaleRate;

    public ZoomImageView(Context context) {
        super(context);
        init();
    }

    public ZoomImageView(Context context, int imageWidth, int imageHeight) {
        super(context);
        this.imageHeight = imageHeight;
        this.imageWidth = imageWidth;
        init();
    }

    public ZoomImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void arithScaleRate() {
        float scaleWidth = WIDTH / (float) imageWidth;
        float scaleHeight = HEIGHT / (float) imageHeight;
        scaleRate = Math.min(scaleWidth, scaleHeight);
        if (scaleRate > 1) {
            scaleRate = 1f;
        }
    }

    public float getScaleRate() {
        return scaleRate;
    }

    public int getImageWidth() {
        return imageWidth;
    }

    public void setImageWidth(int imageWidth) {
        this.imageWidth = imageWidth;
    }

    public int getImageHeight() {
        return imageHeight;
    }

    public void setImageHeight(int imageHeight) {
        this.imageHeight = imageHeight;
    }

    protected Handler mHandler = new Handler();

    @Override
    public void setImageResource(int resId) {
        Bitmap bit = BitmapFactory.decodeResource(getResources(), resId);
        setImageBitmap(bit);
    }

    @Override
    public void setImageBitmap(Bitmap bitmap) {
        super.setImageBitmap(bitmap);
        this.imageHeight = bitmap.getHeight();
        this.imageWidth = bitmap.getWidth();
        image = bitmap;
        requestImage();
    }

    public void requestImage() {
        arithScaleRate();
        zoomTo(scaleRate, WIDTH / 2f, WIDTH / 2f);
        layoutToCenter();
    }

    // Center as much as possible in one or both axis. Centering is
    // defined as follows: if the image is scaled down below the
    // view's dimensions then center it (literally). If the image
    // is scaled larger than the view and is translated out of view
    // then translate it back into view (i.e. eliminate black bars).
    protected void center(boolean horizontal, boolean vertical) {
        // if (mBitmapDisplayed.getBitmap() == null) {
        // return;
        // }
        if (image == null) {
            return;
        }

        Matrix m = getImageViewMatrix();

        RectF rect = new RectF(0, 0, image.getWidth(), image.getHeight());
        // RectF rect = new RectF(0, 0, imageWidth*getScale(),
        // imageHeight*getScale());

        m.mapRect(rect);

        float height = rect.height();
        float width = rect.width();

        float deltaX = 0, deltaY = 0;

        if (vertical) {
            int viewHeight = getHeight();
            if (height < viewHeight) {
                deltaY = (viewHeight - height) / 2 - rect.top;
            } else if (rect.top > 0) {
                deltaY = -rect.top;
            } else if (rect.bottom < viewHeight) {
                deltaY = getHeight() - rect.bottom;
            }
        }

        if (horizontal) {
            int viewWidth = getWidth();
            if (width < viewWidth) {
                deltaX = (viewWidth - width) / 2 - rect.left;
            } else if (rect.left > 0) {
                deltaX = -rect.left;
            } else if (rect.right < viewWidth) {
                deltaX = viewWidth - rect.right;
            }
        }
        postTranslate(deltaX, deltaY);
        setImageMatrix(getImageViewMatrix());
    }
    
    public void setStatusHeight(int height) {
        HEIGHT -= height;
    }

    private void init() {
        // except the height of the top bar
        DisplayMetrics dm = getResources().getDisplayMetrics();
        WIDTH = dm.widthPixels;
        HEIGHT = dm.heightPixels;
        
//        HEIGHT -= getResources().getDimension(R.dimen.top_bar_height);
        setScaleType(ImageView.ScaleType.MATRIX);
        setOnTouchListener(this);
        
        mDetector = new GestureDetector(getContext(), this);
    }

    public void layoutToCenter() {
        float width = imageWidth * getScale();
        float height = imageHeight * getScale();

        float fill_width = WIDTH - width;
        float fill_height = HEIGHT - height;

        float tran_width = 0f;
        float tran_height = 0f;

        if (fill_width > 0)
            tran_width = fill_width / 2;
        if (fill_height > 0)
            tran_height = fill_height / 2;

        float v[] = new float[9];
        Matrix m = getImageMatrix();
        m.getValues(v);
        float left = v[Matrix.MTRANS_X];
        float top = v[Matrix.MTRANS_Y];

        tran_width -= left;
        tran_height -= top;

        postTranslate(tran_width, tran_height);
        // setImageMatrix(getImageViewMatrix());
    }

    protected float getValue(Matrix matrix, int whichValue) {
        matrix.getValues(mMatrixValues);
        // mMinZoom = (BrowsePictureActivity.screenWidth / 2f) / imageWidth;
        mMinZoom = 0;
        return mMatrixValues[whichValue];
    }

    // Get the scale factor out of the matrix.
    protected float getScale(Matrix matrix) {
        return getValue(matrix, Matrix.MSCALE_X);
    }

    protected float getScale() {
        return getScale(mSuppMatrix);
    }

    // Combine the base matrix and the supp matrix to make the final matrix.
    protected Matrix getImageViewMatrix() {
        // The final matrix is computed as the concatentation of the base matrix
        // and the supplementary matrix.
        mDisplayMatrix.set(mBaseMatrix);
        mDisplayMatrix.postConcat(mSuppMatrix);
        return mDisplayMatrix;
    }

    static final float SCALE_RATE = 1.25F;

    // 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%.
    protected float maxZoom() {
        if (image == null) {
            return 1F;
        }

        float fw = (float) image.getWidth() / (float) mThisWidth;
        float fh = (float) image.getHeight() / (float) mThisHeight;
        float max = Math.max(fw, fh) * 4;
        return max;
    }

    protected void zoomTo(float scale, float centerX, float centerY) {
        zoomTo(scale, centerX, centerY, true);
    }

    protected void zoomTo(float scale, float centerX, float centerY,
            boolean limit) {
        if (limit) {
            if (scale > mMaxZoom) {
                scale = mMaxZoom;
            } else if (scale < mMinZoom) {
                scale = mMinZoom;
            }
        }

        float oldScale = getScale();
        float deltaScale = scale / oldScale;

        mSuppMatrix.postScale(deltaScale, deltaScale, centerX, centerY);
        setImageMatrix(getImageViewMatrix());
        center(true, true);
    }

    protected void zoomTo(final float scale, final float centerX,
            final float centerY, final float durationMs) {
        final float incrementPerMs = (scale - getScale()) / durationMs;
        final float oldScale = getScale() + mMinZoom;
        final long startTime = System.currentTimeMillis();

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

    protected void zoomBigToSmall(final float scale, final float centerX,
            final float centerY, final float durationMs) {
        final float incrementPerMs = (scale - getScale()) / durationMs;
        final float oldScale = getScale() + mMaxZoom;
        final long startTime = System.currentTimeMillis();

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

    protected void zoomTo(float scale) {
        float cx = getWidth() / 2F;
        float cy = getHeight() / 2F;

        zoomTo(scale, cx, cy);
    }

    protected void zoomToPoint(float scale, float pointX, float pointY) {
        float cx = getWidth() / 2F;
        float cy = getHeight() / 2F;

        panBy(cx - pointX, cy - pointY);
        zoomTo(scale, cx, cy);
    }

    protected void zoomIn() {
        zoomIn(SCALE_RATE);
    }

    protected void zoomOut() {
        zoomOut(SCALE_RATE);
    }

    protected void zoomIn(float rate) {
        if (getScale() >= mMaxZoom) {
            return; // Don't let the user zoom into the molecular level.
        } else if (getScale() <= mMinZoom) {
            return;
        }
        if (image == null) {
            return;
        }

        float cx = getWidth() / 2F;
        float cy = getHeight() / 2F;

        mSuppMatrix.postScale(rate, rate, cx, cy);
        setImageMatrix(getImageViewMatrix());
    }

    protected void zoomOut(float rate) {
        if (image == null) {
            return;
        }

        float cx = getWidth() / 2F;
        float cy = getHeight() / 2F;

        // Zoom out to at most 1x.
        Matrix tmp = new Matrix(mSuppMatrix);
        tmp.postScale(1F / rate, 1F / rate, cx, cy);

        if (getScale(tmp) < 1F) {
            mSuppMatrix.setScale(1F, 1F, cx, cy);
        } else {
            mSuppMatrix.postScale(1F / rate, 1F / rate, cx, cy);
        }
        setImageMatrix(getImageViewMatrix());
        center(true, true);
    }

    public void postTranslate(float dx, float dy) {
        mSuppMatrix.postTranslate(dx, dy);
        setImageMatrix(getImageViewMatrix());
    }

    private float mdy = 0.0f;

    protected void postTransVerticalDuration(final float dy,
            final float durationMs) {
        final float incrementPerMs = dy / durationMs;
        final long startTime = System.currentTimeMillis();
        mdy = 0.0f;
        mHandler.post(new Runnable() {
            public void run() {
                long now = System.currentTimeMillis();
                float currentMs = Math.min(durationMs, now - startTime);

                postTranslate(0, incrementPerMs * currentMs - mdy);
                mdy = incrementPerMs * currentMs;

                if (currentMs < durationMs) {
                    mHandler.post(this);
                }
            }
        });
    }

    private float mdx = 0.0f;

    protected void postTransHorizontalDuration(final float dx,
            final float durationMs) {
        final float incrementPerMs = dx / durationMs;
        final long startTime = System.currentTimeMillis();
        mdx = 0.0f;
        mHandler.post(new Runnable() {
            public void run() {
                long now = System.currentTimeMillis();
                float currentMs = Math.min(durationMs, now - startTime);

                postTranslate(incrementPerMs * currentMs - mdx, 0);
                mdx = incrementPerMs * currentMs;

                if (currentMs < durationMs) {
                    mHandler.post(this);
                }
            }
        });
    }

    protected void panBy(float dx, float dy) {
        postTranslate(dx, dy);
        setImageMatrix(getImageViewMatrix());
    }

    private float mDeltaX, mDeltaY;

    private float sx, sy;
    private float width, height;
    private float values[] = new float[9];
    private float baseValue;
    private float originalScale;

    private final int NONE = 0;
    private final int DRAG = 1;
    private final int ZOOM = 2;
    private int mMod = 0;

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        if (mDetector != null) {
            mDetector.onTouchEvent(event);
        }
        final ZoomImageView imageView = this;
        final int sWidth = WIDTH;
        final int sHeight = HEIGHT;
        width = imageView.getScale() * imageView.getImageWidth();
        height = imageView.getScale() * imageView.getImageHeight();

        Matrix matrix = imageView.getImageMatrix();
        matrix.getValues(values);
        float x = event.getX();
        float y = event.getY();
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN:
            
            mMod = DRAG;

            baseValue = 0;
            originalScale = imageView.getScale();

            mDeltaX = 0;
            mDeltaY = 0;
            sx = x;
            sy = y;
            break;
        case MotionEvent.ACTION_POINTER_DOWN:
            
            mMod = ZOOM;
            break;
        case MotionEvent.ACTION_MOVE:
            
            if (event.getPointerCount() == 2) {
                mMod = ZOOM;
            }
            float deltaX = sx - x;
            float deltaY = sy - y;
            sx = x;
            sy = y;
            if (deltaX != 0.0f) {
                mDeltaX = deltaX;
            }

            if (deltaY != 0.0f) {
                mDeltaY = deltaY;
            }

            switch (mMod) {
            case DRAG:
                final float left = values[Matrix.MTRANS_X];
                final float right = left + width;
                
                if(right < sWidth && Math.abs(right - sWidth) >= 0.0001f && right < width) { 
                    v.getParent().requestDisallowInterceptTouchEvent(false);
                } 
                else if(right > width) {
                    v.getParent().requestDisallowInterceptTouchEvent(false);
                } 
                else {
                    v.getParent().requestDisallowInterceptTouchEvent(true);
                }
                
                
                if (width < sWidth) {
                    deltaX = 0.0f;
                    mDeltaX = deltaX;
                }

                if (height < sHeight) {
                    deltaY = 0.0f;
                    mDeltaY = deltaY;
                }
                
                
                imageView.postTranslate(-deltaX, -deltaY);
                break;
            case ZOOM:
                if (event.getPointerCount() < 2) {
                    return true;
                }

                float dx = event.getX(0) - event.getX(1);
                float dy = event.getY(0) - event.getY(1);
                float value = FloatMath.sqrt(dx * dx + dy * dy);
                if (baseValue == 0) {
                    baseValue = value;
                } else {
                    float scale = value / baseValue;
                    // scale the image
                    imageView.zoomTo(originalScale * scale,
                            (event.getX(0) + event.getX(1)) / 2,
                            (event.getY(0) + event.getY(1)) / 2, false);
                }
                break;
            default:
            }
            break;
        case MotionEvent.ACTION_UP:
        case MotionEvent.ACTION_CANCEL:
                    
            final float left = values[Matrix.MTRANS_X];
            final float right = left + width;
            final float top = values[Matrix.MTRANS_Y];
            final float bottom = top + height;
            
            switch (mMod) {
            case DRAG:
                final float startX = mDeltaX;
                final float startY = mDeltaY;
                if (startX != 0) { 
                    if (right < sWidth) {
//                        ((ViewPager) v.getParent()).requestDisallowInterceptTouchEvent(false);
                        postTransHorizontalDuration(sWidth - right, 200.0f);
                    }

                    if (left >= 0) {
                        postTransHorizontalDuration(-left, 200.0f);
                    }
                }

                if (startY != 0) {
                    if (bottom < sHeight) {
                        postTransVerticalDuration(sHeight - bottom, 200.0f);
                    }

                    if (top > 0) {
                        postTransVerticalDuration(-top, 200.0f);
                    }
                }

                break;
            case ZOOM:
                final float sr = getScaleRate();
                final float scale = getScale();
                mDoubleClick = false;
                if (sr > scale) {
                    imageView.zoomTo(sr, sWidth / 2, sHeight / 2, 200.0f);
                    return true;
                } else if (mMaxZoom < scale) {
                    imageView.zoomBigToSmall(sr, sWidth / 2, sHeight / 2,
                            300.0f);
                    return true;
                }
                break;
            }

            mMod = NONE;
            break;
        default:
        }

        return true;
    }

    private boolean mDoubleClick;
    private GestureDetector mDetector;
    private OnSimpleListener listener;

    public void doubleClick() {
        float scale = getScale();
        if (!mDoubleClick) { 
            zoomTo(1.0f);
            mDoubleClick = true;
        } else if (scale == 1.0f) {
            zoomTo(2.0f);
        } else if (scale == 2.0f) {
            zoomTo(4.0f);
        } else if (scale == 4.0f) {
            zoomTo(1.0f);
        }
    }

    @Override
    public boolean onDown(MotionEvent e) {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public void onShowPress(MotionEvent e) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public boolean onSingleTapUp(MotionEvent e) {
        // TODO Auto-generated method stub
        if(listener != null) {
            listener.setOnSimpleClickListenr();
        }
        return true;
    }

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
            float distanceY) {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public void onLongPress(MotionEvent e) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
            float velocityY) {
        // TODO Auto-generated method stub
        return false;
    }

    public interface OnSimpleListener {
        void setOnSimpleClickListenr();
    }
    
    public void setOnSimpleClickListenr(OnSimpleListener listener) {
        this.listener = listener;
    }
    
}

为了不和ViewPager的滑动冲突 主要是在action_move里面加了一个判断:

                final float left = values[Matrix.MTRANS_X];
                final float right = left + width;
                
                if(right < sWidth && Math.abs(right - sWidth) >= 0.0001f && right < width) { 
                    v.getParent().requestDisallowInterceptTouchEvent(false);
                } 
                else if(right > width) {
                    v.getParent().requestDisallowInterceptTouchEvent(false);
                } 
                else {
                    v.getParent().requestDisallowInterceptTouchEvent(true);
                }

需要源码的M我~~~

 

posted @ 2014-01-16 10:09  暗殇  阅读(976)  评论(0编辑  收藏  举报