本文转载自Android 实现可以自由移动缩放的图片控件

那位大佬本身已经把此控件说的很明白了,有什么不懂的可以去原帖看看,我这里就不说明所有代码的意思了,就简单的贴一下全部的代码吧

import android.content.Context;import android.graphics.Matrix;import android.graphics.RectF;import android.graphics.drawable.Drawable;import android.support.annotation.Nullable;import android.util.AttributeSet;import android.view.GestureDetector;import android.view.MotionEvent;import android.view.ScaleGestureDetector;import android.view.View;import android.view.ViewConfiguration;import android.view.ViewTreeObserver;import android.widget.ImageView;/** * @author dj * @version 1.0 * @description 可缩放的图片控件 * @created on 2017/4/19. */public class ScaleView extends ImageView implements ViewTreeObserver.OnGlobalLayoutListener,        ScaleGestureDetector.OnScaleGestureListener            ,View.OnTouchListener{    //** 表示是否只有一次加载 */    private boolean isOnce = false;    /** 初始时的缩放值 */    private float mInitScale;    /** 双击时 的缩放值 */    private float mClickScale;    /** 最大的缩放值 */    private float mMaxScale;    /** 图片缩放矩阵 */    private Matrix mMatrix;    /** 图片缩放手势 */    private ScaleGestureDetector mScaleGesture;    // ----------------------------自由移动--------------------------------    /** 可移动最短距离限制,大于这个值时就可移动 */    private int mTouchSlop;    /** 是否可以拖动 */    private boolean isCanDrag;    // ----------------------------双击放大--------------------------------    private GestureDetector mGesture;    // 是否自动缩放    private boolean isAutoScale;    public ScaleView(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public ScaleView(Context context) {        this(context, null);    }    public ScaleView(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);        // 必须设置才能触发        this.setOnTouchListener(this);        mMatrix = new Matrix();        // 设置缩放模式        super.setScaleType(ScaleType.MATRIX);        mScaleGesture = new ScaleGestureDetector(context, this);        mGesture = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {            @Override            public boolean onDoubleTap(MotionEvent e) {                // 如果正在缩放时,不能放大                if (isAutoScale) {                    return true;                }                float px = e.getX();                float py = e.getY();                // 只有小于最大缩放比例才能放大                float scale = getScale();                if (scale < mClickScale) {                    // mMatrix.postScale(mClickScale/scale, mClickScale/scale,                    // px, py);                    postDelayed(new ScaleRunnale(px, py, mClickScale), 16);                    isAutoScale = true;                } else {                    // mMatrix.postScale(mInitScale/scale, mInitScale/scale, px,                    // py);                    postDelayed(new ScaleRunnale(px, py, mInitScale), 16);                    isAutoScale = true;                }                // setImageMatrix(mMatrix);                return true;            }        });        /**         * 是一个距离,表示滑动的时候,手的移动要大于这个距离才开始移动控件。如果小于这个距离就不触发移动控件,如viewpager         * 就是用这个距离来判断用户是否翻页。         */        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();    }    private class ScaleRunnale implements Runnable {        // 放大值        private static final float BIGGER = 1.08f;        // 缩小值        private static final float SMALLER = 0.96f;        private float x;        private float y;        private float mTargetScale;        private float mTempScale;        public ScaleRunnale(float x, float y, float mTargetScale) {            super();            this.x = x;            this.y = y;            this.mTargetScale = mTargetScale;            if (getScale() < mTargetScale) {                mTempScale = BIGGER;            } else if (getScale() > mTargetScale) {                mTempScale = SMALLER;            }        }        @Override        public void run() {            // 先进行缩放            mMatrix.postScale(mTempScale, mTempScale, x, y);            checkSideAndCenterWhenScale();            setImageMatrix(mMatrix);            float currentScale = getScale();            // 如果想放大,并且当前的缩放值小于目标值            if ((mTempScale > 1.0f && currentScale < mTargetScale)                    || (mTempScale < 1.0f && currentScale > mTargetScale)) {                // 递归执行run方法                postDelayed(this, 16);            } else {                float scale = mTargetScale / currentScale;                mMatrix.postScale(scale, scale, x, y);                checkSideAndCenterWhenScale();                setImageMatrix(mMatrix);                isAutoScale = false;            }        }    }    @Override    public void onGlobalLayout() {        // 如果还没有加载图片        if (!isOnce) {            // 获得控件的宽高            int width = getWidth();            int height = getHeight();            Drawable drawable = getDrawable();            if (drawable == null) {                return;            }            // 获得图片的宽高            int bitmapWidth = drawable.getIntrinsicWidth();            int bitmapHeight = drawable.getIntrinsicHeight();            // 设定比例值            float scale = 0.0f;            // 如果图片的宽度>控件的宽度,缩小            if (bitmapWidth > width && bitmapHeight < height) {                scale = width * 1.0f / bitmapWidth;            }            // 如果图片的高度>控件的高度,缩小            if (bitmapHeight > height && bitmapWidth < width) {                scale = height * 1.0f / bitmapHeight;            }            // 如果图片的宽高度>控件的宽高度,缩小 或者 如果图片的宽高度<控件的宽高度,放大            if ((bitmapWidth > width && bitmapHeight > height) || (bitmapWidth < width && bitmapHeight < height)) {                float f1 = width * 1.0f / bitmapWidth;                float f2 = height * 1.0f / bitmapHeight;                scale = Math.min(f1, f2);            }            // 初始化缩放值            mInitScale = scale;            mClickScale = mInitScale * 2;            mMaxScale = mInitScale * 4;            // 得到移动的距离            int dx = width / 2 - bitmapWidth / 2;            int dy = height / 2 - bitmapHeight / 2;            // 平移            mMatrix.postTranslate(dx, dy);            // 在控件的中心缩放            mMatrix.postScale(scale, scale, width / 2, height / 2);            // 设置矩阵            setImageMatrix(mMatrix);            // 关于matrix,就是个3*3的矩阵            /**             * xscale xskew xtrans yskew yscale ytrans 0 0 0             */            isOnce = true;        }    }    /**     * 注册全局事件     */    @Override    protected void onAttachedToWindow() {        super.onAttachedToWindow();        getViewTreeObserver().addOnGlobalLayoutListener(this);    }    /**     * 移除全局事件     */    @Override    protected void onDetachedFromWindow() {        super.onDetachedFromWindow();        getViewTreeObserver().removeGlobalOnLayoutListener(this);    }    /**     * 获得缩放值     *     * @return     */    public float getScale() {        /**         * xscale xskew xtrans yskew yscale ytrans 0 0 0         */        float[] values = new float[9];        mMatrix.getValues(values);        return values[Matrix.MSCALE_X];    }    @Override    public boolean onScale(ScaleGestureDetector detector) {        // 如果没有图片,返回        if (getDrawable() == null) {            return true;        }        // 缩放因子,>0表示正在放大,<0表示正在缩小        float intentScale = detector.getScaleFactor();        float scale = getScale();        // 进行缩放范围的控制        // 判断,如果<最大缩放值,表示可以放大,如果》最小缩放,说明可以缩小        if ((scale < mMaxScale && intentScale > 1.0f) || (scale > mInitScale && intentScale < 1.0f)) {            // scale 变小时, intentScale变小            if (scale * intentScale < mInitScale) {                // intentScale * scale = mInitScale ;                intentScale = mInitScale / scale;            }            // scale 变大时, intentScale变大            if (scale * intentScale > mMaxScale) {                // intentScale * scale = mMaxScale ;                intentScale = mMaxScale / scale;            }            // 以控件为中心缩放            // mMatrix.postScale(intentScale, intentScale, getWidth()/2,            // getHeight()/2);            // 以手势为中心缩放            mMatrix.postScale(intentScale, intentScale, detector.getFocusX(), detector.getFocusY());            // 检测边界与中心点            checkSideAndCenterWhenScale();            setImageMatrix(mMatrix);        }        return true;    }    /**     * 获得图片缩放后的矩阵     *     * @return     */    public RectF getMatrixRectF() {        Matrix matrix = mMatrix;        RectF rectF = new RectF();        Drawable drawable = getDrawable();        if (drawable != null) {            // 初始化矩阵            rectF.set(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());            // 移动s            matrix.mapRect(rectF);        }        return rectF;    }    private void checkSideAndCenterWhenScale() {        RectF rectF = getMatrixRectF();        float deltaX = 0f;        float deltaY = 0f;        int width = getWidth();        int height = getHeight();        // 情况1, 如果图片的宽度大于控件的宽度        if (rectF.width() >= width) {            if (rectF.left > 0) {                deltaX = -rectF.left;// 如果图片没有左边对齐,就往左边移动            }            if (rectF.right < width) {                deltaX = width - rectF.right;// 如果图片没有右边对齐,就往右边移动            }        }        // 情况2, 如果图片的宽度大于控件的宽度        if (rectF.height() >= height) {            if (rectF.top > 0) {                deltaY = -rectF.top;//            }            if (rectF.bottom < height) {                deltaY = height - rectF.bottom;// 往底部移动            }        }        // 情况3,如图图片在控件内,则让其居中        if (rectF.width() < width) {            // deltaX = width/2-rectF.left - rectF.width()/2;            // 或            deltaX = width / 2f - rectF.right + rectF.width() / 2f;        }        if (rectF.height() < height) {            deltaY = height / 2f - rectF.bottom + rectF.height() / 2f;        }        mMatrix.postTranslate(deltaX, deltaY);    }    @Override    public boolean onScaleBegin(ScaleGestureDetector detector) {        // TODO Auto-generated method stub        return true;    }    @Override    public void onScaleEnd(ScaleGestureDetector detector) {        // TODO Auto-generated method stub    }    private float mLastX;    private float mLastY;    /** 上次手指的数量 */    private int mLastPointerCount;    /** 判断是否检测了x,y轴 */    private boolean isCheckX;    private boolean isCheckY;    @Override    public boolean onTouch(View v, MotionEvent event) {        // 把事件传递给双击手势        if (mGesture.onTouchEvent(event)) {            return true;        }        // 把事件传递给缩放手势        mScaleGesture.onTouchEvent(event);        float x = event.getX();        float y = event.getY();        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()) {                    getParent().requestDisallowInterceptTouchEvent(true);                }                break;            case MotionEvent.ACTION_MOVE:                if (rectF.width() > getWidth()) {                    getParent().requestDisallowInterceptTouchEvent(true);                }                float dx = x - mLastX;                float dy = y - mLastY;                if (!isCanDrag) {                    isCanDrag = isMoveAction(dx, dy);                }                /**                 * 如果能移动                 */                if (isCanDrag) {                    //RectF rectF = getMatrixRectF();                    if (getDrawable() == null) {                        return true;                    }                    isCheckX = isCheckY = true;                    // 如果图片在控件内,不允许移动                    if (rectF.width() < getWidth()) {                        isCheckX = false;                        dx = 0f;                    }                    if (rectF.height() < getHeight()) {                        isCheckY = false;                        dy = 0f;                    }                    mMatrix.postTranslate(dx, dy);                    // 移动事检测边界                    checkSideAndCenterWhenTransate();                    setImageMatrix(mMatrix);                }                mLastX = x;                mLastY = y;                break;            case MotionEvent.ACTION_UP:            case MotionEvent.ACTION_CANCEL:                // 清楚手指                mLastPointerCount = 0;                break;        }        return true;    }    private void checkSideAndCenterWhenTransate() {        RectF rectF = getMatrixRectF();        float deltaX = 0f;        float deltaY = 0f;        int width = getWidth();        int height = getHeight();        if (rectF.top > 0 && isCheckY) {            deltaY = -rectF.top;// 往上边移动        }        if (rectF.bottom < height && isCheckY) {            deltaY = height - rectF.bottom;// 往底部移动        }        if (rectF.left > 0 && isCheckX) {            deltaX = -rectF.left;// 往左边移动        }        if (rectF.right < width && isCheckX) {            deltaX = width - rectF.right;// 往右边移动        }        // 移动        mMatrix.postTranslate(deltaX, deltaY);    }    private boolean isMoveAction(float dx, float dy) {        // 求得两点的距离        return Math.sqrt(dx * dx + dy * dy) > mTouchSlop;    }}

然后是activity的代码

import android.graphics.Matrix;import android.graphics.PointF;import android.os.Bundle;import android.support.annotation.Nullable;import android.support.v4.view.PagerAdapter;import android.support.v4.view.ViewPager;import android.support.v7.app.AppCompatActivity;import android.view.MotionEvent;import android.view.View;import android.view.ViewGroup;import android.widget.ImageView;import android.widget.RelativeLayout;import android.widget.TextView;import com.bumptech.glide.DrawableRequestBuilder;import com.bumptech.glide.Glide;import com.bumptech.glide.ListPreloader;import com.bumptech.glide.load.engine.DiskCacheStrategy;import com.bumptech.glide.load.resource.drawable.GlideDrawable;import com.bumptech.glide.request.animation.GlideAnimation;import com.bumptech.glide.request.target.SimpleTarget;import com.example.user.mypractice.R;import com.squareup.picasso.Picasso;import java.util.ArrayList;import java.util.List;/** * @author dj * @version 1.0 * @description 缩放图片 * @created on 2017/4/19. */public class ScaleViewActivity extends AppCompatActivity {    private List list;    private RelativeLayout rlBack;    private TextView tvCurrent;    private TextView tvTotal;    private ViewPager vpImgCheck;    private ImageVpAdapter adapter;    //图片所在的位置    private int position;    @Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_scale_view);        initView();        list = new ArrayList<>();        adapter=new ImageVpAdapter();        position = 0;        vpImgCheck.setAdapter(adapter);        vpImgCheck.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {            @Override            public void onPageScrolled(int i, float v, int i1) {            }            @Override            public void onPageSelected(int i) {                int current=i+1;                tvCurrent.setText(current+"");            }            @Override            public void onPageScrollStateChanged(int i) {            }        });        //加载数据        loadData();    }    /**     * 加载数据     */    private void loadData() {        list.clear();        list.add("http://scimg.jb51.net/allimg/151228/14-15122Q60431W4.jpg");        list.add("http://img1.3lian.com/2015/a1/137/d/37.jpg");        list.add("http://pic.qiantucdn.com/58pic/18/37/96/18n58PICPb7_1024.jpg");        list.add("http://pic.qiantucdn.com/58pic/12/81/76/44n58PICAT2.jpg");        list.add("http://pic.qiantucdn.com/58pic/14/44/24/94b58PICCxn_1024.jpg");        tvTotal.setText(list.size()+"");        vpImgCheck.setCurrentItem(position);        adapter.notifyDataSetChanged();    }    private void initView() {        rlBack = (RelativeLayout) findViewById(R.id.rl_back);        tvCurrent = (TextView) findViewById(R.id.tv_current);        tvTotal = (TextView) findViewById(R.id.tv_total);        vpImgCheck = (ViewPager) findViewById(R.id.vp_img_check);    }    /**     * 适配器     */    private class ImageVpAdapter extends PagerAdapter {        @Override        public int getCount() {            return list.size();        }        @Override        public boolean isViewFromObject(View view, Object o) {            return view == o;        }        @Override        public Object instantiateItem(ViewGroup container, int position) {            final ScaleView scaleView = new ScaleView(ScaleViewActivity.this);            scaleView.setScaleType(ImageView.ScaleType.MATRIX);            Picasso.with(ScaleViewActivity.this).load(list.get(position)).placeholder(R.mipmap.load).error(R.mipmap.error)                    .resize(500,500).centerCrop().into(scaleView);            container.addView(scaleView);            return scaleView;        }        @Override        public void destroyItem(ViewGroup container, int position, Object object) {            if (object instanceof ScaleView) {                ScaleView view = (ScaleView) object;                container.removeView(view);            }        }    }

好了,我这里用的是第三方的Picasso加载的图片,大家都知道,这个框架轻量并且使用方便,所以没有什么太大问题,好了开始运行


咦?这TM是什么和我想象的不一样啊?


明明原图是这么的山清水秀,怎么变成那样的呢?

这是因为picasso图片没有加载完整然后就将图片放置到控件里了,所以没有显示完全,可以随意拖动,但是看起来就好像放大了数倍一样。

那么有没有什么方法可以让图片加载完成后再把图片放置上去呢?

我试着查了一下,发现picasso并没有这个方法,不过在glide这个图片加载框架有相应的方法

new SimpleTarget() {                        @Override                        public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> glideAnimation) {                                                    }                    }

有Drawable属性,可以直接设置,所以就可以写成

Glide.with(ScaleViewActivity.this).load(list.get(position)).placeholder(R.mipmap.load).error(R.mipmap.error)                    .diskCacheStrategy(DiskCacheStrategy.SOURCE)                    .into(new SimpleTarget() {                        @Override                        public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> glideAnimation) {                            scaleView.setImageDrawable(resource);                        }                    });

将原来picasso加载图片的地方替换为这个就行了


这样就达到效果了,破费。

注意:使用时如果这个控件在布局文件里写的要加上

android:scaleType="matrix"

   

更多相关文章

  1. android 分辨率调试
  2. Android(安卓)滑动效果入门篇(二)—— Gallery
  3. Android.自定义控件的实现
  4. ExifInterface使用,Android(安卓)2.0新增类
  5. android直接在桌面生成快捷方式
  6. 隐藏Listview和RecyclerView 滑动边界的阴影,去除滚动条加分隔线
  7. Android当中切换图片
  8. android登录模块之静态登录
  9. android TextView显示文字和图片

随机推荐

  1. Android P Camera架构
  2. Android(安卓)数据存储——数据查询query
  3. Android(安卓)编程下设置 Activity 切换
  4. 获得a meta-data 的值
  5. android:ClassNotFoundException for Act
  6. Android 文件及文件夹操作
  7. Android数据解析JSON解析之手动JSON解析
  8. Android ImageSwitcher 实现按钮的3d旋转
  9. 从xml添加menu注意事项
  10. 超酷的时间选择控件