自定义View实现图片的拖动和缩放
来源:互联网 发布:windows defender在哪 编辑:程序博客网 时间:2024/05/21 10:34
转载自http://blog.csdn.net/kevinscsdn/article/details/52448691
整体思路:
1. 实现缩放功能:
(1) 创建ScaleGestureDetector对象,实现ScaleGestureDetector.OnScaleGestureListener接口;
(2) 在onScale方法中实现缩放逻辑 , 相关逻辑包括获取缩放比例的初始值、定义放大的上限比例;
(3) setOnTouchListener(this),实现OnTouchListener接口,接收触摸事件。
2. 实现拖动功能:
(1) 计算、修正拖动距离
(2) 实现拖动
更多细节呈现在代码中。
代码实现
public class ScalableImageView extends ImageView implements ScaleGestureDetector.OnScaleGestureListener, View.OnTouchListener, ViewTreeObserver.OnGlobalLayoutListener { private static final String TAG = "ScalableImageView"; private int gesture; private static final int GESTURE_DRAG = 1; private static final int GESTURE_ZOOM = 2; // 最大的缩放比例 private static final float MAX_SCALE = 4.0f; // 初始化时的缩放比例,如果图片宽或高大于屏幕,此值将小于1(HongYang大神的博客上有笔误) private float initScale = 1.0f; private Matrix mMatrix = new Matrix(); private ScaleGestureDetector mScaleGestureDetector; private int viewWidth; private int viewHeight; private Drawable mDrawable; public ScalableImageView(Context context) { this(context, null); } public ScalableImageView(Context context, AttributeSet attrs) { super(context, attrs);// 这一步很关键 super.setScaleType(ScaleType.MATRIX); mScaleGestureDetector = new ScaleGestureDetector(context, this);// 下面一步必须有 setOnTouchListener(this); } @Override public boolean onScale(ScaleGestureDetector scaleGestureDetector) {// 全局缩放比例 float scale = getScale();// 上一次缩放事件到当前事件的缩放比例(微分缩放比例) float factor = scaleGestureDetector.getScaleFactor();// 第一次获取的factor偏小,会引起缩放手势触屏的一瞬间图片缩小 if (resetFactor) { factor = 1.0f; resetFactor = false; }// drawable为空或者缩放比例超出范围,拒执行 if (getDrawable() == null || scale * factor > MAX_SCALE || scale * factor < initScale) { return false; } else { mMatrix.postScale(factor, factor, getWidth() / 2, getHeight() / 2); setImageMatrix(mMatrix); return true; }// 以下是HongYang大神的思路// 在INIT_VALUE到MAX_SCALE范围内缩放(放大不超过MAX_SCALE,缩小不小于INIT_SCALE)// if ((scale < MAX_SCALE && factor >= 1.0f) || (scale > initScale && factor <= 1.0f)) {// if (scale * factor > MAX_SCALE) {//放大超过MAX_SCALE的处理// factor = MAX_SCALE / scale;// } else if (scale * factor < initScale) {//缩小超过INIT_SCALE的处理// factor = initScale / scale;// }// mMatrix.postScale(factor, factor, getWidth() / 2, getHeight() / 2);// setImageMatrix(mMatrix);// }// return true; } private final float[] matrixValues = new float[9]; // 获取全局缩放比例(相对于未缩放时的缩放比例),和直接用getScaleX()有区别! private float getScale() { mMatrix.getValues(matrixValues); return matrixValues[Matrix.MSCALE_X]; } // 缩放开始时:可用于过滤一些手势,比如从有效区以外的区域划进来的手势 @Override public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) { return true; } // 缩放结束时 @Override public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) { } private float mLastX; private float mLastY; private boolean resetFactor; private boolean onZoomFinished; @Override public boolean onTouch(View v, MotionEvent event) { float x = event.getX(); float y = event.getY(); switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: gesture = GESTURE_DRAG; break; case MotionEvent.ACTION_POINTER_DOWN: gesture = GESTURE_ZOOM; resetFactor = true; break; case MotionEvent.ACTION_POINTER_UP: gesture = GESTURE_DRAG; onZoomFinished = true; break; case MotionEvent.ACTION_MOVE: switch (gesture) { case GESTURE_DRAG: // 屏蔽缩放转换为拖动时的跳动,此时dx、dy偏大。 if (onZoomFinished) { onZoomFinished = false; break; } float dx = x - mLastX; float dy = y - mLastY; // 对偏移值做适当修正,避免图片与视图边间产生空白区域 PointF dragDelta = amendDelta(dx, dy); mMatrix.postTranslate(dragDelta.x, dragDelta.y); setImageMatrix(mMatrix); break; case GESTURE_ZOOM: mScaleGestureDetector.onTouchEvent(event); break; } break; case MotionEvent.ACTION_UP: // 消除缩放造成的图片距视图边的空白 PointF zoomDelta = amendDelta(0, 0); mMatrix.postTranslate(zoomDelta.x, zoomDelta.y); setImageMatrix(mMatrix); break; } mLastX = x; mLastY = y; return true; } private PointF amendDelta(float dx, float dy) { RectF rectF = getRectF(); if (rectF.width() > viewWidth) {// 图片宽度超过视图宽度 if (rectF.left + dx > 0) {// 拖动会引起图片左边出现空白 dx = -rectF.left; } else if (rectF.right + dx < viewWidth) {// 拖动会引起图片右边出现空白 dx = viewWidth - rectF.right; } } else {// 图片宽度不及视图宽度,不允许图片宽度方向可视区域离开边界 if (rectF.left + dx < 0) { dx = -rectF.left; } else if (rectF.right + dx > viewWidth) { dx = viewWidth - rectF.right; } } if (rectF.height() > viewHeight) { if (rectF.top + dy > 0) { dy = -rectF.top; } else if (rectF.bottom + dy < viewHeight) { dy = viewHeight - rectF.bottom; } } else { if (rectF.top + dy < 0) { dy = -rectF.top; } else if (rectF.bottom + dy > viewHeight) { dy = viewHeight - rectF.bottom; } } return new PointF(dx, dy); } private RectF mRectF; public RectF getRectF() { if (mDrawable == null) { mDrawable = getDrawable(); } if (mRectF == null) { mRectF = new RectF(); } if (mDrawable != null) { mRectF.set(0, 0, mDrawable.getIntrinsicWidth(), mDrawable.getIntrinsicHeight()); mMatrix.mapRect(mRectF); } return mRectF; } // onGlobalLayoutListener可能会多次触发 private boolean isFirstTime = true; // 观察布局变化,目的是获取View的尺寸,在测量之前执行onMeasure getWidth()和getHeight()可能为0 @Override public void onGlobalLayout() { if (isFirstTime) { Drawable drawable = getDrawable(); if (drawable == null) { return; } isFirstTime = false; int drawableWidth = drawable.getIntrinsicWidth(); int drawableHeight = drawable.getIntrinsicHeight(); viewWidth = getWidth(); viewHeight = getHeight();// 图片长宽超出View的可见范围的处理 if (drawableWidth > viewWidth || drawableHeight > viewHeight) { initScale = Math.min(viewWidth * 1.0f / drawableWidth, viewHeight * 1.0f / drawableHeight); }// 将图片偏移到中心位置 mMatrix.postTranslate((viewWidth - drawableWidth) / 2, (viewHeight - drawableHeight) / 2);// 初始化缩放 mMatrix.postScale(initScale, initScale, viewWidth / 2, viewHeight / 2); setImageMatrix(mMatrix); } }// 当View附加到Window上时调用,这时候它的绘画面板已存在,即将开始绘制。注意,该方法能保证发生在onDraw之前,但可能发生在onMeasure之前或之后。// HongYang大神写在此方法中,考虑到上述原因,这里写在onMeasure中,见仁见智。// @Override// protected void onAttachedToWindow() {// super.onAttachedToWindow();// getViewTreeObserver().addOnGlobalLayoutListener(this);// } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { getViewTreeObserver().addOnGlobalLayoutListener(this); super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { getViewTreeObserver().removeOnGlobalLayoutListener(this); } }}
关于偏移量修正的示意图如下,以宽度方向为例,当图片宽度小于View的宽度时,无论如何,图片都会与View都某一边产生距离的,这时候要求伸出View的图片部分缩回来;当图片宽度大于View的宽度时,如果与View边缘产生距离,要求将该空白填充满。总之一个原则,充分利用View的区域,尽可能显示更多的图片内容。
0 0
- 自定义View实现图片的拖动和缩放
- 自定义View实现图片的拖动和缩放
- 自定义ImageView实现图片的拖动、缩放和边界回弹
- Android-实现图片的拖动和缩放
- Android实现图片的缩放和拖动
- 自定义View实现图片的缩放
- 自定义View实现图片缩放
- 自定义类继承ImageView 实现多点图片触碰的拖动和缩放
- 自定义ImageView实现图片的单指拖动和两指缩放
- 自定义类继承ImageView 实现多点图片触碰的拖动和缩放
- Android基于多触控的图片缩放和拖动代码实现
- Android基于多触控的图片缩放和拖动代码实现
- wpf实现图片拖动和缩放
- Android 实现图片缩放和拖动
- android自定义View实现图片的绘制、旋转、缩放
- android Matrix图片的缩放和拖动
- 自定义的ImageView控制,可对图片进行多点触控缩放和拖动
- Android自定义View来实现解析lrc歌词并同步滚动、上下拖动、缩放歌词的功能[转]
- Http 中Connection: close和Connection: keep-alive有什么区别?
- HDU 2066 Dijk 求最短路
- 初学Dagger2
- Linux相关命令百科释义
- M.LCS打印
- 自定义View实现图片的拖动和缩放
- 提交本地代码到github
- 干货!8套H5 App完整源码!果断收藏!
- 线程相关的工具类
- Win10 使用Office 2016 出现多个授权信息无法激活的问题以及解决办法
- Framebufffer 初探
- ListView中嵌套ViewPager滑动事件冲突
- 自定义控件之绘图篇(一):概述及基本几何图形绘制
- Python 正则式学习笔记