直接上代码

/**
* 自定义可伸缩的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. TX2上yolov3精度和速度优化方向

    速度优化的方向: 1.减少输入图片的尺寸, 但是相应的准确率可能会有所下降2.优化darknet工程源代码(去掉一些不必要的运算量或者优化运算过程)3.剪枝和量化yolov3网络(压缩模型---> ...

  2. bzoj 3809 Gty的二逼妹子序列——莫队+分块

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3809 容易想到树状数组维护值域.但修改和查询都是 log 太慢. 考虑有 nsqrt(n) ...

  3. HDU2586(LCA应用:在带权树中求任意两点之间的距离)

    How far away ? Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)To ...

  4. hibernate Criteria中or和and的用法

    /s筛选去除无效数据 /*      detachedCriteria.add( Restrictions.or( Restrictions.like("chanpin", &qu ...

  5. 3.1-3.3 HBase Shell创建表

    一.HBase Shell创建表 1.HBASE shell命令 ## hbase(main):001:0> create_namespace 'ns1' //创建命名空间:ns1 hbase( ...

  6. Flutter实战视频-移动电商-55.购物车_底部结算栏UI制作

    55.购物车_底部结算栏UI制作 主要做下面结算这一栏目 cart_bottom.dart页面 先设置下内边距 拆分成三个子元素 全选 因为有一个文本框和一个全选的text文本,所以这里也用了Row布 ...

  7. 使用Spring Security控制会话

    1.概述 在本文中,我们将说明Spring Security如何允许我们控制HTTP会话.此控件的范围从会话超时到启用并发会话和其他高级安全配置. 2.会话何时创建? 我们可以准确控制会话何时创建以及 ...

  8. PhpStrom之添加文件夹至左侧目录树

    1.打开编辑器,点击工具栏 File,并选择Open (File -> Open) 2.选择需要添加的文件夹路径,点击 OK 3.点击OK后弹出下图窗口(第一个选项:Open in new wi ...

  9. JQuery onload、ready 加载顺序

    // ready 这个方法只是在页面所有的DOM加载完毕后就会触发 // 方式1 $(function(){ // do something }); // 方式2 $(document).ready( ...

  10. WindowsService服务程序开发 安装和卸载

    安装服务:installutil.exe E:\XTestDemo\X_15_WindowsService\bin\Debug\X_15_WindowsService.exe 卸载服务:install ...