向 ImageView 添加缩放效果,例如图库图像

Adding azoom effect to an ImageView like gallery images

我有很多用 viewpager 和 picasso 制作的图片。我想在所有这些图像中实现缩放效果。

我的意思是,当我们 doubleclick/expand 在图库应用程序中用手指触摸照片并再次双击或用手指捏合时,照片就会回到自己的位置。

我想实现那个方法。我 watched/read 很多,但找不到适用于我用 picasso 和 viewpager 制作的所有图像的解决方案。

并且该方法应该兼容 API 19

这是mainactivty.javawhere 图片显示:

public class MainActivity extends AppCompatActivity {
    ViewPager viewPager;

    private int[] imageUrls = new int[]{
            R.raw.oooo,
            R.raw.o8,
            R.raw.oa,
            R.raw.oad,
            R.raw.oap,
            R.raw.ok,
            //there are many others
    };


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        viewPager = findViewById(R.id.view_pager);
        ViewPageAdapter adapter = new ViewPageAdapter(this, imageUrls);
        viewPager.setAdapter(adapter);
}

这是 ViewPagerAdapter:

public class ViewPageAdapter extends PagerAdapter {

    private Context context;
    private int[] imageUrls;

    ViewPageAdapter(Context context, int[] imageUrls) {
        this.context = context;
        this.imageUrls = imageUrls;
    }

    @Override
    public int getCount() {
        return imageUrls.length;
    }

    @Override
    public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
        return view == object;
    }

    @NonNull
    @Override
    public Object instantiateItem(@NonNull ViewGroup container, int position) {
        ImageView imageView = new ImageView(context);
                    Picasso.get()
                    .load(imageUrls[position])
                    .resize(400, 400)
                    .centerCrop()
                    .into(imageView);
            container.addView(imageView);
            return imageView;
        }

    @Override
    public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
        container.removeView((View) object);
    }
}

您可以创建自己的自定义 ImageView。我发现这很有用。

/**
 * Copyright 2012-2013 Jeremie Martinez (jeremiemartinez@gmail.com)
 * <p>
 * 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
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * 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.
 */


import android.content.Context;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.support.v7.widget.AppCompatImageView;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;

/**
 * @author jmartinez
 *
 *         Simple Android ImageView that enables dragging and zooming.
 *
 */
public class ZoomableImageView extends AppCompatImageView {

    private float maxScale = 3f;
    private float minScale = 1f;

    private enum State {
        INIT, DRAG, ZOOM
    }

    private State state;

    private Matrix matrix;
    private float[] finalTransformation = new float[9];
    private PointF last = new PointF();
    private float currentScale = 1f;

    private int viewWidth;
    private int viewHeight;
    private float afterScaleDrawableWidth;
    private float afterScaleDrawableHeight;

    private ScaleGestureDetector scaleDetector;

    private GestureDetector doubleTapDetecture;

    public ZoomableImageView(Context context) {
        super(context);
        setUp(context);
    }

    public ZoomableImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setUp(context);
    }

    public ZoomableImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        setUp(context);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        viewWidth = MeasureSpec.getSize(widthMeasureSpec);
        viewHeight = MeasureSpec.getSize(heightMeasureSpec);

        // Set up drawable at first load
        if (hasDrawable()) {
            resetImage();
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        scaleDetector.onTouchEvent(event);
        doubleTapDetecture.onTouchEvent(event);

        PointF current = new PointF(event.getX(), event.getY());

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                last.set(current);
                state = State.DRAG;
                break;

            case MotionEvent.ACTION_MOVE:
                if (state == State.DRAG) {
                    drag(current);
                    last.set(current);
                }
                break;

            case MotionEvent.ACTION_UP:
                state = State.INIT;
                break;

            case MotionEvent.ACTION_POINTER_UP:
                state = State.INIT;
                break;
        }

        setImageMatrix(matrix);
        invalidate();
        return true;
    }

    /**
     * Set up the class. Method called by constructors.
     *
     * @param context
     */
    private void setUp(Context context) {
        super.setClickable(false);
        matrix = new Matrix();
        state = State.INIT;
        scaleDetector = new ScaleGestureDetector(context, new ScaleListener());
        doubleTapDetecture = new GestureDetector(context, new GestureListener());
        setScaleType(ScaleType.MATRIX);
    }

    private void resetImage() {

        // Scale Image
        float scale = getScaleForDrawable();
        matrix.setScale(scale, scale);

        // Center Image
        float marginY = ((float) viewHeight - (scale * getDrawable().getIntrinsicHeight())) / 2;
        float marginX = ((float) viewWidth - (scale * getDrawable().getIntrinsicWidth())) / 2;
        matrix.postTranslate(marginX, marginY);

        afterScaleDrawableWidth = (float) viewWidth - 2 * marginX;
        afterScaleDrawableHeight = (float) viewHeight - 2 * marginY;

        setImageMatrix(matrix);
    }

    /**
     * Getter and setter for max/min scale. Default are min=1 and max=3
     */

    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;
    }

    /**
     * Drag method
     *
     * @param current
     *            Current point to drag to.
     */
    private void drag(PointF current) {
        float deltaX = getMoveDraggingDelta(current.x - last.x, viewWidth, afterScaleDrawableWidth * currentScale);
        float deltaY = getMoveDraggingDelta(current.y - last.y, viewHeight, afterScaleDrawableHeight * currentScale);
        matrix.postTranslate(deltaX, deltaY);
        limitDrag();
    }

    /**
     * Scale method for zooming
     *
     * @param focusX
     *            X of center of scale
     * @param focusY
     *            Y of center of scale
     * @param scaleFactor
     *            scale factor to zoom in/out
     */
    private void scale(float focusX, float focusY, float scaleFactor) {
        float lastScale = currentScale;
        float newScale = lastScale * scaleFactor;

        // Calculate next scale with resetting to max or min if required
        if (newScale > maxScale) {
            currentScale = maxScale;
            scaleFactor = maxScale / lastScale;
        } else if (newScale < minScale) {
            currentScale = minScale;
            scaleFactor = minScale / lastScale;
        } else {
            currentScale = newScale;
        }

        // Do scale
        if (requireCentering()) {
            matrix.postScale(scaleFactor, scaleFactor, (float) viewWidth / 2, (float) viewHeight / 2);
        } else
            matrix.postScale(scaleFactor, scaleFactor, focusX, focusY);

        limitDrag();
    }

    /**
     * This method permits to keep drag and zoom inside the drawable. It makes sure the drag is staying in bound.
     */
    private void limitDrag() {
        matrix.getValues(finalTransformation);
        float finalXTransformation = finalTransformation[Matrix.MTRANS_X];
        float finalYTransformation = finalTransformation[Matrix.MTRANS_Y];

        float deltaX = getScaleDraggingDelta(finalXTransformation, viewWidth, afterScaleDrawableWidth * currentScale);
        float deltaY = getScaleDraggingDelta(finalYTransformation, viewHeight, afterScaleDrawableHeight * currentScale);

        matrix.postTranslate(deltaX, deltaY);
    }

    private float getScaleDraggingDelta(float delta, float viewSize, float contentSize) {
        float minTrans = 0;
        float maxTrans = 0;

        if (contentSize <= viewSize) {
            maxTrans = viewSize - contentSize;
        } else {
            minTrans = viewSize - contentSize;
        }

        if (delta < minTrans)
            return minTrans - delta;
        else if (delta > maxTrans)
            return maxTrans - delta;
        else
            return 0;
    }

    // Check if dragging is still possible if so return delta otherwise return 0 (do nothing)
    private float getMoveDraggingDelta(float delta, float viewSize, float contentSize) {
        if (contentSize <= viewSize) {
            return 0;
        }
        return delta;
    }

    private float getScaleForDrawable() {
        float scaleX = (float) viewWidth / (float) getDrawable().getIntrinsicWidth();
        float scaleY = (float) viewHeight / (float) getDrawable().getIntrinsicHeight();
        return Math.min(scaleX, scaleY);
    }

    private boolean hasDrawable() {
        return getDrawable() != null && getDrawable().getIntrinsicWidth() != 0 && getDrawable().getIntrinsicHeight() != 0;
    }

    private boolean requireCentering() {
        return afterScaleDrawableWidth * currentScale <= (float) viewWidth || afterScaleDrawableHeight * currentScale <= (float) viewHeight;
    }

    private boolean isZoom() {
        return currentScale != 1f;
    }

    /**
     * Listener for detecting scale.
     *
     * @author jmartinez
     */
    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
        @Override
        public boolean onScaleBegin(ScaleGestureDetector detector) {
            state = State.ZOOM;
            return true;
        }

        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            scale(detector.getFocusX(), detector.getFocusY(), detector.getScaleFactor());
            return true;
        }
    }

    /**
     * Listener for double tap detection
     *
     * @author jmartinez
     */
    private class GestureListener extends GestureDetector.SimpleOnGestureListener {

        @Override
        public boolean onDoubleTap(MotionEvent e) {
            if (isZoom()) {
                resetImage();
                currentScale = 1f;
                state = State.INIT;

            } else {
                scale(e.getX(), e.getY(), maxScale);
            }
            return true;
        }

    }

}

那么您可以使用 ...yourpackage.ZoomableImageView

而不是 ImageView