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

来源:互联网 发布:手机 惯性导航算法 编辑:程序博客网 时间:2024/04/27 09:53

转载请注明出处: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;            }        });    }}

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

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

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

这里写图片描述

0 0
原创粉丝点击