标签:imageview 源码 图片缩放回弹拖动 图片双击单击旋转 自定义image
博客地址: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;
}
}
}
最后再次给出下载地址:
源码下载
由于是本人花了几天功夫写的,所以代码并没有免费下载,大家见谅!!
大家如果有什么问题,欢迎留言,本人会尽快回复的!^_^【握手】
版权声明:本文为博主原创文章,未经博主允许不得转载。
android-ImageView的拖动、旋转、缩放、边界回弹、双击缩放、单击销毁及源码下载
标签:imageview 源码 图片缩放回弹拖动 图片双击单击旋转 自定义image
原文地址:http://blog.csdn.net/u010156024/article/details/48047737