直接上代码

/**
* 自定义可伸缩的ImageView
*/
public class ZoomImageView extends ImageView {
/** 画笔类 **/
private Paint mPaint; private Runnable mRefresh = null;
/** 缩放手势监听类 **/
private ScaleGestureDetector mScaleDetector;
/** 手势识别类 **/
private GestureDetector mGestureDetector;
/** 当前被渲染的Bitmap **/
private Bitmap mBitmap; private int mThisWidth = -1, mThisHeight = -1; private Runnable mOnLayoutRunnable = null; private Matrix mBaseMatrix = new Matrix();
private Matrix mDisplayMatrix = new Matrix();
private Matrix mSuppMatrix = new Matrix();
private Matrix mMatrix = new Matrix(); /** 最大的拉伸比例 **/
private float mMaxZoom; private float[] mMatrixValues = new float[9];
private Runnable mFling = null; private double mLastDraw = 0;
static final int sPaintDelay = 250; public ZoomImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
} public ZoomImageView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
} public ZoomImageView(Context context) {
super(context);
init();
} @SuppressLint("NewApi")
private void init() {
mPaint = new Paint();
// 图像抖动处理
mPaint.setDither(true);
// 过滤优化操作 加快显示
mPaint.setFilterBitmap(true);
// 去掉锯齿
mPaint.setAntiAlias(true); /** 刷新线程 **/
mRefresh = new Runnable() {
@Override
public void run() {
postInvalidate();
}
}; mScaleDetector = new ScaleGestureDetector(getContext(),
new ScaleListener());
mGestureDetector = new GestureDetector(getContext(),
new MyGestureListener()); // 判断是否是新的API 开启硬件加速
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
setLayerType(View.LAYER_TYPE_HARDWARE, null);
}
} public Bitmap getImageBitmap() {
return mBitmap;
} /** 回收Bitmap **/
public void clear() {
if (mBitmap != null && !mBitmap.isRecycled()) {
mBitmap.recycle();
mBitmap = null;
}
} @Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom); mThisWidth = right - left;
mThisHeight = bottom - top; Runnable r = mOnLayoutRunnable;
if (r != null) {
mOnLayoutRunnable = null;
r.run();
} if (mBitmap != null) {
setBaseMatrix(mBitmap, mBaseMatrix);
setImageMatrix(getImageViewMatrix());
}
} private void setBaseMatrix(Bitmap bitmap, Matrix matrix) {
float viewWidth = getWidth();
float viewHeight = getHeight(); matrix.reset();
float widthScale = Math
.min(viewWidth / (float) bitmap.getWidth(), 1.0f);
float heightScale = Math.min(viewHeight / (float) bitmap.getHeight(),
1.0f);
float scale;
if (widthScale > heightScale) {
scale = heightScale;
} else {
scale = widthScale;
} /** 算取比例 进行平移 **/
matrix.setScale(scale, scale);
matrix.postTranslate(
(viewWidth - ((float) bitmap.getWidth() * scale)) / 2F,
(viewHeight - ((float) bitmap.getHeight() * scale)) / 2F);
} public Matrix getImageViewMatrix() {
mDisplayMatrix.set(mBaseMatrix);
mDisplayMatrix.postConcat(mSuppMatrix);
return mDisplayMatrix;
} public void setImageMatrix(Matrix m) {
/** Matrix是否为空并是否定义 **/
if (m != null && m.isIdentity()) {
m = null;
} if (m == null && !this.mMatrix.isIdentity() || m != null
&& !this.mMatrix.equals(m)) {
this.mMatrix.set(m);
invalidate();
}
} static private void translatePoint(Matrix matrix, float[] xy) {
matrix.mapPoints(xy);
} /**
* 设置Bitmap
*
* @param bitmap
*/
@SuppressLint("NewApi")
public void setImageBitmap(final Bitmap bitmap) {
final int viewWidth = getWidth(); // 开启硬件加速
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB && bitmap != null && bitmap.getHeight() > 1800) {
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
} if (viewWidth <= 0) {
mOnLayoutRunnable = new Runnable() {
public void run() {
setImageBitmap(bitmap);
}
};
return;
} if (bitmap != null) {
setBaseMatrix(bitmap, mBaseMatrix);
this.mBitmap = bitmap;
} else {
mBaseMatrix.reset();
this.mBitmap = bitmap;
} mSuppMatrix.reset();
setImageMatrix(getImageViewMatrix());
mMaxZoom = maxZoom();
zoomTo(zoomDefault());
} public void zoomTo(float scale) {
float width = getWidth();
float height = getHeight(); zoomTo(scale, width / 2F, height / 2F);
} protected void zoomTo(float scale, float centerX, float centerY) {
if (scale > mMaxZoom) {
scale = mMaxZoom;
} float oldScale = getScale();
float deltaScale = scale / oldScale; /** 根据某个中心点按照比例缩放 **/
mSuppMatrix.postScale(deltaScale, deltaScale, centerX, centerY);
setImageMatrix(getImageViewMatrix());
center(true, true, false);
} /**
* 计算中心位置
*
* @param vertical
* @param horizontal
* @param animate
*/
protected void center(boolean vertical, boolean horizontal, boolean animate) {
if (mBitmap == null)
return; Matrix m = getImageViewMatrix(); float[] topLeft = new float[] { 0, 0 };
float[] botRight = new float[] { mBitmap.getWidth(),
mBitmap.getHeight() }; translatePoint(m, topLeft);
translatePoint(m, botRight); float height = botRight[1] - topLeft[1];
float width = botRight[0] - topLeft[0]; float deltaX = 0, deltaY = 0; if (vertical) {
int viewHeight = getHeight();
if (height < viewHeight) {
deltaY = (viewHeight - height) / 2 - topLeft[1];
} else if (topLeft[1] > 0) {
deltaY = -topLeft[1];
} else if (botRight[1] < viewHeight) {
deltaY = getHeight() - botRight[1];
}
} if (horizontal) {
int viewWidth = getWidth();
if (width < viewWidth) {
deltaX = (viewWidth - width) / 2 - topLeft[0];
} else if (topLeft[0] > 0) {
deltaX = -topLeft[0];
} else if (botRight[0] < viewWidth) {
deltaX = viewWidth - botRight[0];
}
} postTranslate(deltaX, deltaY);
if (animate) {
Animation a = new TranslateAnimation(-deltaX, 0, -deltaY, 0);
a.setStartTime(SystemClock.elapsedRealtime());
a.setDuration(250);
setAnimation(a);
}
setImageMatrix(getImageViewMatrix());
} protected void postTranslate(float dx, float dy) {
mSuppMatrix.postTranslate(dx, dy);
} public float getScale() {
return getScale(mSuppMatrix);
} protected float getScale(Matrix matrix) {
if (mBitmap != null)
return getValue(matrix, Matrix.MSCALE_X);
else
return 1f;
} protected float getValue(Matrix matrix, int whichValue) {
matrix.getValues(mMatrixValues);
return mMatrixValues[whichValue];
} /**
* 计算最大的拉伸比例
*
* @return
*/
protected float maxZoom() {
if (mBitmap == null)
return 1F; float fw = (float) mBitmap.getWidth() / (float) mThisWidth;
float fh = (float) mBitmap.getHeight() / (float) mThisHeight;
float max = Math.max(fw, fh) * 16;
return max;
} /**
* 原始显示比例
*
* @return
*/
public float zoomDefault() {
if (mBitmap == null)
return 1F; float fw = (float) mThisWidth / (float) mBitmap.getWidth();
float fh = (float) mThisHeight / (float) mBitmap.getHeight();
return Math.max(Math.min(fw, fh), 1);
} protected void zoomTo(final float scale, final float centerX,
final float centerY, final float durationMs) {
final float incrementPerMs = (scale - getScale()) / durationMs;
final float oldScale = getScale();
final long startTime = System.currentTimeMillis(); post(new Runnable() {
public void run() {
long now = System.currentTimeMillis();
float currentMs = Math.min(durationMs,
(float) (now - startTime));
float target = oldScale + (incrementPerMs * currentMs);
zoomTo(target, centerX, centerY); if (currentMs < durationMs) {
post(this);
}
}
});
} protected void scrollBy(float distanceX, float distanceY,
final float durationMs) {
final float dx = distanceX;
final float dy = distanceY;
final long startTime = System.currentTimeMillis(); mFling = new Runnable() {
float old_x = 0;
float old_y = 0; public void run() {
long now = System.currentTimeMillis();
float currentMs = Math.min(durationMs, now - startTime);
float x = easeOut(currentMs, 0, dx, durationMs);
float y = easeOut(currentMs, 0, dy, durationMs);
postTranslate((x - old_x), (y - old_y));
center(true, true, false); old_x = x;
old_y = y;
if (currentMs < durationMs) {
post(this);
}
}
};
post(mFling);
} private float easeOut(float time, float start, float end, float duration) {
return end * ((time = time / duration - 1) * time * time + 1) + start;
} @SuppressLint("NewApi")
@Override
protected void onDraw(Canvas canvas) {
if (mBitmap != null && !mBitmap.isRecycled()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB && getLayerType() == View.LAYER_TYPE_HARDWARE) {
canvas.drawBitmap(mBitmap, mMatrix, null);
} else {
if ((System.currentTimeMillis() - mLastDraw) > sPaintDelay) {
canvas.drawBitmap(mBitmap, mMatrix, mPaint);
mLastDraw = System.currentTimeMillis();
} else {
canvas.drawBitmap(mBitmap, mMatrix, null);
removeCallbacks(mRefresh);
postDelayed(mRefresh, sPaintDelay);
}
}
}
} /**
* @author Administrator
*
* 手势缩放监听
*/
class ScaleListener extends
ScaleGestureDetector.SimpleOnScaleGestureListener { @Override
public boolean onScale(ScaleGestureDetector detector) {
if (detector != null && detector.isInProgress()) {
try {
float targetScale = getScale() * detector.getScaleFactor();
targetScale = Math.min(maxZoom(),
Math.max(targetScale, 1.0f)); zoomTo(targetScale, detector.getFocusX(),
detector.getFocusY());
invalidate();
return true;
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
}
return false;
}
}; /**
* @author Administrator
*
* 手势识别监听
*/
class MyGestureListener extends GestureDetector.SimpleOnGestureListener { @Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
if ((e1 != null && e1.getPointerCount() > 1)
|| (e2 != null && e2.getPointerCount() > 1)
|| (mScaleDetector != null && mScaleDetector.isInProgress())) {
return false;
} if (getScale() > zoomDefault()) {
removeCallbacks(mFling);
postTranslate(-distanceX, -distanceY);
center(true, true, false);
} return true;
} @Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
if ((e1 != null && e1.getPointerCount() > 1) || (e2 != null && e2.getPointerCount() > 1)) {
return false;
} if (mScaleDetector.isInProgress()) {
return false;
} try {
float diffX = e2.getX() - e1.getX();
float diffY = e2.getY() - e1.getY(); if (Math.abs(velocityX) > 800 || Math.abs(velocityY) > 800) {
scrollBy(diffX / 2, diffY / 2, 300);
invalidate();
}
} catch (NullPointerException e) {
e.printStackTrace();
} return super.onFling(e1, e2, velocityX, velocityY);
} @Override
public boolean onDoubleTap(MotionEvent e) {
if (getScale() > zoomDefault()) {
zoomTo(zoomDefault());
} else
zoomTo(zoomDefault() * 3, e.getX(), e.getY(), 200);
return true;
} @Override
public boolean onSingleTapConfirmed(MotionEvent e) {
// 设置点击事件
if (mImageTouchedListener != null) {
mImageTouchedListener.onImageTouched();
return false;
}
return super.onSingleTapConfirmed(e);
} } @Override
public boolean onTouchEvent(MotionEvent event) {
if (mBitmap != null) {
mScaleDetector.onTouchEvent(event); if (!mScaleDetector.isInProgress()) {
mGestureDetector.onTouchEvent(event);
}
} return true;
}; /**
*
* @author Administrator
*
* 点击接口
*/
private onImageTouchedListener mImageTouchedListener; public interface onImageTouchedListener {
void onImageTouched();
} public void setOnImageTouchedListener(onImageTouchedListener listener) {
this.mImageTouchedListener = listener;
}
}

  

自定义可伸缩的imageView的更多相关文章

  1. Glide加载图片到自定义的圆形ImageView中不显示

    当使用自定义的圆形ImageView时,发现使用Glide加载并设置默认初始图片时,自定义的ImageView一直显示默认图片,无法更新到加载的图片. 使用下面代码可以解决这个问题 Glide.wit ...

  2. Android开发之自定义圆形的ImageView的实现

    android中的ImageView只能显示矩形的图片,这样一来不能满足我们其他的需求,比如要显示圆形的图片,这个时候,我们就需要自定义ImageView了,其原理就是首先获取到图片的Bitmap,然 ...

  3. Android项目实战(九):CustomShapeImageView 自定义形状的ImageView

    一个两年前出来的第三方类库,具有不限于圆形ImageView的多种形状ImageView,项目开发必备 github下载地址:https://github.com/MostafaGazar/Custo ...

  4. Android布局自定义Shap圆形ImageView,可以单独设置背景与图片

    一.图片预览:                  一.实现功能: 需求要实现布局中为圆形图片,图片背景与图标分开且合并到一个ImageView. 二.具体实现: XML中布局中定义ImageView, ...

  5. Android 自定义的圆角矩形ImageView 工具类

    上图看效果 自定义圆角矩形ImageView工具类 package com.wechaotou.utils; import android.content.Context; import androi ...

  6. 自己动手,丰衣足食!一大波各式各样的ImageView来袭!

    工作略忙,一直想自己打造一个开源控件却苦于没有时间,可是这种事情如果不动手就会一直拖下去,于是最近抽时间做了个简单的自定义形状的ImageView控件. 时间紧迫,目前仅支持正六边形.圆形.菱形.椭圆 ...

  7. 安卓自定义控件(三)实现自定义View

    前面两篇博客,把View绘制的方法说了一下,但是,我们只在onDraw里面做文章,控件都是直接传入一个Context,还不能在布局文件里使用自定义View.这一篇博客,就不再讲绘制,在我们原先的基础上 ...

  8. Android特效专辑(五)——自定义圆形头像和仿MIUI卸载动画—粒子爆炸

    Android特效专辑(五)--自定义圆形头像和仿MIUI卸载动画-粒子爆炸 好的,各位亲爱的朋友,今天讲的特效还是比较炫的,首先,我们会讲一个自定义圆形的imageView,接着,我们会来实现粒子爆 ...

  9. 自定义View之一圆形图片

    自定义View的方法 对现有控件进行扩展 通过组合来实现新的控件 重写View来实现全新的控件 本篇文章主要讲对现有控件的扩展 1.圆形图片控件 自定义View,对ImageView的扩展 重写onD ...

随机推荐

  1. 五 pyJWT使用

    PyJWT是一个Python库,用来编码/解码JWT(JSON Web Token)的. 1:安装PyJWT 2:  直接上代码了: import datetime, jwt, time from a ...

  2. docker学习 (三) Windows 10 安装Docker

    Docker CE: 社区版,免费. Docker EE: 企业版,收费. 安装:             Windows 10 Pro 64, 必须开启Hyper-v 下载:Docker for W ...

  3. 【旧文章搬运】KeUserModeCallback用法详解

    原文发表于百度空间及看雪论坛,2010-01-10 看雪论坛地址:https://bbs.pediy.com/thread-104918.htm  代码及附件可到这里下载=============== ...

  4. 2014年将会受欢迎的IT技能--你有多少哪?

    据国外媒体报道,据Global Knowledge等十几家研究机构发布的2014年IT技能和薪金调查报告显示,2014年最受欢迎的十大IT技能如下: 1.编程与应用开发 据美国劳工统计局称,开发者和程 ...

  5. sass编译命令

    sass编译一个文件的方式 sass xx.scss:xx.css 这种方式只能编译一次,要是想一直监控编译,只要有保存更改就会立即编译,那么就需要下面这条命令了 sass --watch xx.sc ...

  6. 谈谈Spring Ioc的理解

    原文:http://blog.csdn.net/qq_22654611/article/details/52606960 学了几天Ioc了,但是对它的理解还是模模糊糊,看了这篇博客感觉对Ioc有了更深 ...

  7. 2-2和2-3基本数据类型 & 2-4基本数据类型详解 & 3-1和3-2整形字面量值及变量声

    2-4基本数据类型详解 3-1和3-2整形字面量值及变量声 023是八进制的 0x1357是十六进制 0X3C也是十六进制 0x1abL:长整型 变量声明 数据类型 空格 变量名 赋值: 变量的定义:

  8. web调试利器_fiddler

    此文已由作者夏君授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 一.fiddler简介 直接引用官网介绍 The free web debugging proxy for a ...

  9. sql查询的时候,等于这两个的值得全部取出来

    sql查询的时候  用or连接 ad.jqtype='人文历史' or  ad.jqtype='名胜古迹'

  10. [Xcode 实际操作]八、网络与多线程-(17)使用网址会话对象URLSession向远程服务器上传图片

    目录:[Swift]Xcode实际操作 本文将演示如何通过网址会话对象URLSession向远程服务器上传图片. 网址会话对象URLSession具有在后台上传和下载.暂停和恢复网络操作.丰富的代理模 ...