ImageView图片拖拽缩放控件

1、在父控件为Viewpager的背景实现,所以会处理图片左右滑动和Viewpager滑动 的冲突

package com.example.widget;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.widget.ImageView;

public class ImageControl extends ImageView {
    private String tag = getClass().getSimpleName();

    /** 用于记录开始时候的坐标位置 */
    private PointF startPoint = new PointF();
    private static final int MODE_DRAG = 1;
    /** 放大缩小照片模式 */
    private static final int MODE_ZOOM = 2;
    /** 不支持Matrix */
    private static final int MODE_UNABLE = 3;
    private static final int MODE_EXZOOM= 4;
    private int mMode = 0;//
    /** 缩放开始时的手指间距 */
    private float mStartDis;
    /** 当前Matrix */
    private Matrix mCurrentMatrix = new Matrix();
    private float[] mCurrentValues=new float[9];
    private Matrix mMatrix = new Matrix();

    private float MAX_ZOOM;
    // 这里希望双指控制不要缩小,但是Min_ZOOM!=1,因为图片可能本身就被放大过以填充控件
    private float MIN_ZOOM;
    private float DEFAULT_ZOOM;
    /** 位图对象 */
    private Bitmap mBitmap = null;
    /** 图片长度 */
    private float mImageWidth;
    /** 图片高度 */
    private float mImageHeight;

    private ImageListener listener;
    private float mDobleClickScale;
    private float[] mValues;

    PointF mid = new PointF();

    private XViewPager pager;

    public ImageControl(Context context) {
        super(context);
        init();
    }

    public ImageControl(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public ImageControl(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        // TODO Auto-generated constructor stub
        init();
    }

    GestureDetector mGestureDetector;

    private void init() {
        // 背景设置为balck
        setBackgroundColor(Color.BLACK);
        // 将缩放类型设置为MATRIX,表示控件内按原图原始大小在左上角(0,0)置放
        mGestureDetector = new GestureDetector(getContext(),
                new GestureListener());
    }

    @Override
    public void setImageBitmap(Bitmap bm) {
        // TODO Auto-generated method stub
        // setimageBitmap在2次onMeasure之后
        setScaleType(ScaleType.MATRIX);
        super.setImageBitmap(bm);
        // 这个判断很有必要,在activiy destroy时会setImageBitmap(null)
        if (bm == null) {
            return;
        }
        mBitmap = bm;
        // 图片原生尺寸
        mImageWidth = mBitmap.getWidth();
        mImageHeight = mBitmap.getHeight();

        fitScreen();

        // matrixA=getImageMatrix();表示matrixA指向当前的matrix对象,当getImageMatrix变化时matrixA也变了
        // matrixA.set(getImageMatrix());表示对此时的matrix的状态copy一份保存下来,无论getImageMatrix怎么变
        mMatrix.set(getImageMatrix());
        mValues = new float[9];
        mMatrix.getValues(mValues);

        MIN_ZOOM = mValues[Matrix.MSCALE_X]*2/3;
        DEFAULT_ZOOM=mValues[Matrix.MSCALE_X];
        mDobleClickScale = mValues[Matrix.MSCALE_X] * 2;
        MAX_ZOOM = mValues[Matrix.MSCALE_X] * 3;
    }
    @Override
    public void setImageResource(int resId) {
        // TODO Auto-generated method stub
        Log.i(tag, "setImageResource");
        setScaleType(ScaleType.FIT_CENTER);
        super.setImageResource(resId);
    }
    private void fitScreen() {
        // TODO Auto-generated method stub
        float scaleX = (float) getWidth() / (float) mImageWidth;
        mCurrentMatrix.set(getImageMatrix());
        mCurrentMatrix.postScale(scaleX, scaleX);
        // 图片与下边界 的距离
        float marginY = getHeight() - scaleX * mImageHeight;
        Log.i(tag, "空余纵向边界marginY=" + marginY);
        mCurrentMatrix.getValues(mCurrentValues);
        if (marginY > 0) {
            // 说明纵向铺不满
            mCurrentMatrix.postTranslate(0, (marginY-mCurrentValues[Matrix.MTRANS_Y]) / 2f);
        }else {
            mCurrentMatrix.postTranslate(0, marginY);

        }
        // Matrix是从(0,0)左上角开始做的变换
        setImageMatrix(mCurrentMatrix);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        /** 处理单点、多点触摸 **/
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
        // 按下事件
        case MotionEvent.ACTION_DOWN:
            // 设置拖动模式
            mMode = MODE_DRAG;
            startPoint.set(event.getX(), event.getY());
            mid.set(getWidth() / 2, getHeight() / 2);
            break;
        // 多点触摸
        case MotionEvent.ACTION_POINTER_DOWN:
            if (mMode == MODE_UNABLE)
                return true;
            mMode = MODE_ZOOM;
            pager.willIntercept=false;

            mStartDis = getDistance(event);
            midPoint(mid, event);
            break;

        case MotionEvent.ACTION_MOVE:
            if (mMode == MODE_ZOOM) {
                float endDis = getDistance(event);
                if (endDis > 10f) { // 两个手指并拢在一起的时候像素大于10
                    float scale = endDis / mStartDis;// 得到缩放倍数
                    setZoom(scale);
                    mStartDis = endDis;// 重置距离
                }

            } else if (mMode == MODE_DRAG) {
                float dx = event.getX() - startPoint.x; // 得到x轴的移动距离
                float dy = event.getY() - startPoint.y; // 得到y轴的移动距离

                // 避免和双击冲突,大于10f才算是拖动
                if (Math.sqrt(dx * dx + dy * dy) > 10f) {
                    startPoint.set(event.getX(), event.getY());
                    // 在当前基础上移动
                    mCurrentMatrix.set(getImageMatrix());
                    mCurrentMatrix.getValues(mCurrentValues);
                    Log.i(tag, "dx="+dx);

                    dx = checkDxBound(mCurrentValues, dx);
                    dy = checkDyBound(mCurrentValues, dy);
                    mCurrentMatrix.postTranslate(dx, dy);

                    setImageMatrix(mCurrentMatrix);
                    Log.i(tag, "dx="+dx);
                    if(dx==0){
                        pager.willIntercept=true;
                    }else {
                        pager.willIntercept=false;
                    }
                }

            }
            break;
        case MotionEvent.ACTION_CANCEL:
            // break;
        case MotionEvent.ACTION_UP:
            if(mMode==MODE_EXZOOM&&mCurrentValues[Matrix.MSCALE_X]<DEFAULT_ZOOM){
                    pager.willIntercept=true;
            }
            //至此前缩放模式结束,重置

            mMode = 0;
            break;

        // 多点松开
        case MotionEvent.ACTION_POINTER_UP:
            getImageMatrix().getValues(mCurrentValues);
            //至此缩放模式结束,进入“前缩放模式“,用于后面的ACTION_MOVE、UP
            mMode=MODE_EXZOOM;
        default:
            break;
        }

        return mGestureDetector.onTouchEvent(event);
    }

    // scale是相对于当前(不是最初)的大小来进行变化的倍率
    private void setZoom(float scale) {
        // TODO Auto-generated method stub

        float[] values = new float[9];
        // 不要getImageMatrix().getValues(values)
        mCurrentMatrix.set(getImageMatrix());
        mCurrentMatrix.getValues(values);

        // 限制放大的最大倍数
        if (scale * values[Matrix.MSCALE_X] > MAX_ZOOM) {
            scale = MAX_ZOOM / values[Matrix.MSCALE_X];
        }
        // 限制缩小的最小倍数
        if (scale * values[Matrix.MSCALE_X] <MIN_ZOOM) {
            scale = MIN_ZOOM / values[Matrix.MSCALE_X];
        }
        if (scale == 1) {
            return;
        }
        mCurrentMatrix.postScale(scale, scale, mid.x, mid.y);
        setImageMatrix(mCurrentMatrix);
        restrainXYBound();
    }

    @Override
    public void setImageMatrix(Matrix matrix) {
        // TODO Auto-generated method stub
        super.setImageMatrix(matrix);
        //双指缩放viewpager决不响应
        if(mMode==MODE_ZOOM){
            pager.willIntercept=false;
            return;
        }

    }

    // 取手势中心点
    private void midPoint(PointF point, MotionEvent event) {
        float x = event.getX(0) + event.getX(1);
        float y = event.getY(0) + event.getY(1);
        point.set(x / 2, y / 2);
    }

    private void restrainXYBound() {
        // TODO Auto-generated method stub
        float[] values = new float[9];
        mCurrentMatrix.set(getImageMatrix());
        mCurrentMatrix.getValues(values);
        float dx = 0, dy = 0;

        dx = checkDxBound(values, dx);
        dy = checkDyBound(values, dy);
        mCurrentMatrix.postTranslate(dx, dy);
        Log.i(tag, "dx="+dx);
        Log.i(tag, "dy="+dy);

        setImageMatrix(mCurrentMatrix);

        // float x1=(values[Matrix.MTRANS_X] + values[Matrix.MSCALE_X]
        // * mImageWidth - getWidth());
        // float y1=(values[Matrix.MTRANS_Y] + values[Matrix.MSCALE_Y]
        // * mImageHeight - getHeight());
        //
        // Log.i(tag, "x1="+x1+"y1="+y1);
        // float dx=0,dy=0;
        // if(x1>mValues[Matrix.MTRANS_X]){
        // dx=-x1-mValues[Matrix.MTRANS_X];
        // }
        // // else if(x1>mValues[Matrix.MTRANS_X]){
        // // dx=x1-mValues[Matrix.MTRANS_X];
        // // dx=-dx;
        // // }
        // if(y1>mValues[Matrix.MTRANS_Y]){
        // dy=-y1-mValues[Matrix.MTRANS_Y];
        // }
        // // else if(y1>mValues[Matrix.MTRANS_Y]){
        // // dy=y1-mValues[Matrix.MTRANS_Y];
        // // dy=-dy;
        // // }
        // mCurrentMatrix.postTranslate(dx,dy);
        // setImageMatrix(mCurrentMatrix);
        // mCurrentMatrix.set(getImageMatrix());
        // mCurrentMatrix.getValues(values);
        // x1=(values[Matrix.MTRANS_X] + values[Matrix.MSCALE_X]
        // * mImageWidth - getWidth());
        // y1=(values[Matrix.MTRANS_Y] + values[Matrix.MSCALE_Y]
        // * mImageHeight - getHeight());
        // Log.i(tag, "x1="+x1+"y1="+y1);

    }

    /**
     * 计算两个手指间的距离
     *
     * @param event
     * @return
     */
    private float getDistance(MotionEvent event) {
        float dx = event.getX(1) - event.getX(0);
        float dy = event.getY(1) - event.getY(0);
        /** 使用勾股定理返回两点之间的距离 */
        return (float) Math.sqrt(dx * dx + dy * dy);
    }

    /**
     * 和当前矩阵对比,检验dx,使图像移动后不会超出ImageView边界
     *
     * @param values
     * @param dx
     * @return
     */
    private float checkDxBound(float[] values, float dx) {
        float width = getWidth();
        if (mImageWidth * values[Matrix.MSCALE_X] < width){
            //当控件能容纳整张图片时,让图片保持正中
            dx=(getWidth()-mImageWidth*values[Matrix.MSCALE_X])/2-values[Matrix.MTRANS_X];
        }else if (values[Matrix.MTRANS_X] + dx > 0)
            dx = -values[Matrix.MTRANS_X];
        else if (values[Matrix.MTRANS_X] + dx < -(mImageWidth
                * values[Matrix.MSCALE_X] - width))
            dx = -(mImageWidth * values[Matrix.MSCALE_X] - width)
                    - values[Matrix.MTRANS_X];
        return dx;
    }

    /**
     * 和当前矩阵对比,检验dy,使图像移动后不会超出ImageView边界
     *
     * @param values
     * @param dy
     * @return
     */
    private float checkDyBound(float[] values, float dy) {
        // 获取ImageView控件高度
        float height = getHeight();
        // mImageHeight=sitImageBitmap方法中获取的图片高度
        // values[Matrix.MSCALE_Y]当前Y轴缩放级别计算出当前Y轴的显示高度
        if (mImageHeight * values[Matrix.MSCALE_Y] < height) {
            // 图片的纵向全部显示了
            dy=(getHeight()-mImageHeight*values[Matrix.MSCALE_Y])/2-values[Matrix.MTRANS_Y];
        }else if (values[Matrix.MTRANS_Y] + dy > 0) {

            dy = -values[Matrix.MTRANS_Y];
        } else if (values[Matrix.MTRANS_Y] + dy < -(mImageHeight
                * values[Matrix.MSCALE_Y] - height)) {

            dy = -(mImageHeight * values[Matrix.MSCALE_Y] - height)
                    - values[Matrix.MTRANS_Y];
        }
        return dy;
    }

    public void onDoubleClick() {
        float[] values = new float[9];
        getImageMatrix().getValues(values);
        // 获取当前X轴缩放级别
        float scale = values[Matrix.MSCALE_X];
        float tmp;
        if (scale > DEFAULT_ZOOM) {
            tmp = DEFAULT_ZOOM / scale;
        } else {
            tmp = mDobleClickScale / scale;
        }
        setZoom(tmp);
    }

    private class GestureListener extends SimpleOnGestureListener {

        @Override
        public boolean onDown(MotionEvent e) {
            // 捕获Down事件
            return true;
        }

        @Override
        public boolean onDoubleTap(MotionEvent e) {
            // 触发双击事件
            onDoubleClick();
            return true;
        }

        @Override
        public boolean onSingleTapUp(MotionEvent e) {
            // TODO Auto-generated method stub
            return super.onSingleTapUp(e);
        }

        @Override
        public void onLongPress(MotionEvent e) {
            // TODO Auto-generated method stub
            super.onLongPress(e);
            if (listener != null) {
                listener.onLongPress();
            }

        }

        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2,
                float distanceX, float distanceY) {
            return super.onScroll(e1, e2, distanceX, distanceY);
        }

        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
                float velocityY) {
            // TODO Auto-generated method stub
            return super.onFling(e1, e2, velocityX, velocityY);
        }

        @Override
        public void onShowPress(MotionEvent e) {
            // TODO Auto-generated method stub
            super.onShowPress(e);
        }

        @Override
        public boolean onDoubleTapEvent(MotionEvent e) {
            // TODO Auto-generated method stub
            return super.onDoubleTapEvent(e);
        }

        @Override
        public boolean onSingleTapConfirmed(MotionEvent e) {
            // TODO Auto-generated method stub
            return super.onSingleTapConfirmed(e);
        }
    }

    public interface ImageListener {
        void onLongPress();
    }

    public ImageListener getListener() {
        return listener;
    }

    public void setListener(ImageListener listener) {
        this.listener = listener;
    }

    public XViewPager getPager() {
        return pager;
    }

    public void setPager(XViewPager pager) {
        this.pager = pager;
    }

}

Viewpager

package com.example.widget;

import android.content.Context;
import android.graphics.Matrix;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;

public class XViewPager extends ViewPager {
    public boolean willIntercept = true;
    private String tag = "XViewPager";

    public XViewPager(Context context) {
        super(context);
    }

    public XViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO Auto-generated constructor stub
    }
    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        Log.i(tag, "willIntercept---" + willIntercept);

        /** 处理单点、多点触摸 **/
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
        // 按下事件
        case MotionEvent.ACTION_DOWN:
            Log.i(tag, "ACTION_DOWN");
        case MotionEvent.ACTION_POINTER_DOWN:
            Log.i(tag, "ACTION_POINTER_DOWN");
        case MotionEvent.ACTION_MOVE:
            Log.i(tag, "ACTION_MOVE");

            break;
        case MotionEvent.ACTION_CANCEL:
            Log.i(tag, "ACTION_CANCEL");

             break;
        case MotionEvent.ACTION_UP:
            Log.i(tag, "ACTION_UP");
            break;

        // 多点松开
        case MotionEvent.ACTION_POINTER_UP:
            Log.i(tag, "ACTION_POINTER_UP");

        default:
            break;
        }
        if (willIntercept) {
            // 这个地方直接返回true会很卡
            return super.onInterceptTouchEvent(event);
        } else {
            // 不让viewpager响应outouch
            return false;
        }
    }

    /**
     * 设置ViewPager是否拦截点击事件
     *
     * @param value
     *            if true, ViewPager拦截点击事件 if false,
     *            ViewPager将不能滑动,ViewPager的子View可以获得点击事件 主要受影响的点击事件为横向滑动
     *
     */
    public void setTouchIntercept(boolean value) {
        willIntercept = value;
    }
}

Done!

时间: 2024-08-29 21:51:16

ImageView图片拖拽缩放控件的相关文章

ios 为什么拖拽的控件为weak 手写的strong

ib拖拽的控件自动声明为weak  而平时自己手写的为strong 在ios中,对象默认都是强引用,不是强引用赋值后会立即释放 ib声明weak 不立即被释放 简单说就是 1.声明的弱引用指向强引用 2.加到了view中 @property (nonatomic, weak) UILabel * lab; ..... -(void)viewDidLoad { UILabel * label = [ UILabel alloc] init.... _lab = label; [self.view

一个能接受外部拖拽的控件(文字或文件)

恩....也是这2天写的一个小东西的需求, 可以拖拽外部文本文件, 或者选择的一段文本到Memo里显示 查了一下资料, 主要从2个方面实现: 1.拖拽文件实现WM_DROPFILES就可以了 2.拖拽文本需要实现IDropTarget接口 针对这个功能, 重新封装了一个Memo出来: TDropMemo = class(TMemo, IUnknown, IDropTarget) private FDropAccept: Boolean; FDTDropAccept: HResult; FFE:

独家原创,拖拽任意控件移动任意目标,拖拽控件移动整个窗体

独家原创,拖拽任意控件移动任意目标,拖拽控件移动整个窗体,在无边框窗体及其友好的实现拖拽移动窗体 http://www.cnblogs.com/vonly/ only原创首发,vonly.net 1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Configuration; 5 using System.Diagnostics; 6 using Sys

移动、PC图片拖拽缩放2

// 获取图片 function getImage() { let modal = document.getElementById("modal"); getMainBox().addEventListener("click",function(e) { let event = getEvent(e); let target = event.target || event.srcElement; if(isImage(target)) { let imgUrl =

VS2010 VS2012拖拽NumericUpDown控件直接卡死的解决办法

昨天晚上画界面过程中碰到这个问题,本以为是VS的bug,并没在意gg了一个2010的Sp1补丁,之后进入官方下载器,50mb/s的网,大概更新了3-4个小时.今天晚上到了画界面的时间准备开始就又碰到了这个问题,我就立刻的换了2012尝试,同样的结果,索性在群里问了问没人搭理我,故不断的google,终于找到一个说是是是是有道字典和vs冲突,关掉了有道,问题解决啦!解决方案就是关掉关关关关关关有道,如果你也碰到这个问题,希望对您有帮助吧

Winform图片拖拽与缩放

最近做项目的时候遇到上传施工平面布置图,查看,因为图片比较大,一般的显示器分辨率无法显示全,然后还需要放大看清楚图片里面的文字内容,所以需要用到图片的拖拽与缩放功能.这里整理下具体操作. 首先新建一个窗体,拖一个panel控件到窗体中,然后在拖一个pictureobx控件到panel中,然后在添加个上传图片的按钮: 具体代码: using System; using System.Collections.Generic; using System.ComponentModel; using Sy

.NET成人礼 | 还记得20年前一起拖过的控件吗?

本文是MVP Ediwang写的回忆一个80后的拖控件的感悟,与君共勉: 每一代人都有记忆里的味道.煤球炉.黑白电视机是属于父母的记忆.而“拖控件”式编程,启蒙了无数像我这样的80后(嗯,89也算80后). 经典旧世 2000 年那个时候,上海这样的城市里也不是每家每户都有电脑,我家也没有电脑.我在学校经常听几位家里条件不错的同学说他们玩电脑的事情.CIH.千年虫等名词让我对电脑有着非常强烈的好奇心.而我第一次体验到计算机,是在我母亲单位的机房里.它是一台卧式机箱.15寸 CRT 凸面屏显示器的

WPF如何将数据库中的二进制图片数据显示在Image控件上

首先在xaml文件里定义一个Image控件,取名为img MemoryStream stream = new MemoryStream(获得的数据库对象): BitMapImage bmp = new BitMapImage(); bmp.BeginInit();//初始化 bmp.StreamSource = stream;//设置源 bmp.EndInit();//初始化结束 img.Source = bmp;//设置图像Source 很多人用这个方法都没有初始化BitMapImage ,这

HTML5多图片拖拽上传带进度条

前言 昨天利用css2的clip属性实现了网页进度条觉得还不错,但是很多情况下,我们在那些时候用进度条呢,一般网页加载的时候如果有需要可以用,那么问题就来了,怎么才算整个加载完毕呢,是页面主要模块加载完毕,还是window.onload之后算呢,对这些方面,我真不敢随意回答,因业务需求而定,本文想要说的是在图片上传的时候用到的进度条. 效果展示 详细参考请移步至html5demo HTML5 新增的拖拽相关事件说明 1.ondragover 效果图演示是所看见的可以将文件不仅仅是图片拖拽到该di