android-ImageView的拖动、旋转、缩放、边界回弹、双击缩放、单击销毁及源码下载

博客地址:http://blog.csdn.net/u010156024

TouchImageViewActivity 是本人一句一句代码写的,参考了网上大牛的博客。

不过其中的效果是网上没有的,也是本人一直想实现的效果。

* 本实例重写ImageView的触摸事件和手势方法。

* 实现图片的缩放、拖动,双击放大缩小、单击销毁,边界回弹,旋转并实现自动摆正。

* 详细效果请看目录下面的:结果展示动态图.gif

* 其中大部分关键节点都给出了注释,相信大家一看就会很快明白的。

本示例所展示的效果,是网上没有的,请详细查看,并且,本人力求实例简单。

最终的效果目的是模仿QQ聊天记录中图片的展示效果。

如果大家在学习过程中,遇到什么问题,可以到博客下留言或评论,本人都会及时回复的。

先看效果图:

整个效果图是我用另一部手机录制的,由于录制比较大,上传不了,然后就截成了GIF文件,大家可以看看效果。

项目代码中有完整的录制视频,大家可以下载下来之后看到。

源码下载

源码下载之后大家如果有什么问题,欢迎留言!本人会尽快回复的。

本篇代码尽量简单,以上效果全在一个类中完成的,整个代码四百多行。代码中,我都尽量多的给出了注释,相信大家一看就懂得。

源码工程中,还有另外一个网上很多项目中采用的自定义ImageView的代码,大家完全可以采用,不过效果没有本人写的代码的效果,并且它的代码有点不易看懂。

废话不多说,本人代码如下:

/**
 * 本实例重写ImageView的触摸事件和手势方法。
 * 实现图片的缩放、拖动,双击放大缩小、单击销毁,边界回弹,旋转并实现自动摆正。
 * 详细效果请看目录下面的:结果展示动态图.gif
 * @author 龙吟在天
 * {@link http://blog.csdn.net/u010156024}
 */
public class TouchImageViewActivity extends Activity {
    /** Called when the activity is first created. */
    private ImageView touchImageView;
    private float x_down = 0;
    private float y_down = 0;
    private PointF mid = new PointF();
    private float oldDist = 1f;
    private float oldRotation = 0;//第二个手指放下时的两点的旋转角度
    private float rotation = 0;//旋转角度差值
    private float newRotation = 0;
    private float Reset_scale = 1;
    private Matrix matrix = new Matrix();
    private Matrix matrix1 = new Matrix();
    private Matrix savedMatrix = new Matrix();
    private GestureDetector gestureDetector;

    private static final int NONE = 0;
    private static final int DRAG = 1;
    private static final int ZOOM = 2;
    private int mode = NONE;
    private boolean isFangda = false;//双击放大还是缩小
    private int widthScreen;
    private int heightScreen;
    private Bitmap gintama;
    boolean isCheckTopAndBottom,isCheckRightAndLeft;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.show_touchimageview);
        touchImageView = (ImageView) findViewById(R.id.touchimageview);
        gintama = BitmapFactory.decodeResource(getResources(), R.drawable.abc);
        touchImageView.setImageBitmap(gintama);
        /**
         * 使用图片的矩阵类型进行图片的设置,必须设置
         * setScaleType(ScaleType.MATRIX);
         * 网上有说,设置了该类型之后,怎么设置图片在中心位置上?
         * 我的解决方法就是通过代码控制,将图片设置在屏幕中心
         * 具体代码请参看 center(true,true);方法
         */
        touchImageView.setScaleType(ScaleType.MATRIX);
        DisplayMetrics dm = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(dm);
        //获取屏幕的宽和高
        widthScreen = dm.widthPixels;
        heightScreen = dm.heightPixels;
        //初始化图片的矩阵
        matrix.set(touchImageView.getImageMatrix());
        /**
         * 初始化手势
         * 单击  双击 长按
         * 这三个均是手势起作用
         * 如果想要在这三种手势中进行何种操作,
         * 将代码放在对应的方法中即可。
         */
        gestureDetector = new GestureDetector(
                TouchImageViewActivity.this,
                new GestureDetector.SimpleOnGestureListener(){
            @Override
            public boolean onDown(MotionEvent e) {
                return false;
            }
            @Override
            public void onLongPress(MotionEvent e) {
                super.onLongPress(e);
            }
        });
        gestureDetector.setOnDoubleTapListener(new OnDoubleTapListener() {
            @Override
            public boolean onDoubleTapEvent(MotionEvent e) {
                return false;
            }
            /**
             * 双击手势的时候 会触发该方法一次
             * 所以双击手势对应的代码应放在这里
             */
            @Override
            public boolean onDoubleTap(MotionEvent e) {
                mid.x=e.getX();
                mid.y=e.getY();
                matrix1.set(savedMatrix);
                if (isFangda) {
                    matrix1.postScale(0.5f, 0.5f, mid.x, mid.y);//缩小
                    isFangda = false;
                }else {
                    matrix1.postScale(2f, 2f, mid.x, mid.y);// 放大
                    isFangda = true;
                }
                matrix.set(matrix1);
                center(true, true);
                touchImageView.setImageMatrix(matrix);
                return true;
            }
            /**
             * 单击手势 会触发该方法一次
             * 单击对应的代码应放在这里
             */
            @Override
            public boolean onSingleTapConfirmed(MotionEvent e) {
                //点击图片 销毁activity
                TouchImageViewActivity.this.finish();
                return false;
            }
        });
        /**
         * 初始化 将图片放在屏幕中心位置
         */
        center(true, true);
        /**
         * 图片设置中心之后,重新设置图片的缩放矩阵
         */
        touchImageView.setImageMatrix(matrix);
        /**
         * 设置ImageView的触摸事件
         */
        touchImageView.setOnTouchListener(new OnTouchListener() {
        @SuppressLint("ClickableViewAccessibility")
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            onTouchEvent(event);
            return true;
        }
    });
 }
    public boolean onTouchEvent(MotionEvent event) {
        /**
         * 在这里调用手势的方法
         * 这是手势和触摸事件同时使用的方法
         */
        if (gestureDetector.onTouchEvent(event)) {
            return true;
        }else {
            switch (event.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:
                mode = DRAG;
                x_down = event.getX();
                y_down = event.getY();
                /**
                 * 单个手指放下,首先保存图片的缩放矩阵到savedMatrix
                 */
                savedMatrix.set(matrix);
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                mode = ZOOM;
                /**
                 * 第二个手指刚放下时
                 * 计算两个手指间的距离
                 */
                oldDist = spacing(event);
                /**
                 * 第二个手指刚放下时
                 * 计算两个手指见的旋转角度
                 */
                oldRotation = rotation(event);
                savedMatrix.set(matrix);
                /**
                 * 第二个手指刚放下时
                 * 计算两个手指见的中间点坐标,并存在mid中
                 */
                midPoint(mid, event);
                break;
            case MotionEvent.ACTION_MOVE:
                if (mode == ZOOM) {
                    matrix1.set(savedMatrix);
                    /**
                     * 两个手指开始移动
                     * 计算移动后旋转角度
                     */
                    newRotation = rotation(event);
                    /**
                     * 两个角度之差
                     * 即是图片的旋转角度
                     */
                    rotation = newRotation - oldRotation;
                    /**
                     * 计算移动后两点间的中间点
                     */
                    float newDist = spacing(event);
                    /**
                     * 两个中间点的商即时放大倍数
                     */
                    float scale = newDist / oldDist;

                    matrix1.postScale(scale, scale, mid.x, mid.y);// 縮放
                    matrix1.postRotate(rotation, mid.x, mid.y);// 旋轉
                    matrix.set(matrix1);
                    /**
                     * 调用该方法即可重新图片
                     */
                    touchImageView.setImageMatrix(matrix);
                } else if (mode == DRAG) {
                    matrix1.set(savedMatrix);
                    float tx = event.getX() - x_down;
                    float ty = event.getY() - y_down;
                    /**
                     * 单个手指移动后的距离大于20 才算作移动
                     */
                    if (Math.sqrt(tx*tx+ty*ty)>20f) {
                        /**
                         * 设置图片宽高与屏幕宽高的大小的boolean类型的值
                         */
                        isCheckRightAndLeft = isCheckTopAndBottom = true;
                        /**
                         * 得到目前图片的宽高
                         */
                        RectF rectF = getMatrixRectF();
                        /**
                         * 图片宽度小于屏幕大小
                         * 不移动
                         */
                        if (rectF.width()<widthScreen) {
                            tx = 0;
                            isCheckRightAndLeft = false;
                        }
                        /**
                         * 图片高度小于屏幕高度
                         * 不移动
                         */
                        if (rectF.height()<heightScreen) {
                            ty = 0;
                            isCheckTopAndBottom = false;
                        }
                        matrix1.postTranslate(tx,ty);// 平移
                        /**
                         * 如果想在拖动图片的同时检测图片边缘是否
                         * 到达屏幕的边缘,则取消下面的注释
                         */
//                      matrix.set(matrix1);
//                      checkDxDyBounds();
                        matrix.set(matrix1);
                        touchImageView.setImageMatrix(matrix);
                    }
                }
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_POINTER_UP:
                if (mode == ZOOM) {
                    /**
                     * 双手放开,停止图片的旋转和缩放
                     * Reset_scale还原图片的缩放比例
                     */
                    matrix1.postScale(Reset_scale, Reset_scale, mid.x, mid.y);
                    /**
                     * 双手放开,停止缩放、旋转图片,此时根据已旋转的角度
                     * 计算还原图片的角度,最终的效果是把图片竖直或横平方正。
                     */
                    setRotate();
                    matrix.set(matrix1);
                    /**
                     * 将图片放在屏幕中间位置
                     */
                    center(true, true);
                    touchImageView.setImageMatrix(matrix);
                    matrix1.reset();
                }else if (mode == DRAG) {
                    /**
                     * 单手拖动图片,放开手指,停止拖动
                     * 此时检测图片是否已经偏离屏幕边缘
                     * 如果偏离屏幕边缘,则图片回弹
                     */
                    checkDxDyBounds();
                    matrix.set(matrix1);
                    touchImageView.setImageMatrix(matrix);
                    matrix1.reset();
                }
                mode = NONE;
                break;
            }
            return true;
        }
   }
    /**
     * 根据当前图片的Matrix获得图片的范围
     * 这里获取的是当前显示的图片的大小。
     * 图片放大后,获取的就是图片放大后的图片的大小。
     * 图片缩小后,获取的就是图片缩小后的图片的大小。
     *
     * 这个大小与图片的大小是有区别的。
     * 下面是固定用法,记住即可。
     * @return
     */
    private RectF getMatrixRectF()
    {
        Matrix m = matrix;
        RectF rect = new RectF();
        Drawable d = touchImageView.getDrawable();
        if (null != d)
        {
            rect.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
        }
        return rect;
    }
    /**
     * 横向、纵向 图片居中
     */
    protected void center(boolean horizontal, boolean vertical) {
        RectF rect = getMatrixRectF();
        float deltaX = 0, deltaY = 0;
        float height = rect.height();
        float width = rect.width();
        if (vertical) {
            /**
             *  图片小于屏幕大小,则居中显示。
             *  大于屏幕,如果图片上方留空则往上移,
             *  图片下方留空则往下移
             */
            int screenHeight = heightScreen;
            if (height < screenHeight) {
                deltaY = (screenHeight - height) / 2 - rect.top;
            } else if (rect.top > 0) {
                deltaY = -rect.top;
            } else if (rect.bottom < screenHeight) {
                deltaY = touchImageView.getHeight() - rect.bottom;
            }
        }

        if (horizontal) {
            int screenWidth = widthScreen;
            if (width < screenWidth) {
                deltaX = (screenWidth - width) / 2 - rect.left;
            } else if (rect.left > 0) {
                deltaX = -rect.left;
            } else if (rect.right < screenWidth) {
                deltaX = screenWidth - rect.right;
            }
        }
        matrix.postTranslate(deltaX, deltaY);
    }

    // 触碰两点间距离
    private float spacing(MotionEvent event) {
        float x = event.getX(0) - event.getX(1);
        float y = event.getY(0) - event.getY(1);
        return (float) Math.sqrt(x * x + y * y);
    }

    // 取手势中心点
    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 float rotation(MotionEvent event) {
        double delta_x = (event.getX(0) - event.getX(1));
        double delta_y = (event.getY(0) - event.getY(1));
        /**
         * 反正切函数
         * 计算两个坐标点的正切角度
         */
        double radians = Math.atan2(delta_y, delta_x);
        return (float)(Math.toDegrees(radians));
    }
    /**
     * 手指松开,确定旋转的角度
     */
    private void setRotate(){

    }
    /**
     * 检测图片偏离屏幕两边的距离
     * 然后平移,是图片边缘在屏幕边,
     * 使图片周围没有空白
     */
    private void checkDxDyBounds(){
        RectF rectF = getMatrixRectF();
        float dx =0.0f,dy=0.0f;
        /**
         * 如果图片的左侧大于零,说明图片左侧向右
         * 偏离了左侧屏幕,则左移偏离的距离.
         * rectF.left的值,是基于左侧坐标计算的。
         * 图片正常情况下,该值为0.
         * 当图片向右侧拖动以后,该值大于0.
         * 当图片向左侧拖动以后,该值小于0.
         */
        if (rectF.left>0&&isCheckRightAndLeft) {
            dx = -rectF.left;
        }
        /**
         * 如果图片的右侧偏离屏幕的右侧,则
         * 图片右移图片的宽度与图片显示的宽度的差.
         *
         * rectF.right的值,是基于左侧计算的,图片没有缩放旋转情况下,
         * 该值==touchImageView.getWidth()图片的宽度。
         * 当拖动图片以后,该值变化,等于显示的图片的宽度
         */
        if (rectF.right<touchImageView.getWidth()&&isCheckRightAndLeft) {
            dx=touchImageView.getWidth()-rectF.right;
        }
        /**
         * 当图片顶部大于0,说明图片向下偏离屏幕顶部,
         * 则图片向上回弹偏离的距离。
         *
         * rectF.top的值基于顶部坐标,
         * 图片正常情况下,该值=0.
         */
        if (rectF.top>0&&isCheckTopAndBottom) {
            dy=-rectF.top;
        }
        /**
         * 当图片底部小于图片高度时,图片偏离屏幕底部
         * 则图片回弹图片的高度与显示的图片的高度之差。
         *
         * rectF.bottom的值,基于顶部坐标。
         * 图片正常情况下,该值=图片的高度。
         */
        if (rectF.bottom<touchImageView.getHeight()&&isCheckTopAndBottom) {
            dy=touchImageView.getHeight()-rectF.bottom;
        }
    }
}

最后再次给出下载地址:

源码下载

由于是本人花了几天功夫写的,所以代码并没有免费下载,大家见谅!!

大家如果有什么问题,欢迎留言,本人会尽快回复的!^_^【握手】

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-11-10 14:16:40

android-ImageView的拖动、旋转、缩放、边界回弹、双击缩放、单击销毁及源码下载的相关文章

Android 音视频深入 十九 使用ijkplayer做个视频播放器(附源码下载)

项目地址https://github.com/979451341/Myijkplayer 前段时候我觉得FFmpeg做个视频播放器好难,虽然播放上没问题,但暂停还有通过拖动进度条来设置播放进度,这些都即便做得到,可以那个延缓..... 现在学习一下目前移动端最知名的视频播放器的框架ijkplayer,这个框架他是基于FFmpeg.SDL.还有安卓原生API MediaCodec之类的.他是没有播放界面的,这个需要我们去做,所以这个里我就做个基于ijkplayer的视频播放器,随便浅显的说一下ij

Android 音视频深入 九 FFmpeg解码视频生成yuv文件(附源码下载)

项目地址,求star https://github.com/979451341/Audio-and-video-learning-materials/tree/master/FFmpeg(MP4%E8%BD%ACyuv%EF%BC%89 这一次是将MP4解码出yuv文件出来,先介绍一波yuv文件 YUV是指亮度参量和色度参量分开表示的像素格式,而这样分开的好处就是不但可以避免相互干扰,还可以降低色度的采样率而不会对图像质量影响太大.YUV是一个比较笼统地说法,针对它的具体排列方式,可以分为很多种

【流媒体开发】VLC Media Player - Android 平台源码编译 与 二次开发详解 (提供详细800M下载好的编译源码及eclipse可调试播放器源码下载)

作者 : 韩曙亮  博客地址 : http://blog.csdn.net/shulianghan/article/details/42707293 转载请注明出处 : http://blog.csdn.net/shulianghan VLC 二次开发 视频教程 : http://edu.csdn.net/course/detail/355 博客总结 : -- 本博客目的 : 让 Android 开发者通过看本博客能够掌握独立移植 VLC Media Player 核心框架到自己的 app 中,

Android图片加载框架最全解析(二),从源码的角度理解Glide的执行流程

转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/53939176 本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 郭霖 即可关注,每天都有文章更新. 在本系列的上一篇文章中,我们学习了Glide的基本用法,体验了这个图片加载框架的强大功能,以及它非常简便的API.还没有看过上一篇文章的朋友,建议先去阅读 Android图片加载框架最全解析(一),Glide的基本用法 . 在多数情况下,我们想要在界面上加载并展示一

通过scaleType裁剪大发888源码下载缩放图片适配不同屏幕不同

后特意大发888源码下载论坛:haozbbs.com Q1446595067设置一个ImageView宽高均为600pix.观察不同scaleType配置下,Android对原图的裁剪缩放效果.左侧为代码,右侧为缩放和裁剪效果.(1)android:scaleType="fitXY" 可见是铺面整个ImageView进行拉伸缩放.不对原图进行裁剪,仅仅缩放. (2)android:scaleType="center" 这种情况下,要对原图裁剪,以图片中心为基准,以中

Android网络:HTTP之利用HttpURLConnection访问网页、获取网络图片实例 (附源码)

http://blog.csdn.net/yanzi1225627/article/details/22222735 如前文所示的TCP局域网传送东西,除了对传输层的TCP/UDP支持良好外,Android对HTTP(超文本传输协议)也提供了很好的支持,这里包括两种接口: 1.标准Java接口(java.net) ----HttpURLConnection,可以实现简单的基于URL请求.响应功能: 2.Apache接口(org.appache.http)----HttpClient,使用起来更方

Android UI开发: 横向ListView(HorizontalListView)及一个简单相册的完整实现 (附源码下载)

Android UI开发: 横向ListView(HorizontalListView)及一个简单相册的完整实现 (附源码下载) POSTED ON 2014年6月27日 BY 天边的星星 本文内容: 1.横向ListView的所有实现思路; 2.其中一个最通用的思路HorizontalListView,并基于横向ListView开发一个简单的相册: 3.实现的横向ListView在点击.浏览时item背景会变色,并解决了listview里setSelected造成item的选择状态混乱的问题.

Android源码下载

Android源码下载 Linux系统有很多工具和插件,这些插件Linux源生不附带,需要的话,需要自行下载.官方已经把所有插件部署在Linux的源上,Android源码的下载,是断点续传的,但是临时文件很大,至少预备40G空间,当然下载时间也会很长. > Android源码下载支持的系统目前只有Ubuntu和Mac OS两种操作系统, 本次以Ubuntu系统为例.> 官方网站:http://source.android.com/source/downloading.html 1. 下载Git

Android学习笔记(十五)——碎片的生命周期(附源码)

碎片的生命周期 点击下载源码 与活动类似,碎片具有自己的生命周期.理解了碎片的生命周期后,我们可以在碎片被销毁时正确地保存其实例,在碎片被重建时将其还原到前一个状态. 1.使用上一篇的项目Fragments,在Fragment1.java文件中添加如下代码: package net.zenail.Fragments; import android.app.Activity; import android.os.Bundle; import android.support.v4.app.Fragm