直接上代码

/**
* 自定义可伸缩的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. pycharm常用快捷键和自定义快捷键

     默认快捷键 编辑类: Ctrl + Space 基本的代码完成(类.方法.属性)Ctrl + Alt + Space 类名完成Ctrl + Shift + Enter 语句完成Ctrl + P 参数 ...

  2. python下载各大主流视频网站电影

    You-Get 是一个命令行工具, 用来下载各大视频网站的视频, 是我目前知道的命令行下载工具中最好的一个, 之前使用过 youtube-dl, 但是 youtube-dl 吧, 下载好的视频是分段的 ...

  3. ubuntu12.04下安装搜狗拼音

    Ubuntu 12.04 LTS 版本   由于 Ubuntu 12.04 LTS 自带的 Fcitx 版本较旧,需要先通过 PPA 升级,才能安装下载的 deb 软件包.   1. 点击左上角的图标 ...

  4. HTML学习笔记(五)框架

    框架 通过使用框架,可以在同一个浏览器窗口中显示多个页面. eg: <frameset rows="50%,50%"> <frame src="/exa ...

  5. SpringCloud之旅第一篇-微服务概念

    一.单体架构的问题 微服务为什么会出现?在学习Springboot的时候知道Springboot极大的简化了我们的开发,我们可以快速的进行业务开发,Springboot单体应用在项目的开发初期能够满足 ...

  6. E20181030-hm

    conquer  vt. 征服; 克服; 攻克; 打败(敌人);  vi. 得胜,胜利; recur vi. 复发; 重现; 再发生; 回想; recurrence n. 复回,重现; 反复,隐现; ...

  7. 201621123016 《Java程序设计》第6周学习总结

    1. 本周学习总结 1.1 面向对象学习暂告一段落,请使用思维导图,以封装.继承.多态为核心概念画一张思维导图或相关笔记,对面向对象思想进行一个总结. 注1:关键词与内容不求多,但概念之间的联系要清晰 ...

  8. zookeeper 搭建

    zookeeper 版本为zookeeper 3.4.8 操作系统为ubuntu 12.04 64位 zookeeper 单机搭建 解压zookeeper 包 .tar.gz -C /root/sof ...

  9. 异常定义-Mybatis中的源码参考

    public class IbatisException extends RuntimeException { private static final long serialVersionUID = ...

  10. CF1076D Edge Deletion

    洛谷传送门 cf传送门 这道题作为div.2的D题,被我一眼秒了我觉得十分荣幸,然后就开始写,然后就写了好久. AC之后看网上的题解,发现好多最短路树的,猛然发现我写的好复杂啊,结果还看到了直接一遍d ...