Android-图片预览(自定义ImageView实现图片缩放,多点触控,自由移动)

来源:互联网 发布:域名不给转出 编辑:程序博客网 时间:2024/05/02 00:39

1.回顾

   上篇学习了,开源框架中:个性通知效果,7种动画实现;非常不错;

2.重点

   (1)实现自定义控件ImageView

   (2)自由的放大和缩小

   (3)自由移动

 3.背景

 (1) 因为需要使用到 图片预览效果,包括 触控放大缩小,双击 放大缩小,自由移动;

 (2) 就在 慕课网上看了 hyman 老师的课程 《打造个性的图片预览与多点触控课程》;

 (3)学习过程中收获很多,懂一些原理知识;

 (4) 但是 最终效果 稍微有点参差,在自由移动的时候,只能在放大的地方自由移动,但不影响平常使用就很好了;

 (5)后自己在开源框架中,挖取了 图片预览的 自定义ImageView ,封装成了 jar包,非常完美;

4.课程学习原理源码

package com.example.view;import android.annotation.SuppressLint;import android.content.Context;import android.graphics.Matrix;import android.graphics.RectF;import android.graphics.drawable.Drawable;import android.support.v4.view.ViewPager;import android.util.AttributeSet;import android.util.Log;import android.view.GestureDetector;import android.view.MotionEvent;import android.view.ScaleGestureDetector;import android.view.ScaleGestureDetector.OnScaleGestureListener;import android.view.View;import android.view.View.OnTouchListener;import android.view.ViewConfiguration;import android.view.ViewTreeObserver.OnGlobalLayoutListener;import android.widget.ImageView;@SuppressLint("NewApi")public class ZoomImageView extends ImageView implements OnGlobalLayoutListener,OnScaleGestureListener, OnTouchListener {/** * 自由的放大 和 缩小 放大 可以 自由的 移动 处理 和viewpager 事件冲突 *  *  * 1.Matrix 2.ScaleGestureDetector 3.GestureDetector 4.事件分发机制 *  *  * 1.实现 *  */// 第一次运行 初始化private boolean isOnce = false;// 缩放比例private float initScale;private float minScale;private float maxScale;// 缩放实现private Matrix matrix;// 多点触控private ScaleGestureDetector scaleGestureDetector;// 自由移动的比较值private int touchSlop;// 双击 放大缩小private GestureDetector gestureDetector;// 判断双击中private boolean isDoubletag = false;/** * 重写 3个 构造方法 *  * @param context * @param attrs * @param defStyleAttr */public ZoomImageView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);//matrix = new Matrix();setScaleType(ScaleType.MATRIX);// 初始化操作写在 3个参数的 构造函数里scaleGestureDetector = new ScaleGestureDetector(context, this);setOnTouchListener(this);// 初始化 比较值touchSlop = ViewConfiguration.get(context).getScaledDoubleTapSlop();// 双击放大缩小gestureDetector = new GestureDetector(context,new GestureDetector.SimpleOnGestureListener() {@Overridepublic boolean onDoubleTap(MotionEvent e) {// 双击事件if (isDoubletag) {return isDoubletag;}float x = e.getX();float y = e.getY();if (getScale() < minScale) {postDelayed(new AutoScaleRunnable(minScale, x, y),16);isDoubletag = true;} else {postDelayed(new AutoScaleRunnable(initScale, x, y),16);isDoubletag = true;}return true;}});}/** * 自动放大 缩小 缓慢的缩放 *  * @author yuan * */private class AutoScaleRunnable implements Runnable {private float mTargetScale;private float x;private float y;private final float BIGGER = 1.07f;private final float SMALL = 0.93f;private float tmpScale;/** * 实现构造方法 *  * @param mTargetScale * @param x * @param y */public AutoScaleRunnable(float mTargetScale, float x, float y) {this.mTargetScale = mTargetScale;this.x = x;this.y = y;if (getScale() < mTargetScale) {tmpScale = BIGGER;}if (getScale() > mTargetScale) {tmpScale = SMALL;}}@Overridepublic void run() {//matrix.postScale(tmpScale, tmpScale, x, y);checkBorderAndCenterWhenScale();setImageMatrix(matrix);float currentScale = getScale();if ((tmpScale > 1.0f && currentScale < mTargetScale)|| (tmpScale < 1.0f && currentScale > mTargetScale)) {postDelayed(this, 16);} else {float scale = mTargetScale / currentScale;matrix.postScale(scale, scale, x, y);checkBorderAndCenterWhenScale();setImageMatrix(matrix);isDoubletag = false;}}}public ZoomImageView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public ZoomImageView(Context context) {this(context, null);}@Overrideprotected void onAttachedToWindow() {// 注册 GlobalListenersuper.onAttachedToWindow();getViewTreeObserver().addOnGlobalLayoutListener(this);}@SuppressLint("NewApi")@Overrideprotected void onDetachedFromWindow() {// 移除 GlobalListenersuper.onDetachedFromWindow();getViewTreeObserver().removeOnGlobalLayoutListener(this);}@Overridepublic void onGlobalLayout() {// 初始化if (!isOnce) {// 控件的宽和高int width = getWidth();int height = getHeight();// 获得图片的宽和高Drawable d = getDrawable();if (d == null) {return;}int imgWidth = d.getIntrinsicWidth();int imgHeight = d.getIntrinsicHeight();// 缩放比例float scale = 1.0f;if (imgWidth > width && imgHeight < height) {scale = width * 1.0f / imgWidth;} else if (imgHeight > height && imgWidth < width) {scale = height * 1.0f / imgHeight;} else if ((imgWidth > width && imgHeight > height)|| (imgWidth < width && imgHeight < height)) {scale = Math.min(width * 1.0f / imgWidth, height * 1.0f/ imgHeight);}/** * 初始化 缩放比例 */initScale = scale;maxScale = scale * 4;minScale = scale * 2;Log.i("TAG", scale + "");/** * 将图片移动到 图片中心 *  */int mx = width / 2 - imgWidth / 2;int my = height / 2 - imgHeight / 2;/** * Matrix 3*3 矩阵 xScale xskew xTrans yScale yskew yTrans 0 0 0 *  * 使用 post 操作 */matrix.postTranslate(mx, my);matrix.postScale(initScale, initScale, width / 2, height / 2);setImageMatrix(matrix);isOnce = true;}}/** * 拿到当前图片的缩放值 *  * @return */public float getScale() {float[] values = new float[9];matrix.getValues(values);return values[Matrix.MSCALE_X];}@Overridepublic boolean onScale(ScaleGestureDetector detector) {// 缩放比例 initScale maxScalefloat scaleFactor = detector.getScaleFactor();if (getDrawable() == null) {return true;}float scale = getScale();// 缩放控制if ((scale < maxScale && scaleFactor > 1.0f)|| (scale > initScale && scaleFactor < 1.0f)) {if (scale * scaleFactor < initScale) {scaleFactor = initScale / scale;} else if (scale * scaleFactor > maxScale) {scale = maxScale / scale;}matrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(),detector.getFocusY());checkBorderAndCenterWhenScale();setImageMatrix(matrix);}return false;}@Overridepublic boolean onScaleBegin(ScaleGestureDetector detector) {// 返回truereturn true;}@Overridepublic void onScaleEnd(ScaleGestureDetector detector) {// TODO Auto-generated method stub}// ---------------------------------------------------------自由移动// 存储最后的位置private int lastPointCount;private float Lx;private float Ly;private boolean isDrag;private boolean isCheckLeftAndRight, isCheckTopAndBottom = false;@Overridepublic boolean onTouch(View v, MotionEvent event) {        Log.i("TAG","onTouch"+1);// 双击的时候不让其 移动if (gestureDetector.onTouchEvent(event)) {return true;}// 设置 onTouch 事件scaleGestureDetector.onTouchEvent(event);// 自由移动实现float x = 0, y = 0;int pointerCount = event.getPointerCount();for (int i = 0; i < pointerCount; i++) {x += event.getX(i);y += event.getY(i);}        Log.i("TEST","pointerCount:"+pointerCount+"X:"+x+"Y:"+y);x /= pointerCount;y /= pointerCount;if (lastPointCount != pointerCount) {isDrag = false;Lx = x;Ly = y;}lastPointCount = pointerCount;RectF f = getMatrixRectF();// 处理事件冲突问题!!switch (event.getAction()) {case MotionEvent.ACTION_DOWN:  Log.i("TAG","onTouch"+2);// 解决事件冲突// 当图片的 高度和宽度 大于屏幕的寬高度的时候,为图片的事件,否则为viewPagerif (ischong(f)) {if (getParent() instanceof ViewPager) {getParent().requestDisallowInterceptTouchEvent(true);}}break;case MotionEvent.ACTION_MOVE:  Log.i("TAG","onTouch"+3);// 解决事件冲突if (ischong(f)) {if (getParent() instanceof ViewPager) {getParent().requestDisallowInterceptTouchEvent(true);}}// 正在移动float dx = x - Lx;float dy = y - Ly;if (!isDrag) {isDrag = isMoveAction(dx, dy);}if (isDrag) {if (getDrawable() != null) {isCheckLeftAndRight = isCheckTopAndBottom = true;// 如果高度 小于控件宽度,不允许横向移动if (f.width() < getWidth()) {isCheckLeftAndRight = false;dx = 0;}if (f.height() < getHeight()) {isCheckTopAndBottom = false;dy = 0;}matrix.postTranslate(dx, dy);checkBorderWhenTranslate();setImageMatrix(matrix);setFocusable(true);}}Ly = y;Lx = x;break;case MotionEvent.ACTION_CANCEL:  Log.i("TAG","onTouch"+4);lastPointCount = 0;break;}return true;}/** * 解决 事件冲突 *  * @param f * @return */private boolean ischong(RectF f) {return (f.width() > getWidth() + 0.01)|| (f.height() > getHeight() + 0.01);}/** * 移动 判断边界 */private void checkBorderWhenTranslate() {RectF rectf = getMatrixRectF();float dx = 0;float dy = 0;int width = getWidth();int height = getHeight();if (rectf.top > 0 && isCheckTopAndBottom) {dy = -rectf.top;}if (rectf.bottom < height && isCheckTopAndBottom) {dy = height - rectf.bottom;}if (rectf.right < width && isCheckLeftAndRight) {dx = width - rectf.right;}if (rectf.left > 0 && isCheckLeftAndRight) {dx = -rectf.left;}matrix.postTranslate(dx, dy);}/** * 判断是否移动 *  * @param dx * @param dy * @return */private boolean isMoveAction(float dx, float dy) {return Math.sqrt(dx * dx + dy * dy) > touchSlop;}// ------------------------------------------------比例缩放/** * 获得 图片放大缩小 以后的宽和高 *  * @return */private RectF getMatrixRectF() {Matrix smatrix = matrix;RectF rectF = new RectF();Drawable drawable = getDrawable();if (drawable != null) {rectF.set(0, 0, drawable.getIntrinsicWidth(),drawable.getIntrinsicHeight());smatrix.mapRect(rectF);}return rectF;}/** * 防止缩放时,出现白边 */private void checkBorderAndCenterWhenScale() {RectF rectf = getMatrixRectF();float dx = 0;float dy = 0;int width = getWidth();int height = getHeight();// 防止出现 白边if (rectf.width() >= width) {if (rectf.left > 0) {dx = -rectf.left;}if (rectf.right < width) {dx = width - rectf.right;}}if (rectf.height() >= height) {if (rectf.top > 0) {dy = -rectf.top;}if (rectf.bottom < height) {dy = height - rectf.bottom;}}// 如果 宽度和高度 小于 控件宽度和高度,则让其 居中;if (rectf.width() < width) {dx = width / 2 - rectf.right + rectf.width() / 2;}if (rectf.height() < height) {dy = height / 2 - rectf.bottom + rectf.height() / 2;}matrix.postTranslate(dx, dy);}}


5.调用

  

  <com.example.view.ZoomImageView       android:id="@+id/img_scale"       android:layout_width="fill_parent"       android:layout_height="fill_parent"       android:scaleType="matrix"       android:src="@drawable/img" />

6.总结

   通过 这篇课 可以学到很多 原理知识;

   下篇将 分享 另一个非常完美的 图片预览 demo

1 0