美文网首页
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