Android自定义ImageView实现图片缩放滑动,双击放大缩小,多点触控缩放

Posted 胖子爱你520

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android自定义ImageView实现图片缩放滑动,双击放大缩小,多点触控缩放相关的知识,希望对你有一定的参考价值。

转载请注明出处:http://blog.csdn.net/woshizisezise/article/details/47445605

大家好,最近研究学习了一个自定义控件,ZoomImageView,安卓自定义缩放图片的控件,支持双击放大双击缩小,多点触控拉伸放大与缩小,并且很好的控制了图片的位置,不会出现缩小图片后图片左右出现空白的情况,缩小到默认值的时候,图片会居中填充窗口,可以推荐大家学习一下,先看下效果图,具体代码如下所示。

  • (1)首先在activity_main.xml布局文件中引用这个自定义控件的全路径名:

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    
        <com.example.zoomimageview.view.ZoomImageView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scaleType="matrix"
            android:src="@drawable/baby1" />
    
    </RelativeLayout>
    
  • (2)现在也是实现最核心的一步了,编写我们的ZoomImageView控件了,代码中注释都已经标的很清楚了,大家可以参照着看:

    package com.example.zoomimageview.view;
    
    import android.content.Context;
    import android.graphics.Matrix;
    import android.graphics.RectF;
    import android.graphics.drawable.Drawable;
    import android.support.v4.view.ViewPager;
    import android.util.AttributeSet;
    import android.view.GestureDetector;
    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.ViewConfiguration;
    import android.view.ViewTreeObserver.OnGlobalLayoutListener;
    import android.widget.ImageView;
    
    public class ZoomImageView extends ImageView implements OnGlobalLayoutListener,
            OnScaleGestureListener, OnTouchListener 
        private boolean mOnce;
    
        /**
         * 初始化时缩放的值
         */
        private float mInitScale;
    
        /**
         * 双击放大值到达的值
         */
        private float mMidScale;
    
        /**
         * 放大的最大值
         */
        private float mMaxScale;
    
        private Matrix mScaleMatrix;
    
        /**
         * 捕获用户多指触控时缩放的比例
         */
        private ScaleGestureDetector mScaleGestureDetector;
    
        // **********自由移动的变量***********
        /**
         * 记录上一次多点触控的数量
         */
        private int mLastPointerCount;
    
        private float mLastX;
        private float mLastY;
    
        private int mTouchSlop;
        private boolean isCanDrag;
    
        private boolean isCheckLeftAndRight;
        private boolean isCheckTopAndBottom;
    
        // *********双击放大与缩小*********
        private GestureDetector mGestureDetector;
    
        private boolean isAutoScale;
    
        public ZoomImageView(Context context) 
            this(context, null);
        
    
        public ZoomImageView(Context context, AttributeSet attrs) 
            this(context, attrs, 0);
        
    
        public ZoomImageView(Context context, AttributeSet attrs, int defStyle) 
            super(context, attrs, defStyle);
            // init
            mScaleMatrix = new Matrix();
            setScaleType(ScaleType.MATRIX);
            setOnTouchListener(this);
            mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
            mScaleGestureDetector = new ScaleGestureDetector(context, this);
            mGestureDetector = new GestureDetector(context,
                    new GestureDetector.SimpleOnGestureListener() 
                        @Override
                        public boolean onDoubleTap(MotionEvent e) 
    
                            if (isAutoScale) 
                                return true;
                            
    
                            float x = e.getX();
                            float y = e.getY();
    
                            if (getScale() < mMidScale) 
                                postDelayed(new AutoScaleRunnable(mMidScale, x, y), 16);
                                isAutoScale = true;
                             else 
                                postDelayed(new AutoScaleRunnable(mInitScale, x, y), 16);
                                isAutoScale = true;
                            
                            return true;
                        
                    );
        
    
        /**
         * 自动放大与缩小
         * 
         * @author zhangyan@lzt.com.cn
         * 
         */
        private class AutoScaleRunnable implements Runnable 
            /**
             * 缩放的目标值
             */
            private float mTargetScale;
            // 缩放的中心点
            private float x;
            private float y;
    
            private final float BIGGER = 1.07f;
            private final float SMALL = 0.93f;
    
            private float tmpScale;
    
            /**
             * @param mTargetScale
             * @param x
             * @param y
             */
            public AutoScaleRunnable(float mTargetScale, float x, float y) 
                this.mTargetScale = mTargetScale;
                this.x = x;
                this.y = y;
    
                if (getScale() < mTargetScale) 
                    tmpScale = BIGGER;
                
                if (getScale() > mTargetScale) 
                    tmpScale = SMALL;
                
            
    
            @Override
            public void run() 
                //进行缩放
                mScaleMatrix.postScale(tmpScale, tmpScale, x, y);
                checkBorderAndCenterWhenScale();
                setImageMatrix(mScaleMatrix);
    
                float currentScale = getScale();
    
                if ((tmpScale >1.0f && currentScale <mTargetScale) ||(tmpScale<1.0f &&currentScale>mTargetScale)) 
                    //这个方法是重新调用run()方法
                    postDelayed(this, 16);
                else
                    //设置为我们的目标值
                    float scale = mTargetScale/currentScale;
                    mScaleMatrix.postScale(scale, scale, x, y);
                    checkBorderAndCenterWhenScale();
                    setImageMatrix(mScaleMatrix);
    
                    isAutoScale = false;
                
            
        
    
        /**
         * 获取ImageView加载完成的图片
         */
        @Override
        public void onGlobalLayout() 
            if (!mOnce) 
                // 得到控件的宽和高
                int width = getWidth();
                int height = getHeight();
    
                // 得到我们的图片,以及宽和高
                Drawable drawable = getDrawable();
                if (drawable == null) 
                    return;
                
                int dh = drawable.getIntrinsicHeight();
                int dw = drawable.getIntrinsicWidth();
    
                float scale = 1.0f;
    
                // 图片的宽度大于控件的宽度,图片的高度小于空间的高度,我们将其缩小
                if (dw > width && dh < height) 
                    scale = width * 1.0f / dw;
                
    
                // 图片的宽度小于控件的宽度,图片的高度大于空间的高度,我们将其缩小
                if (dh > height && dw < width) 
                    scale = height * 1.0f / dh;
                
    
                // 缩小值
                if (dw > width && dh > height) 
                    scale = Math.min(width * 1.0f / dw, height * 1.0f / dh);
                
    
                // 放大值
                if (dw < width && dh < height) 
                    scale = Math.min(width * 1.0f / dw, height * 1.0f / dh);
                
    
                /**
                 * 得到了初始化时缩放的比例
                 */
                mInitScale = scale;
                mMaxScale = mInitScale * 4;
                mMidScale = mInitScale * 2;
    
                // 将图片移动至控件的中间
                int dx = getWidth() / 2 - dw / 2;
                int dy = getHeight() / 2 - dh / 2;
    
                mScaleMatrix.postTranslate(dx, dy);
                mScaleMatrix.postScale(mInitScale, mInitScale, width / 2,
                        height / 2);
                setImageMatrix(mScaleMatrix);
    
                mOnce = true;
            
        
    
        /**
         * 注册OnGlobalLayoutListener这个接口
         */
        @Override
        protected void onAttachedToWindow() 
            super.onAttachedToWindow();
            getViewTreeObserver().addOnGlobalLayoutListener(this);
        
    
        /**
         * 取消OnGlobalLayoutListener这个接口
         */
        @SuppressWarnings("deprecation")
        @Override
        protected void onDetachedFromWindow() 
            super.onDetachedFromWindow();
            getViewTreeObserver().removeGlobalOnLayoutListener(this);
        
    
        /**
         * 获取当前图片的缩放值
         * 
         * @return
         */
        public float getScale() 
            float[] values = new float[9];
            mScaleMatrix.getValues(values);
            return values[Matrix.MSCALE_X];
        
    
        // 缩放区间时initScale maxScale
        @Override
        public boolean onScale(ScaleGestureDetector detector) 
            float scale = getScale();
            float scaleFactor = detector.getScaleFactor();
    
            if (getDrawable() == null) 
                return true;
            
    
            // 缩放范围的控制
            if ((scale < mMaxScale && scaleFactor > 1.0f)
                    || (scale > mInitScale && scaleFactor < 1.0f)) 
                if (scale * scaleFactor < mInitScale) 
                    scaleFactor = mInitScale / scale;
                
    
                if (scale * scaleFactor > mMaxScale) 
                    scale = mMaxScale / scale;
                
    
                // 缩放
                mScaleMatrix.postScale(scaleFactor, scaleFactor,
                        detector.getFocusX(), detector.getFocusY());
    
                checkBorderAndCenterWhenScale();
    
                setImageMatrix(mScaleMatrix);
            
    
            return true;
        
    
        /**
         * 获得图片放大缩小以后的宽和高,以及left,right,top,bottom
         * 
         * @return
         */
        private RectF getMatrixRectF() 
            Matrix matrix = mScaleMatrix;
            RectF rectF = new RectF();
            Drawable d = getDrawable();
            if (d != null) 
                rectF.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
                matrix.mapRect(rectF);
            
            return rectF;
        
    
        /**
         * 在缩放的时候进行边界以及我们的位置的控制
         */
        private void checkBorderAndCenterWhenScale() 
            RectF rectF = getMatrixRectF();
            float deltaX = 0;
            float deltaY = 0;
    
            int width = getWidth();
            int height = getHeight();
    
            // 缩放时进行边界检测,防止出现白边
            if (rectF.width() >= width) 
                if (rectF.left > 0) 
                    deltaX = -rectF.left;
                
                if (rectF.right < width) 
                    deltaX = width - rectF.right;
                
            
    
            if (rectF.height() >= height) 
                if (rectF.top > 0) 
                    deltaY = -rectF.top;
                
                if (rectF.bottom < height) 
                    deltaY = height - rectF.bottom;
                
            
    
            /**
             * 如果宽度或高度小于空间的宽或者高,则让其居中
             */
            if (rectF.width() < width) 
                deltaX = width / 2f - rectF.right + rectF.width() / 2f;
            
    
            if (rectF.height() < height) 
                deltaY = height / 2f - rectF.bottom + rectF.height() / 2f;
            
    
            mScaleMatrix.postTranslate(deltaX, deltaY);
        
    
        @Override
        public boolean onScaleBegin(ScaleGestureDetector detector) 
    
            return true;
        
    
        @Override
        public void onScaleEnd(ScaleGestureDetector detector) 
    
        
    
        @Override
        public boolean onTouch(View v, MotionEvent event) 
    
            if (mGestureDetector.onTouchEvent(event)) 
                return true;
            
    
            mScaleGestureDetector.onTouchEvent(event);
    
            float x = 0;
            float y = 0;
            // 拿到多点触控的数量
            int pointerCount = event.getPointerCount();
            for (int i = 0; i < pointerCount; i++) 
                x += event.getX(i);
                y += event.getY(i);
            
    
            x /= pointerCount;
            y /= pointerCount;
    
            if (mLastPointerCount != pointerCount) 
                isCanDrag = false;
                mLastX = x;
                mLastY = y;
            
            mLastPointerCount = pointerCount;
            RectF rectF = getMatrixRectF();
            switch (event.getAction()) 
    
            case MotionEvent.ACTION_DOWN:
                if (rectF.width()>getWidth() +0.01|| rectF.height()>getHeight()+0.01) 
                    if(getParent() instanceof ViewPager)
                    getParent().requestDisallowInterceptTouchEvent(true);
                
                break;
    
            case MotionEvent.ACTION_MOVE:
                if (rectF.width()>getWidth()+0.01 || rectF.height()>getHeight()+0.01) 
                    if(getParent() instanceof ViewPager)
                    getParent().requestDisallowInterceptTouchEvent(true);
                
                float dx = x - mLastX;
                float dy = y - mLastY;
    
                if (!isCanDrag) 
                    isCanDrag = isMoveAction(dx, dy);
                
    
                if (isCanDrag) 
                    if (getDrawable() != null) 
                        isCheckLeftAndRight = isCheckTopAndBottom = true;
                        // 如果宽度小于控件宽度,不允许横向移动
                        if (rectF.width() < getWidth()) 
                            isCheckLeftAndRight = false;
                            dx = 0;
                        
                        // 如果高度小于控件高度,不允许纵向移动
                        if (rectF.height() < getHeight()) 
                            isCheckTopAndBottom = false;
                            dy = 0;
                        
                        mScaleMatrix.postTranslate(dx, dy);
    
                        checkBorderWhenTranslate();
    
                        setImageMatrix(mScaleMatrix);
                    
                
                mLastX = x;
                mLastY = y;
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                mLastPointerCount = 0;
                break;
    
            default:
                break;
            
    
            return true;
        
    
        /**
         * 当移动时进行边界检查
         */
        private void checkBorderWhenTranslate() 
            RectF rectF = getMatrixRectF();
            float deltaX = 0;
            float deltaY = 0;
    
            int width = getWidth();
            int heigth = getHeight();
    
            if (rectF.top > 0 && isCheckTopAndBottom) 
                deltaY = -rectF.top;
            
            if (rectF.bottom < heigth && isCheckTopAndBottom) 
                deltaY = heigth - rectF.bottom;
            
            if (rectF.left > 0 && isCheckLeftAndRight) 
                deltaX = -rectF.left;
            
            if (rectF.right < width && isCheckLeftAndRight) 
                deltaX = width - rectF.right;
            
            mScaleMatrix.postTranslate(deltaX, deltaY);
    
        
    
        /**
         * 判断是否是move
         * 
         * @param dx
         * @param dy
         * @return
         */
        private boolean isMoveAction(float dx, float dy) 
            return Math.sqrt(dx * dx + dy * dy) > mTouchSlop;
        
    
    
    
  • (3)下面我们将ZoomImageView应用到ViewPager上,所以现在新建一个布局文件vp.xml:

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:background="#000000"
        android:layout_height="match_parent" >
    
        <android.support.v4.view.ViewPager
            android:id="@+id/id_viewpager"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
             />
    
    </RelativeLayout>
    
  • (4)最后,我们在MainActivity上写一些测试用的数据,实现最终的一个效果:

    package com.example.zoomimageview;
    
    import com.example.zoomimageview.view.ZoomImageView;
    
    import android.app.Activity;
    import android.os.Bundle;
    import android.support.v4.view.PagerAdapter;
    import android.support.v4.view.ViewPager;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.ImageView;
    
    public class MainActivity extends Activity 
    
        private ViewPager mViewPager;
    
        private int[] mImgs = new int[]R.drawable.baby1,R.drawable.baby2,R.drawable.baby3,R.drawable.baby4;
    
        private ImageView[] mImageViews = new ImageView[mImgs.length];
    
        @Override
        protected void onCreate(Bundle savedInstanceState) 
            super.onCreate(savedInstanceState);
            setContentView(R.layout.vp);
    
            mViewPager = (ViewPager) findViewById(R.id.id_viewpager);
            mViewPager.setAdapter(new PagerAdapter() 
                @Override
                public Object instantiateItem(ViewGroup container, int position) 
                    ZoomImageView imageView = new ZoomImageView(getApplicationContext());
                    imageView.setImageResource(mImgs[position]);
                    container.addView(imageView);
                    mImageViews[position] = imageView;
                    return imageView;
                
    
                @Override
                public void destroyItem(ViewGroup container, int position,
                        Object object) 
                    container.removeView(mImageViews[position]);
                
    
                @Override
                public boolean isViewFromObject(View arg0, Object arg1) 
                    return arg0 == arg1;
                
    
                @Override
                public int getCount() 
                    return mImageViews.length;
                
            );
    
        
    
    

到此,我们的代码已经全部写完了,供大家学习,希望对大家的进步有所帮助,谢谢!

  • 更多资讯可以关注我的博客或者我的新浪微博:我是紫色紫色,不定期分享更多精彩内容,谢谢!

你的支持就是我的动力,欢迎大家热烈交流~欢迎大家订阅公众号,我会不定期更新资源,供大家一起学习。

以上是关于Android自定义ImageView实现图片缩放滑动,双击放大缩小,多点触控缩放的主要内容,如果未能解决你的问题,请参考以下文章

Android实现图片的缩放和拖动

Android Matrix手势缩放自定义view 不止于Imageview

Android自定义控件之可平移、缩放、旋转图片控件

Android开发之自定义圆角矩形图片ImageView的实现

Android自定义ImageView实现图片圆形 ,椭圆和矩形圆角显示

android 关于imageview的布局文件的设定