之前有有开发过图片放大缩小功能,是用改写OnTouch()方法来实现的,周末在家学习了下相关视屏,
发现通过ScaleGestureDetector来实现更加的方便,写篇学习日志!
/**
* 自定义缩放ImageView
* @description:
* @author ldm
* @date 2016-4-13 上午11:08:53
*/
blic class ScaleImageView extends ImageView implements OnGlobalLayoutListener,
OnTouchListener, OnScaleGestureListener {
// 是否是初次加载
private boolean mIsFirst = false;
// 初始化的图片缩放值
private float mBaseScale;
// 图片放大的最大值
private float mMaxScale;
// 图片缩放工具操作类Matrix
private Matrix mImageMatrix;
// 检测两个手指在屏幕上做缩放的手势工具类
private ScaleGestureDetector mScaleGestureDetector;
/**
* 以下三个方法为自定义View要实现的构造方法
*
* @param context
*/
public ScaleImageView(Context context) {
this(context, null);
}
public ScaleImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ScaleImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
/**
* 初始化数据
*
* @description:
* @author ldm
* @date 2016-4-12 下午4:57:04
*/
private void init(Context context) {
mImageMatrix = new Matrix();
super.setScaleType(ScaleType.MATRIX);
mScaleGestureDetector = new ScaleGestureDetector(context, this);
setOnTouchListener(this);
}
// 当View加载完成时可能通过OnGlobalLayoutListener监听,在布局加载完成后获得一个view的宽高。
@Override
public void onGlobalLayout() {
if (!mIsFirst) {
mIsFirst = true;
// 获取控件的宽度和高度
int width = getWidth();
int height = getHeight();
// 获取到ImageView对应图片的宽度和高度
Drawable d = getDrawable();
if (null == d) {
return;
}
int dw = d.getIntrinsicWidth();// 图片固有宽度
int dh = d.getIntrinsicHeight();// 图片固有高度
float scale = 1.0f;
if (dw > width && dh < height) {// 图片宽度大于控件宽度但高度小于控件高度
scale = width * 1.0f / dw;// 缩小一定值
}
// 图片宽度大于控件宽度但高度小于控件高度& 图片的宽高都小于控件的宽高
if ((dw < width && dh < height) || (dw > width && dh < height)) {
scale = Math.min(width * 1.0f / dw, height * 1.0f / dh);// 按照宽度对应缩放最小值进行缩放
}
if (dw < width && dh > height) {// 图片宽度小于控件宽度,但图片高度大于控件高度
scale = height * 1.0f / dh;// 缩小一定的比例
}
mBaseScale = scale;
mMaxScale = mBaseScale * 4;
// 将图片移动到手机屏幕的中间位置
float dx = width / 2 - dw / 2;
float dy = height / 2 - dh / 2;
mImageMatrix.postTranslate(dx, dy);
mImageMatrix.postScale(mBaseScale, mBaseScale, width / 2,
height / 2);
setImageMatrix(mImageMatrix);
}
}
@Override
public boolean onTouch(View v, MotionEvent event) {
mScaleGestureDetector.onTouchEvent(event);
return true;//这里要return true才行
}
// 当view被附着到一个窗口时触发
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
//注册OnGlobalLayoutListener
getViewTreeObserver().addOnGlobalLayoutListener(this);
}
// 当view离开附着的窗口时触发
@SuppressWarnings("deprecation")
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
//移除OnGlobalLayoutListener
getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
// 手势发生时监听器
@Override
public boolean onScale(ScaleGestureDetector detector) {
// 前一个伸缩事件至当前伸缩事件的伸缩比率
float scaleFactor = detector.getScaleFactor();
float scale = getScale();
if (null == getDrawable()) {
return true;
}
// 控件图片的缩放范围
if ((scale < mMaxScale && scaleFactor > 1.0f)
|| (scale > mBaseScale && scaleFactor < 1.0f)) {
if (scale * scaleFactor < mBaseScale) {
scaleFactor = mBaseScale / scale;
}
if (scale * scaleFactor > mMaxScale) {
scaleFactor = mMaxScale / scale;
}
// 以屏幕中央位置进行缩放
// mScaleMatrix.postScale(scaleFactor, scaleFactor, getWidth() / 2,
// getHeight() / 2);
// 以手指所在地方进行缩放
mImageMatrix.postScale(scaleFactor, scaleFactor,
detector.getFocusX(), detector.getFocusY());
borderAndCenterCheck();
setImageMatrix(mImageMatrix);
}
return false;
}
// 手势发生开始监听
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
// 一定要返回true才会进入onScale()这个函数
return true;
}
// 手势结束监听
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
// TODO Auto-generated method stub
}
private float getScale() {
float[] values = new float[9];
mImageMatrix.getValues(values);
return values[Matrix.MSCALE_X];
}
/**
* 图片在缩放时进行边界控制
*/
private void borderAndCenterCheck() {
RectF rect = getMatrixRectF();
float deltaX = 0;
float deltaY = 0;
int width = getWidth();
int height = getHeight();
// 缩放时进行边界检测,防止出现白边
if (rect.width() >= width) {
if (rect.left > 0) {
deltaX = -rect.left;
}
if (rect.right < width) {
deltaX = width - rect.right;
}
}
if (rect.height() >= height) {
if (rect.top > 0) {
deltaY = -rect.top;
}
if (rect.bottom < height) {
deltaY = height - rect.bottom;
}
}
// 如果宽度或者高度小于控件的宽或者高;则让其居中
if (rect.width() < width) {
deltaX = width / 2f - rect.right + rect.width() / 2f;
}
if (rect.height() < height) {
deltaY = height / 2f - rect.bottom + rect.height() / 2f;
}
mImageMatrix.postTranslate(deltaX, deltaY);
}
/**
* 获得图片放大缩小以后的宽和高
*
* @return
*/
private RectF getMatrixRectF() {
Matrix matrix = mImageMatrix;
RectF rectF = new RectF();
Drawable d = getDrawable();
if (d != null) {
rectF.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
matrix.mapRect(rectF);
}
return rectF;
}
}
在布局xml中直接使用
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<com.ldm.view.ScaleImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="matrix"
android:src="@drawable/zoom_test" />
LinearLayout>