• 图片缩放和多点触控


    实现图片的缩放并不难,主要需要一些计算和对图片的平移及缩放操作

    主布局:

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/LinearLayout1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context="com.example.zoomimageview.MainActivity" >
    
        <com.view.ZoomImageView
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:scaleType="matrix"
            android:src="@drawable/m3" />
    
    </LinearLayout>

    上面使用自定义View 

    如下:

    package com.view;
    
    import android.content.Context;
    import android.graphics.Matrix;
    import android.graphics.RectF;
    import android.graphics.drawable.Drawable;
    import android.util.AttributeSet;
    import android.view.MotionEvent;
    import android.view.ScaleGestureDetector;
    import android.view.ScaleGestureDetector.OnScaleGestureListener;
    import android.view.View;
    import android.view.View.OnTouchListener;
    import android.view.ViewTreeObserver.OnGlobalLayoutListener;
    import android.widget.ImageView;
    import android.widget.SectionIndexer;
    
    public class ZoomImageView extends ImageView implements OnGlobalLayoutListener,
            OnScaleGestureListener, OnTouchListener {
        private boolean once;
        // 缩放得最小值
        private float minScale;
        // 双击放大值
        private float doubleTouch;
        // 缩放最大值
        private float maxScale;
    
        private Matrix matrix;
        // 控件的宽高
        private float width;
        private float height;
    
        private ScaleGestureDetector scaleGestureDetector;
    
        public ZoomImageView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            matrix = new Matrix();
            setScaleType(ScaleType.MATRIX);
            scaleGestureDetector = new ScaleGestureDetector(context, this);
            setOnTouchListener(this);
        }
    
        public ZoomImageView(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public ZoomImageView(Context context) {
            this(context, null);
        }
    
        public void onGlobalLayout() {
            if (!once) {
                // 得到控件的宽和高
                width = getWidth();
                height = getHeight();
                // 得到资源的宽和高
                Drawable drawable = getDrawable();
                int imgWidth = drawable.getIntrinsicWidth();
                int imgHeight = drawable.getIntrinsicHeight();
    
                float scale = 1.0f;
    
                if (imgWidth > width && imgHeight < height) {
                    scale = width * 1.0f / imgWidth;
                }
    
                if (imgWidth < width && imgHeight > height) {
                    scale = height * 1.0f / imgHeight;
                }
    
                if ((imgWidth > width && imgHeight > height)
                        || (imgWidth < width && imgHeight < height)) {
                    scale = Math.min(width * 1.0f / imgWidth, height * 1.0f
                            / imgHeight);
                }
                minScale = scale;
                maxScale = scale * 4;
    // 偏移量
                float dx = width * 1 / 2 - imgWidth * 1 / 2;
                float dy = height * 1 / 2- imgHeight * 1 / 2;
                // 将图片移动到控件的中心
                matrix.postTranslate(dx, dy);
                // 缩放
                matrix.postScale(minScale, minScale, width * 1 / 2, height * 1 / 2);
                setImageMatrix(matrix);
    
                once = true;
    
            }
    
        }
    
        @Override
        protected void onAttachedToWindow() {
            super.onAttachedToWindow();
            getViewTreeObserver().addOnGlobalLayoutListener(this);
    
        }
    
        @Override
        protected void onDetachedFromWindow() {
            super.onDetachedFromWindow();
            getViewTreeObserver().removeGlobalOnLayoutListener(this);
        }
    
        // 取得缩放值
        private float getScale() {
            float values[] = new float[9];
            matrix.getValues(values);
            return values[Matrix.MSCALE_X];
        }
    
        // 将缩放的图片放到矩形中 来获得缩放之后图片的宽高
        private RectF getMatrixRectF() {
            Matrix newmMatrix = matrix;
            RectF rectf = new RectF();
            Drawable drawable = getDrawable();
            if (drawable != null) {
                rectf.set(0, 0, drawable.getIntrinsicWidth(),
                        drawable.getIntrinsicHeight());
                newmMatrix.mapRect(rectf);
            }
            return rectf;
        }
    
        // 对缩放的位置及边界控制
        private void controlScaleImgState() {
            // 边界空白消除:
            RectF rectf = getMatrixRectF();
            float transX = 0;
            float transY = 0;
            if (width <= rectf.width()) {
                if (rectf.left > 0) {
                    transX = -rectf.left;
                }
                if (rectf.right < width) {
                    transX = width - rectf.right;
                }
            }
            if (height <= rectf.height()) {
                if (rectf.top > 0) {
                    transY = rectf.top;
                }
                if (rectf.bottom < height) {
                    transY = height - rectf.bottom;
                }
    
            }
            
            // 控制居中 当图片小于屏幕
            if (width > rectf.width()) {
                transX = width * 1 / 2 - rectf.right + rectf.width() * 1 / 2;
            }
            if (height > rectf.height()) {
                transY = height * 1 / 2 - rectf.bottom + rectf.height() * 1 / 2;
            }
    //设置平移
            matrix.postTranslate(transX, transY);
    
        }
    
        // onScaleGestureListener 需要复写的方法
        public boolean onScale(ScaleGestureDetector detector) {
            // 取得当前缩放值
            float scale = getScale();
            // 取得根据手指判断的缩放值
            float scaleFactor = detector.getScaleFactor();
            if (getDrawable() == null) {
                return true;
            }
            // 如果在放大 当前缩放值不大于最大缩放值 或者 如果在缩小 当前缩放值不小于最小缩放值
            if ((scale < maxScale && scaleFactor > 1.0f)
                    || (scale > minScale && scaleFactor < 1.0f)) {
                // 如果乘积大于最大值 则设为最大值
                if (scale * scaleFactor > maxScale) {
                    scaleFactor = maxScale / scale;
                }
                // 如果乘积小于最小值 则设为最小值
                if (scale * scaleFactor < minScale) {
                    scaleFactor = minScale / scale;
                }
    
                controlScaleImgState();
    
                matrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(),
                        detector.getFocusX());
                setImageMatrix(matrix);
    
            }
    
            return true;
        }
    
        public boolean onScaleBegin(ScaleGestureDetector detector) {
            return true;
        }
    
        public void onScaleEnd(ScaleGestureDetector detector) {
    
        }
    
        // onTouchListener 复写的方法
        public boolean onTouch(View v, MotionEvent event) {
            scaleGestureDetector.onTouchEvent(event);
            return true;
        }
    
    }

    说一下上面实现的大致过程:

    1、首先继承了ImageView   实现了 OnGlobalLayoutListener 接口

      为什么要实现OnGlobalLayoutListener:

         当一个视图树的布局发生改变时,可以被ViewTreeObserver监听到, 这是一个注册监听视图树的观察者(observer),在视图树的全局事件改变时得到通知。ViewTreeObserver不能直接实例化,而是通过 getViewTreeObserver()获得。 在oncreate中View.getWidth和View.getHeight无法获得一个view的高度和宽度,这是因为View组件布局要 在onResume回调后完成。所以现在需要使用getViewTreeObserver().addOnGlobalLayoutListener() 来获得宽度或者高度。这是获得一个view的宽度和高度的方法之一。

    2、之后又实现了 OnScaleGestureListener 和 OnTouchListener 接口

      说一下它们的作用:

       为View创建scaleGestureDetector,它会提供多点触摸在的手势变化信息,实例化它时需要OnScaleGestureListener 作为参数。callback方法ScaleGestureDetector.OnScaleGestureListener 会在特定手势事件时发出通知。而该类需要和Touch事件引发的MotionEvent配合使用。所以需要实现OnTouchListener 接口,来侦测多点触控。

      因此,需要在public boolean onTouch(View v, MotionEvent event) 方法中

      调用scaleGestureDetector.onTouchEvent(event) 将MotionEvent 传出;

    3、onGlobalLayout() 

     实现OnTouchListener 接口时,需要复写onGlobalLayout() ,在这个方法中计算了缩放值和平移值

    来保证初始化视图的大小及位置

    4、onAttachedToWindow()和onDetachedFromWindow()

      用来添加和移除OnGlobalLayoutListener(this)

    5、onScale(ScaleGestureDetector detector)  

      onScaleonScaleBegin 、onScaleEnd是实现OnScaleGestureListener接口需要复写的

      根据detector.getScaleFactor()所返回的值来进行缩放。

     

  • 相关阅读:
    Java Varargs 可变参数使用
    GitLab 如何删除 Forked from
    Git Clone 的时候遇到 Filename too long 错误
    Spring Boot 如何部署到 Linux 中的服务
    Gradle 如何打包 Spring Boot 如何不添加版本代码
    HDU 5878---预处理+二分查找
    HDU 5881--Tea 思维规律
    HDU 5879---cure
    (转)C/S 与 B/S 区别
    卡特兰数
  • 原文地址:https://www.cnblogs.com/mydomainlistentome/p/4748812.html
Copyright © 2020-2023  润新知