Android自定义ImageView实现图片缩放滑动,双击放大缩小,多点触控缩放
Posted 胖子爱你520
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android自定义ImageView实现图片缩放滑动,双击放大缩小,多点触控缩放相关的知识,希望对你有一定的参考价值。
转载请注明出处:http://blog.csdn.net/woshizisezise/article/details/47445605
大家好,最近研究学习了一个自定义控件,ZoomImageView,安卓自定义缩放图片的控件,支持双击放大双击缩小,多点触控拉伸放大与缩小,并且很好的控制了图片的位置,不会出现缩小图片后图片左右出现空白的情况,缩小到默认值的时候,图片会居中填充窗口,可以推荐大家学习一下,先看下效果图,具体代码如下所示。
(1)首先在activity_main.xml布局文件中引用这个自定义控件的全路径名:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" > <com.example.zoomimageview.view.ZoomImageView android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="matrix" android:src="@drawable/baby1" /> </RelativeLayout>
(2)现在也是实现最核心的一步了,编写我们的ZoomImageView控件了,代码中注释都已经标的很清楚了,大家可以参照着看:
package com.example.zoomimageview.view; 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.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; public class ZoomImageView extends ImageView implements OnGlobalLayoutListener, OnScaleGestureListener, OnTouchListener private boolean mOnce; /** * 初始化时缩放的值 */ private float mInitScale; /** * 双击放大值到达的值 */ private float mMidScale; /** * 放大的最大值 */ private float mMaxScale; private Matrix mScaleMatrix; /** * 捕获用户多指触控时缩放的比例 */ private ScaleGestureDetector mScaleGestureDetector; // **********自由移动的变量*********** /** * 记录上一次多点触控的数量 */ private int mLastPointerCount; private float mLastX; private float mLastY; private int mTouchSlop; private boolean isCanDrag; private boolean isCheckLeftAndRight; private boolean isCheckTopAndBottom; // *********双击放大与缩小********* private GestureDetector mGestureDetector; private boolean isAutoScale; public ZoomImageView(Context context) this(context, null); public ZoomImageView(Context context, AttributeSet attrs) this(context, attrs, 0); public ZoomImageView(Context context, AttributeSet attrs, int defStyle) super(context, attrs, defStyle); // init mScaleMatrix = new Matrix(); setScaleType(ScaleType.MATRIX); setOnTouchListener(this); mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); mScaleGestureDetector = new ScaleGestureDetector(context, this); mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() @Override public boolean onDoubleTap(MotionEvent e) if (isAutoScale) return true; float x = e.getX(); float y = e.getY(); if (getScale() < mMidScale) postDelayed(new AutoScaleRunnable(mMidScale, x, y), 16); isAutoScale = true; else postDelayed(new AutoScaleRunnable(mInitScale, x, y), 16); isAutoScale = true; return true; ); /** * 自动放大与缩小 * * @author zhangyan@lzt.com.cn * */ 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; @Override public void run() //进行缩放 mScaleMatrix.postScale(tmpScale, tmpScale, x, y); checkBorderAndCenterWhenScale(); setImageMatrix(mScaleMatrix); float currentScale = getScale(); if ((tmpScale >1.0f && currentScale <mTargetScale) ||(tmpScale<1.0f &¤tScale>mTargetScale)) //这个方法是重新调用run()方法 postDelayed(this, 16); else //设置为我们的目标值 float scale = mTargetScale/currentScale; mScaleMatrix.postScale(scale, scale, x, y); checkBorderAndCenterWhenScale(); setImageMatrix(mScaleMatrix); isAutoScale = false; /** * 获取ImageView加载完成的图片 */ @Override public void onGlobalLayout() if (!mOnce) // 得到控件的宽和高 int width = getWidth(); int height = getHeight(); // 得到我们的图片,以及宽和高 Drawable drawable = getDrawable(); if (drawable == null) return; int dh = drawable.getIntrinsicHeight(); int dw = drawable.getIntrinsicWidth(); float scale = 1.0f; // 图片的宽度大于控件的宽度,图片的高度小于空间的高度,我们将其缩小 if (dw > width && dh < height) scale = width * 1.0f / dw; // 图片的宽度小于控件的宽度,图片的高度大于空间的高度,我们将其缩小 if (dh > height && dw < width) scale = height * 1.0f / dh; // 缩小值 if (dw > width && dh > height) scale = Math.min(width * 1.0f / dw, height * 1.0f / dh); // 放大值 if (dw < width && dh < height) scale = Math.min(width * 1.0f / dw, height * 1.0f / dh); /** * 得到了初始化时缩放的比例 */ mInitScale = scale; mMaxScale = mInitScale * 4; mMidScale = mInitScale * 2; // 将图片移动至控件的中间 int dx = getWidth() / 2 - dw / 2; int dy = getHeight() / 2 - dh / 2; mScaleMatrix.postTranslate(dx, dy); mScaleMatrix.postScale(mInitScale, mInitScale, width / 2, height / 2); setImageMatrix(mScaleMatrix); mOnce = true; /** * 注册OnGlobalLayoutListener这个接口 */ @Override protected void onAttachedToWindow() super.onAttachedToWindow(); getViewTreeObserver().addOnGlobalLayoutListener(this); /** * 取消OnGlobalLayoutListener这个接口 */ @SuppressWarnings("deprecation") @Override protected void onDetachedFromWindow() super.onDetachedFromWindow(); getViewTreeObserver().removeGlobalOnLayoutListener(this); /** * 获取当前图片的缩放值 * * @return */ public float getScale() float[] values = new float[9]; mScaleMatrix.getValues(values); return values[Matrix.MSCALE_X]; // 缩放区间时initScale maxScale @Override public boolean onScale(ScaleGestureDetector detector) float scale = getScale(); float scaleFactor = detector.getScaleFactor(); if (getDrawable() == null) return true; // 缩放范围的控制 if ((scale < mMaxScale && scaleFactor > 1.0f) || (scale > mInitScale && scaleFactor < 1.0f)) if (scale * scaleFactor < mInitScale) scaleFactor = mInitScale / scale; if (scale * scaleFactor > mMaxScale) scale = mMaxScale / scale; // 缩放 mScaleMatrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY()); checkBorderAndCenterWhenScale(); setImageMatrix(mScaleMatrix); return true; /** * 获得图片放大缩小以后的宽和高,以及left,right,top,bottom * * @return */ private RectF getMatrixRectF() Matrix matrix = mScaleMatrix; RectF rectF = new RectF(); Drawable d = getDrawable(); if (d != null) rectF.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight()); matrix.mapRect(rectF); return rectF; /** * 在缩放的时候进行边界以及我们的位置的控制 */ private void checkBorderAndCenterWhenScale() RectF rectF = getMatrixRectF(); float deltaX = 0; float deltaY = 0; int width = getWidth(); int height = getHeight(); // 缩放时进行边界检测,防止出现白边 if (rectF.width() >= width) if (rectF.left > 0) deltaX = -rectF.left; if (rectF.right < width) deltaX = width - rectF.right; if (rectF.height() >= height) if (rectF.top > 0) deltaY = -rectF.top; if (rectF.bottom < height) deltaY = height - rectF.bottom; /** * 如果宽度或高度小于空间的宽或者高,则让其居中 */ if (rectF.width() < width) deltaX = width / 2f - rectF.right + rectF.width() / 2f; if (rectF.height() < height) deltaY = height / 2f - rectF.bottom + rectF.height() / 2f; mScaleMatrix.postTranslate(deltaX, deltaY); @Override public boolean onScaleBegin(ScaleGestureDetector detector) return true; @Override public void onScaleEnd(ScaleGestureDetector detector) @Override public boolean onTouch(View v, MotionEvent event) if (mGestureDetector.onTouchEvent(event)) return true; mScaleGestureDetector.onTouchEvent(event); 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 (mLastPointerCount != pointerCount) isCanDrag = false; mLastX = x; mLastY = y; mLastPointerCount = pointerCount; RectF rectF = getMatrixRectF(); switch (event.getAction()) case MotionEvent.ACTION_DOWN: if (rectF.width()>getWidth() +0.01|| rectF.height()>getHeight()+0.01) if(getParent() instanceof ViewPager) getParent().requestDisallowInterceptTouchEvent(true); break; case MotionEvent.ACTION_MOVE: if (rectF.width()>getWidth()+0.01 || rectF.height()>getHeight()+0.01) if(getParent() instanceof ViewPager) getParent().requestDisallowInterceptTouchEvent(true); float dx = x - mLastX; float dy = y - mLastY; if (!isCanDrag) isCanDrag = isMoveAction(dx, dy); if (isCanDrag) if (getDrawable() != null) isCheckLeftAndRight = isCheckTopAndBottom = true; // 如果宽度小于控件宽度,不允许横向移动 if (rectF.width() < getWidth()) isCheckLeftAndRight = false; dx = 0; // 如果高度小于控件高度,不允许纵向移动 if (rectF.height() < getHeight()) isCheckTopAndBottom = false; dy = 0; mScaleMatrix.postTranslate(dx, dy); checkBorderWhenTranslate(); setImageMatrix(mScaleMatrix); mLastX = x; mLastY = y; break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: mLastPointerCount = 0; break; default: break; return true; /** * 当移动时进行边界检查 */ private void checkBorderWhenTranslate() RectF rectF = getMatrixRectF(); float deltaX = 0; float deltaY = 0; int width = getWidth(); int heigth = getHeight(); if (rectF.top > 0 && isCheckTopAndBottom) deltaY = -rectF.top; if (rectF.bottom < heigth && isCheckTopAndBottom) deltaY = heigth - rectF.bottom; if (rectF.left > 0 && isCheckLeftAndRight) deltaX = -rectF.left; if (rectF.right < width && isCheckLeftAndRight) deltaX = width - rectF.right; mScaleMatrix.postTranslate(deltaX, deltaY); /** * 判断是否是move * * @param dx * @param dy * @return */ private boolean isMoveAction(float dx, float dy) return Math.sqrt(dx * dx + dy * dy) > mTouchSlop;
(3)下面我们将ZoomImageView应用到ViewPager上,所以现在新建一个布局文件vp.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:background="#000000" android:layout_height="match_parent" > <android.support.v4.view.ViewPager android:id="@+id/id_viewpager" android:layout_width="match_parent" android:layout_height="match_parent" /> </RelativeLayout>
(4)最后,我们在MainActivity上写一些测试用的数据,实现最终的一个效果:
package com.example.zoomimageview; import com.example.zoomimageview.view.ZoomImageView; import android.app.Activity; import android.os.Bundle; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; public class MainActivity extends Activity private ViewPager mViewPager; private int[] mImgs = new int[]R.drawable.baby1,R.drawable.baby2,R.drawable.baby3,R.drawable.baby4; private ImageView[] mImageViews = new ImageView[mImgs.length]; @Override protected void onCreate(Bundle savedInstanceState) super.onCreate(savedInstanceState); setContentView(R.layout.vp); mViewPager = (ViewPager) findViewById(R.id.id_viewpager); mViewPager.setAdapter(new PagerAdapter() @Override public Object instantiateItem(ViewGroup container, int position) ZoomImageView imageView = new ZoomImageView(getApplicationContext()); imageView.setImageResource(mImgs[position]); container.addView(imageView); mImageViews[position] = imageView; return imageView; @Override public void destroyItem(ViewGroup container, int position, Object object) container.removeView(mImageViews[position]); @Override public boolean isViewFromObject(View arg0, Object arg1) return arg0 == arg1; @Override public int getCount() return mImageViews.length; );
到此,我们的代码已经全部写完了,供大家学习,希望对大家的进步有所帮助,谢谢!
- 更多资讯可以关注我的博客或者我的新浪微博:我是紫色紫色,不定期分享更多精彩内容,谢谢!
你的支持就是我的动力,欢迎大家热烈交流~欢迎大家订阅公众号,我会不定期更新资源,供大家一起学习。
以上是关于Android自定义ImageView实现图片缩放滑动,双击放大缩小,多点触控缩放的主要内容,如果未能解决你的问题,请参考以下文章
Android Matrix手势缩放自定义view 不止于Imageview
Android开发之自定义圆角矩形图片ImageView的实现