ZoomImageView是一个支持收拾缩放,多点触控的开源控件,这篇文章学习他的图片居中和手势缩放。要缩放的是一个图片所以要用到自定义控件,我们MyZoomImageView,继承自AppCompatImageView实现他的构造方法,既然是图片的处理,我们这里用到了一个类Matrix,他是一个3×3的一个矩阵,可以对图片进行缩放、旋转、位移、倾斜等处理。因此需要在构造方法里面进行初始化,另外还需要获取到图片宽高信息,以及Drawable信息,这里实现OnGlobalLayoutListener接口,在onGlobalLayout方法中处理。如下:
private void init(Context context) {
setScaleType(ScaleType.MATRIX);
mMatrix = new Matrix();
}
在注册完OnGlobalLayoutListener接口后还需要进行注销,防止内存泄漏:
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
getViewTreeObserver().addOnGlobalLayoutListener(this);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
做好了上面的准备工作,现在我们先实现图片的居中显示,然后再实现多指触控缩放。
一、图片的居中显示
xml布局如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="header.footer.recycle.MainActivity">
<header.footer.recycle.MyZoomImageView
android:id="@+id/rl_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="matrix"
android:background="#cccccc"
android:src="@mipmap/ic_launcher"/>
</RelativeLayout>
实现思路:
- 获取图片宽高信息。
- 设置图片居中。
- 设置图片缩放。
由于获取图片信息只需要获取一次,因此需要在onGlobalLayout方法上面加个判断,防止图片缩放或者移动的时候多次调用,这里设置一个变量boolean,进行控制:
@Override
public void onGlobalLayout() {
if (!flag) {
//获取设置的宽高:一般为屏幕宽高
mWidth = getWidth();
mHeight = getHeight();
//获取设置图片的尺寸
Drawable drawable = getDrawable();
if (drawable == null) {
return;
}
mDrawableHeight = drawable.getIntrinsicHeight();
mDrawableWidth = drawable.getIntrinsicWidth();
//设置图片位置
int dx = mWidth / 2 - mDrawableWidth / 2;
int dy = mHeight / 2 - mDrawableHeight / 2;
//平移
mMatrix.postTranslate(dx, dy);
//设置图片
setImageMatrix(mMatrix);
flag = true;
}
}
运行,如下图,很清楚的看到图片居中了。
这样上面一二步,就实现了,下面看第三步,设置图片缩放。首先得获取缩放系数,分为如下四种情况进行讨论,如下备注:
private float getScaleSize() {
float scale = 1.0f;
//宽度很大,高度很小
if (mDrawableWidth > mWidth && mDrawableHeight < mHeight) {
scale = mWidth * 1.0f / mDrawableWidth;
}
//宽度很小,高度很大
if (mDrawableWidth < mWidth && mDrawableHeight > mHeight) {
scale = mHeight * 1.0f / mDrawableHeight;
}
//高度宽度都很大
if (mDrawableWidth > mWidth && mDrawableHeight > mHeight) {
float widthScale = mWidth * 1.0f / mDrawableWidth;
float heightScale = mHeight * 1.0f / mDrawableHeight;
//这里需要注意,为啥只min
scale = Math.min(widthScale, heightScale);
}
//宽度很小,高度很小
if (mDrawableWidth < mWidth && mDrawableHeight < mHeight) {
float widthScale = mWidth * 1.0f / mDrawableWidth;
float heightScale = mHeight * 1.0f / mDrawableHeight;
//这里需要注意,为啥只min
scale = Math.min(widthScale, heightScale);
}
return scale;
}
获取到缩放系数之后还需要定义三个变量,分别是缩放初始值,缩放最小值,缩放最大值,,我们定义在成员变量,供后面使用:
//设置缩放比
private float minScale = 1.0f; //最小值为设置原图大小
private float maxScale = minScale * 2.0f; //最大值为设置原图2倍大小
private float madScale = minScale * 4.0f; //最最大值为设置原图4倍大小
再在onGlobalLayout中设置:
@Override
public void onGlobalLayout() {
if (!flag) {
//获取设置的宽高:一般为屏幕宽高
mWidth = getWidth();
mHeight = getHeight();
//获取设置图片的尺寸
Drawable drawable = getDrawable();
if (drawable == null) {
return;
}
mDrawableHeight = drawable.getIntrinsicHeight();
mDrawableWidth = drawable.getIntrinsicWidth();
//设置图片位置
int dx = mWidth / 2 - mDrawableWidth / 2;
int dy = mHeight / 2 - mDrawableHeight / 2;
//缩放系数
initScale = getScaleSize();
mMatrix.postTranslate(dx, dy);
//第一个、第二个为x,y周缩放比;后面两个为缩放中心,设置为屏幕中心为缩放中心
mMatrix.postScale(initScale, initScale, mWidth / 2, mHeight / 2);
//设置图片
setImageMatrix(mMatrix);
flag = true;
}
}
再次运行效果:
二、图片的多指触控缩放
多指触控需要用到ScaleGestureDetector这个类,该类主要是用于识别一些特定的手势,然后将事件交给TouchEvent方法,这样我们就不用额外处理手势事件了,只要要实现ScaleGestureDetector.OnScaleGestureListener,在回调中进行处理,然后再实现View.OnTouchListener接口,重写onTouch方法,把MotionEvent交个mGestureDetector。
@Override
public boolean onTouch(View v, MotionEvent event) {
mGestureDetector.onTouchEvent(event);
}
OnScaleGestureListener回调方法一共有三个:
//缩放中,返回true表示处理了
@Override
public boolean onScale(ScaleGestureDetector detector) {
return false;
}
//缩放开始,一定要返回true,否则监听不到后面的回调
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
return true;
}
//缩放结束
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
}
手势的多点触控主要是在onScale方法里面进行处理:
@Override
public boolean onScale(ScaleGestureDetector detector) {
//获取当前的缩放事件
float scaleFactor = detector.getScaleFactor();\
//获取当前的缩放系数
float currentScalse = getCurrentScaleSize();
//判断是否设置图片
if (getDrawable() == null) {
return true;
}
// 用户将要放大图片或者用户将要缩小图片
if ((scaleFactor > 1.0f && currentScalse < maxScale) ||
(scaleFactor < 1.0f && currentScalse > minScale)) {
// 缩小时
if (scaleFactor * currentScalse < minScale) {
scaleFactor = minScale / currentScalse;
}
// 放大时
if (scaleFactor * currentScalse > maxScale) {
scaleFactor = maxScale / currentScalse;
}
//执行缩放动作
mMatrix.postScale(scaleFactor, scaleFactor, mWidth / 2, mHeight / 2);
//设置mMatrix
setImageMatrix(mMatrix);
}
//返回true,默认false
return true;
}
/**
* 当前缩放图片的缩放值:x,y缩放值一样,获取一个就可以
*
* @return
*/
public float getCurrentScaleSize() {
float[] values = new float[9];
mMatrix.getValues(values);
return values[Matrix.MSCALE_X];
}
就分析到这里,其他部分后面有时间了在接着分析!