美文网首页
Android 图片双击缩放&拖动查看

Android 图片双击缩放&拖动查看

作者: RookieRun | 来源:发表于2020-04-13 23:36 被阅读0次

一.实现的效果

ScaleImageView_without_over_scroll.gif

二.思路

1.定义放大和缩小的系数
2.监听双击
3.监听手势滑动&fling

三.难点及解决

3.1.放大缩小级别的计算:
使用图片宽高比与控件宽高比做对比,
3.2.双击&手势滑动&fling:
交给GestureDetector&Scroller
Scroller与OverScroller存在速度上的区别,并且,OverScroller的可以实现过度滑动如:


image.png
ScaleImageView_with_over_scroll.gif

四.talk is cheap

import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.support.annotation.Nullable;
import android.support.v4.view.GestureDetectorCompat;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.widget.OverScroller;

import com.example.rookie.hencoder_plus.R;

/**
 * 支持双击放大/缩小&拖拽的ImageView
 * 1.定义大图&小图模式的系数
 * <p>
 * 2.增加放大缩小的过程动画
 * 3.使用GestureDetector处理双击&拖拽
 */
public class ScaleImageView extends View implements GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener {

    private static final float OVER_SCALE_FACTOR = 1.5f;
    private int width;
    private int height;
    private Bitmap bitmap;
    private float originalOffsetX, originalOffsetY;//初始偏移,保证图片在正中间
    private float offsetX, offsetY;
    Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private float bigModeScale, smallModeScale;
    GestureDetectorCompat gestureDetectorCompat;
    private float scaleFraction;
    private OverScroller overScroller;

    /**
     * 属性动画使用
     *
     * @return
     */
    private float getScaleFraction() {
        return scaleFraction;
    }

    /**
     * 属性动画使用
     *
     * @return
     */
    private void setScaleFraction(float scaleFraction) {
        this.scaleFraction = scaleFraction;
        invalidate();
    }

    public ScaleImageView(Context context) {
        this(context, null);
    }

    public ScaleImageView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ScaleImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        gestureDetectorCompat = new GestureDetectorCompat(getContext(), this);
        gestureDetectorCompat.setOnDoubleTapListener(this);
        bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.pic_tmac);
        overScroller = new OverScroller(getContext());
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        width = getWidth();
        height = getHeight();
        originalOffsetX = (width - bitmap.getWidth()) / 2f;
        originalOffsetY = (height - bitmap.getHeight()) / 2f;
        //计算缩放系数
        if (((float) bitmap.getWidth() / bitmap.getHeight()) > ((float) width / height)) {
            Log.e("test", "图片的宽高比更大--->");
            //图片的宽高比更大,那么较小的缩放模式就是以宽度的
            smallModeScale = ((float) width) / bitmap.getWidth();
            bigModeScale = ((float) height) / bitmap.getHeight() * OVER_SCALE_FACTOR;
        } else {
            //view的宽高比更大,图片应该放大以铺满整个屏幕
            Log.e("test", "控件的宽高比更大--->");
            smallModeScale = ((float) height) / bitmap.getHeight();
            bigModeScale = ((float) width) / bitmap.getWidth() * OVER_SCALE_FACTOR;
        }
    }

    private boolean bigMode;

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //将图片画到中间
        // canvas.translate((width - bitmap.getWidth()) / 2, (height - bitmap.getHeight()) / 2);
        //拖动所产生的偏移
        if (!bigMode) {
            canvas.save();
        }
        canvas.translate(offsetX, offsetY);
        if (!bigMode) {
            canvas.restore();
            offsetX = offsetY = 0;
        }
        float scale = smallModeScale + (bigModeScale - smallModeScale) * scaleFraction;
        canvas.scale(scale, scale, width / 2f, height / 2f);
        canvas.drawBitmap(bitmap, originalOffsetX, originalOffsetY, paint);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return gestureDetectorCompat.onTouchEvent(event);
    }

    @Override
    public boolean onDown(MotionEvent e) {
        if (overScroller != null && !overScroller.isFinished()) {
            overScroller.forceFinished(true);
        }
        return true;
    }

    @Override
    public void onShowPress(MotionEvent e) {

    }

    @Override
    public boolean onSingleTapUp(MotionEvent e) {
        return false;
    }

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
        //向下,向右移动,distanceY,distanceX为正值(这里的distance是旧位置-新位置所产生的位移)
//        Log.e("test", "distanceX:" + distanceX + "distanceY:" + distanceY);
        //只有大图模式才可以拖动
        if (bigMode) {
            //注意边界值
            //放大后的bitmap的width
            final float overScaleBitmapWidth = bitmap.getWidth() * bigModeScale;
            final float overScaleBitmapHeight = bitmap.getHeight() * bigModeScale;
            offsetX = offsetX - distanceX;
            offsetX = Math.min(offsetX, (overScaleBitmapWidth - width) / 2);
            offsetX = Math.max(offsetX, -(overScaleBitmapWidth - width) / 2);

            offsetY = offsetY - distanceY;
            offsetY = Math.min(offsetY, (overScaleBitmapHeight - height) / 2);
            offsetY = Math.max(offsetY, -(overScaleBitmapHeight - height) / 2);
        }

        invalidate();
        return false;
    }


    @Override
    public void onLongPress(MotionEvent e) {

    }

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        //以当前的偏移点offsetX,offsetY为参考点
        //这里的overScroller 的8个参数的最后两个参数是额外的超出滑动的区域,类似与ios的弹性滑动
        int overScrollDistance = 0;
        overScroller.fling(((int) offsetX), ((int) offsetY), ((int) velocityX), ((int) velocityY),
                -(((int) (bitmap.getWidth() * bigModeScale)) - width) / 2,
                (((int) (bitmap.getWidth() * bigModeScale)) - width) / 2,
                -(((int) (bitmap.getHeight() * bigModeScale)) - height) / 2,
                (((int) (bitmap.getHeight() * bigModeScale)) - height) / 2, overScrollDistance, overScrollDistance);
        invalidate();
        return false;
    }

    @Override
    public void computeScroll() {
        super.computeScroll();
        if (overScroller.computeScrollOffset() && bigMode) {
            //当前的x,y即offsetX,offsetY
            offsetX = overScroller.getCurrX();
            offsetY = overScroller.getCurrY();
//            Log.e("test", "currX:" + currX + "->currY:" + currY);
//            Log.e("test", "offsetX:" + offsetX + "->offsetY:" + offsetY);
            invalidate();
        }
    }

    @Override
    public boolean onSingleTapConfirmed(MotionEvent e) {
        return false;
    }

    @Override
    public boolean onDoubleTap(MotionEvent e) {
        Log.e("test", "onDoubleTap--->");
        bigMode = !bigMode;
        if (bigMode) {
            getScaleAnimator().start();
        } else {
            getScaleAnimator().reverse();
        }
        return false;
    }

    @Override
    public boolean onDoubleTapEvent(MotionEvent e) {
        return false;
    }

    ObjectAnimator scaleAnimator;

    ObjectAnimator getScaleAnimator() {
        if (scaleAnimator == null) {
            scaleAnimator = ObjectAnimator.ofFloat(this, "scaleFraction", 0, 1);
        }
        return scaleAnimator;
    }
}

相关文章

网友评论

      本文标题:Android 图片双击缩放&拖动查看

      本文链接:https://www.haomeiwen.com/subject/doqamhtx.html