Android中自定义ImageView实现图片放大缩小的功能_android customimageview支持放大缩小-程序员宅基地

技术标签: Bitmap问题  ImageView  

开发中会使用到查看图片的功能,不仅要能查看图片而且还能双击放大,缩小等操作,下面对ImageView

进行自定义,可以实现放大缩小的功能,以后使用的时候可以直接拿来用。

/**
 * 对图片进行放大缩小
 * Created by acer-pc on 2018/8/21.
 */

public class DoubleScaleImageView  extends AppCompatImageView implements View.OnTouchListener, ViewTreeObserver.OnGlobalLayoutListener {
    private boolean isFirst = false;
    private float doubleScale;// 双击放大的值
    private Matrix mScaleMatrix;
    private float defaultScale;// 默认的缩放值
    private int mLastPinterCount;// 记录上一次多点触控的数量
    private float mLastX;
    private float mLastY;
    private int mTouchSlop;
    private boolean isCanDrag;
    private boolean isCheckLeft;
    private boolean isCheckTop;
    private GestureDetector mGestureDetector;
    public DoubleScaleImageView(Context context) {
        this(context, null);
    }
    public DoubleScaleImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
    @SuppressLint("ClickableViewAccessibility")
    public DoubleScaleImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mScaleMatrix = new Matrix();
        setScaleType(ScaleType.MATRIX);
        setOnTouchListener(this);
        // getScaledTouchSlop是一个距离,表示滑动的时候,手的移动要大于这个距离才开始移动控件。如果小于这个距离就不触发移动控件
        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
        mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
            @Override
            public boolean onDoubleTap(MotionEvent e) {
                float x = e.getX();
                float y = e.getY();
                if (getScale() < doubleScale) {
                    mScaleMatrix.postScale(doubleScale / getScale(), doubleScale / getScale(), x, y);// 放大
                }
                else {
                    mScaleMatrix.postScale(defaultScale / getScale(), defaultScale / getScale(), x, y);// 缩小
                }
                setImageMatrix(mScaleMatrix);
                return super.onDoubleTap(e);
            }
        });
    }
    @Override
    protected void onAttachedToWindow() {// view附加到窗体上时调用该方法
        super.onAttachedToWindow();
        getViewTreeObserver().addOnGlobalLayoutListener(this);
    }
    @SuppressWarnings("deprecation")
    @Override
    protected void onDetachedFromWindow() {// 将视图从窗体上分离的时候调用该方法。
        super.onDetachedFromWindow();
        getViewTreeObserver().removeGlobalOnLayoutListener(this);
    }
    @Override
    public void onGlobalLayout() {// 在这个方法中获取ImageView加载完成后的图片
        if (!isFirst) {
            // 获取控件的宽度和高度
            int width = getWidth();
            int height = getHeight();
            // 得到我们的图片以及图片的宽度及高度
            Drawable drawable = getDrawable();
            if (drawable == null) { return; }
            int imageWidth = drawable.getIntrinsicWidth();// 图片的宽度
            int imageHeight = drawable.getIntrinsicHeight();// 图片的高度
            float scale = 1.0f;
            // 如果图片宽度大于控件宽度,但是图片高度小于控件 高度,我们要缩小图片
            if (imageWidth > width && imageHeight < height) {
                scale = width * 1.0f / imageWidth;
            }
            // 如果图片宽度小于控件宽度,但是图片高度大于控件 高度,我们要缩小图片
            if (imageWidth < width && imageHeight > height) {
                scale = height * 1.0f / imageHeight;
            }
            // 如果图片的宽度都 大于或小于控件宽度,我们则要对图片进行对应缩放,保证图片占满控件
            if ((imageWidth > width && imageHeight > height) || (imageWidth < width && imageHeight < height)) {
                scale = Math.min(width * 1.0f / imageWidth, height * 1.0f / imageHeight);
            }
            // 初始化对应的缩放值
            defaultScale = scale;
            doubleScale = defaultScale * 2;
            // 图片缩放后,将图片要移动到控件中心
            int dx = width / 2 - imageWidth / 2;
            int dy = height / 2 - imageHeight / 2;
            mScaleMatrix.postTranslate(dx, dy);
            mScaleMatrix.postScale(defaultScale, defaultScale, width / 2, height / 2);
            setImageMatrix(mScaleMatrix);
            isFirst = true;
        }
    }
    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        if (mGestureDetector.onTouchEvent(event)) { return true; }
        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 (mLastPinterCount != pointerCount) {
            isCanDrag = false;
            mLastX = x;
            mLastY = y;

        }
        mLastPinterCount = pointerCount;
        switch (event.getAction()) {
            case MotionEvent.ACTION_MOVE:
                float dx = x - mLastX;
                float dy = y - mLastY;
                isCanDrag = isMove(dx, dy);
                if (isCanDrag) {
                    RectF rectf = getMatrixRectf();
                    if (null != getDrawable()) {
                        isCheckLeft = isCheckTop = true;
                        if (rectf.width() < getWidth()) {// 如果图片宽度小于控件宽度(屏幕宽度)不允许横向移动
                            dx = 0;
                            isCheckLeft = false;
                        }
                        if (rectf.height() < getHeight()) {// 如果图片高度小于控件高度(屏幕高度)不允许纵向移动
                            dy = 0;
                            isCheckTop = false;
                        }
                        mScaleMatrix.postTranslate(dx, dy);
                        checkTranslateWithBorder();
                        setImageMatrix(mScaleMatrix);
                    }
                }
                mLastX = x;
                mLastY = y;
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                mLastPinterCount = 0;
                break;
        }
        return true;
    }
    /**
     * 移动图片时进行边界检查
     */
    private void checkTranslateWithBorder() {
        RectF rectf = getMatrixRectf();
        float delX = 0;
        float delY = 0;
        int width = getWidth();
        int height = getHeight();
        if (rectf.top > 0 && isCheckTop) {
            delY = -rectf.top;
        }
        if (rectf.bottom < height && isCheckTop) {
            delY = height - rectf.bottom;
        }
        if (rectf.left > 0 && isCheckLeft) {
            delX = -rectf.left;
        }
        if (rectf.right < width && isCheckLeft) {
            delX = width - rectf.right;
        }
        mScaleMatrix.postTranslate(delX, delY);
    }
    // 判断是否有移动
    private boolean isMove(float x, float y) {
        return Math.sqrt(x * x + y * y) > mTouchSlop;
    }
    /**
     * 获取图片的位置
     */
    private RectF getMatrixRectf() {
        Matrix matrix = mScaleMatrix;
        RectF recft = new RectF();
        if (getDrawable() != null) {
            recft.set(0, 0, getDrawable().getIntrinsicWidth(), getDrawable().getIntrinsicHeight());
            matrix.mapRect(recft);
        }
        return recft;
    }

    // 获取当前图片的缩放值
    private float getScale() {
        float values[] = new float[9];
        mScaleMatrix.getValues(values);
        return values[Matrix.MSCALE_X];
    }
}
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_42618969/article/details/81910107

智能推荐

ZOJ-3772 Calculate the Function(线段树,矩阵乘法)-程序员宅基地

文章浏览阅读260次。Calculate the FunctionTime Limit: 2 Seconds Memory Limit: 65536 KBYou are given a list of numbers A1 A2 .. AN and M queries. For the i-th query:The query has two parameters Li and R_calculate the function

linux命令实现git代码copy到svn仓库并提交_git文件考到svn路徑下,怎麽留下copy路徑-程序员宅基地

文章浏览阅读534次。项目需要,将git仓库master代码copy到svn仓库的主干上,需要在linux上实现,只能老老实实敲命令了。1. 解决方案 非常原始,首先创建/git目录,然后clone git仓库master最新代码,copy到/svn目录下,删除.git文件,然后使用svn add,commit到svn的分支上。2. 直接写命令:#创建git文件夹并进入目录,mkdir -p:递归创建目录,即使上级目录不存在,会按目录层级自动创建目录mkdir -p /code/..._git文件考到svn路徑下,怎麽留下copy路徑

win10linux双系统时间,win10与linux双系统切换时间不一致的调整-程序员宅基地

文章浏览阅读956次。按照Linux系统之后再切换回到win10后,我发现win10的时间不再是北京时间,而是比北京时间多了整整8小时,之后百度找到了问题来源,这里给出解决方法。如果安装了 Windows 和 Linux(比如 Ubuntu)双系统,有时会出现两个系统的时间不一致的情况。这是因为,两个操作系统对电脑硬件时间的定义不一样,Windows 认为电脑硬件时间是“本地时间”,因此它启动后直接用该时间作为“系统时..._win10+linux电脑时间不对

别浪费自己的高学历_别浪费自己的高学历,你最该拥有这些能力-程序员宅基地

文章浏览阅读237次。转自:科学堂原文链接:http://scienceroom.net/dont-waste-your-high-academic-records-666.html很多人在问念研究生能学到什么,短短的两年时间,上课学不到多少有用的,科研也没怎么搞,到底念研究生能学到什么?一开始念硕士博士的时间都是有这 些迷惘的,甚至有些人连这些问题想也没想。如果用点心思,不管我们将来搞不搞_别浪费自己的高学历,你最该拥有这些能力

【改进篇】Python实现VRP常见求解算法——量子粒子群算法(QDPSO)_python群算法求解vrptw-程序员宅基地

文章浏览阅读342次。基于python语言,实现经典量子粒子群算法(QDPSO)对车辆路径规划问题(CVRP)进行求解,优化代码结构,改进Split函数_python群算法求解vrptw

Spring整合Kafka消费端concurrency参数设置_kafka concurrency-程序员宅基地

文章浏览阅读7.9k次,点赞4次,收藏19次。Spring整合Kafka时在消费端为我们提供了一个参数concurrency,这个参数可主要是用来设置消费者的线程数,提高消费的能力,当然与所有线程数设置的套路一样,这个值并不是越大越好。Spring中提供的这个参数其原理就和启动多个消费者一样,kafka在启动时会根据你的消费者数量与主题中的分区数进行匹配,比如你有3个消费者订阅了‘test_topic’主题,这个主题又有3个分区,那么kafka就会为每一个消费者分配一个分区,服务端再发送消息时,可以把消费均匀的发送到3个分区中,这样就可以实现3个消费_kafka concurrency

随便推点

InnoDB: Error: io_setup() failed with EAGAIN after 5 attempts_[error] innodb: io_setup() failed with eagain afte-程序员宅基地

文章浏览阅读1.5k次。在一台服务器中以各数据库的备份文件为数据文件启动多个MySQL实例供SQL Review使用。之前运行一直没有问题(最多的时候有23个MySQL实例同时运行),后来新配置了一台服务器,启动其对应的实例时失败。部分错误日志如下:……140505 16:05:59 InnoDB: Using Linux native AIO140505 16:05:59 InnoDB: Warning: io_se..._[error] innodb: io_setup() failed with eagain after 5 attempts. [error] inno

原生js轮播图的实现_原生轮播-程序员宅基地

文章浏览阅读284次。原理就是当点击到小圆点时,得到相应的i值,这个i值也就是span的index值,我们拿他和全局变量index作比较,然后重新设置wrap.style.left的值,然后把i值复制给全局变量index,最后显示当前的小原点即可。值得注意的是这里涉及到了闭包的概念,如果直接使用for循环,则不能得到正确的结果。很多网站都有轮播图,我这里为大家简答的介绍一下,如果有些的不对的地方请大家及时提出意见,也希望给大家带来帮助。小圆点部分是那个可以点击切换部分这个是根据图片来设置圆点的个数的不是自己想设置多少。..._原生轮播

ssldump0.9b3版本的源码分析-程序员宅基地

文章浏览阅读668次。一、目录结构Base 提供一些数据收集和调试打印的功能 Common 提供一些链表、字符串结构、位域、调试、错误打印、时间戳、线程操作等功能 Dummy 无 Null 提供了分析数据的功能,跟ssl目录中的analyze部分很像,还不明白具体的用处 Ssl 对tcp连接的往返数据进行顺序解析https的过程 Wi..._ssldump0.9b3

全志H616方案香橙派orangepi zero2的26pin接口 SPI测试_香橙派zero2 的26pin 修改功能-程序员宅基地

文章浏览阅读1.2k次。1) 由 26pin 接口的原理图可知,Orange Pi Zero 2 可用的 spi 为 spi12) 先查看下 linux 系统中是否存在 spidev1.1 的设备节点,如果存在,说明 SPI1 已 经设置好了,可以直接使用3) 再在 wiringOP 的 examples 中编译 spidev_test 测试程序4) 先不短接 SPI1 的 mosi 和 miso 两个引脚,运行 spidev_test 的输出结果如下所示, 可以看到 TX 和 RX..._香橙派zero2 的26pin 修改功能

C++ 赋值、浅拷贝、深拷贝和零拷贝解析_c++零拷贝-程序员宅基地

文章浏览阅读2.3k次。1. 浅拷贝浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。2. 深拷贝深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。深拷贝和浅拷贝是只针对Object和Array这样的引用数据类型的,示意图大致如下:3. 赋值与浅拷贝差异赋值:把一个对象赋值给一个新的变量时,赋的其实是该对象的在栈中的地址,而不是堆中的数据。也就是两个对象指向的是同一个存储空间,无论哪个对象发生改变,其实都是改变的存储空间的内容,因此,两个对象是联动的_c++零拷贝

php流程控制(新建流程)-程序员宅基地

文章浏览阅读136次。流程控制,又是一个广泛应用于办公自动化等内部自定义流程及审核的功能。比如请假流程啊,申请办公用品经费,报销申请啊,等等,需要层层审批通过才能完成的(按规章办事,最好别越级,你说是吧)不习惯一篇博客太长,看不过来,这篇我们先实现流程的新建先看看效果图1.有个下拉选择用户,可以依次添加到流程的节点位置,2.下方显示流程的每个节点3.填写流程名称,点击保存即可完成流程的新建数..._php事件上报流程功能

推荐文章

热门文章

相关标签