自定义可伸缩的imageView
直接上代码
/**
* 自定义可伸缩的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的更多相关文章
- Glide加载图片到自定义的圆形ImageView中不显示
当使用自定义的圆形ImageView时,发现使用Glide加载并设置默认初始图片时,自定义的ImageView一直显示默认图片,无法更新到加载的图片. 使用下面代码可以解决这个问题 Glide.wit ...
- Android开发之自定义圆形的ImageView的实现
android中的ImageView只能显示矩形的图片,这样一来不能满足我们其他的需求,比如要显示圆形的图片,这个时候,我们就需要自定义ImageView了,其原理就是首先获取到图片的Bitmap,然 ...
- Android项目实战(九):CustomShapeImageView 自定义形状的ImageView
一个两年前出来的第三方类库,具有不限于圆形ImageView的多种形状ImageView,项目开发必备 github下载地址:https://github.com/MostafaGazar/Custo ...
- Android布局自定义Shap圆形ImageView,可以单独设置背景与图片
一.图片预览: 一.实现功能: 需求要实现布局中为圆形图片,图片背景与图标分开且合并到一个ImageView. 二.具体实现: XML中布局中定义ImageView, ...
- Android 自定义的圆角矩形ImageView 工具类
上图看效果 自定义圆角矩形ImageView工具类 package com.wechaotou.utils; import android.content.Context; import androi ...
- 自己动手,丰衣足食!一大波各式各样的ImageView来袭!
工作略忙,一直想自己打造一个开源控件却苦于没有时间,可是这种事情如果不动手就会一直拖下去,于是最近抽时间做了个简单的自定义形状的ImageView控件. 时间紧迫,目前仅支持正六边形.圆形.菱形.椭圆 ...
- 安卓自定义控件(三)实现自定义View
前面两篇博客,把View绘制的方法说了一下,但是,我们只在onDraw里面做文章,控件都是直接传入一个Context,还不能在布局文件里使用自定义View.这一篇博客,就不再讲绘制,在我们原先的基础上 ...
- Android特效专辑(五)——自定义圆形头像和仿MIUI卸载动画—粒子爆炸
Android特效专辑(五)--自定义圆形头像和仿MIUI卸载动画-粒子爆炸 好的,各位亲爱的朋友,今天讲的特效还是比较炫的,首先,我们会讲一个自定义圆形的imageView,接着,我们会来实现粒子爆 ...
- 自定义View之一圆形图片
自定义View的方法 对现有控件进行扩展 通过组合来实现新的控件 重写View来实现全新的控件 本篇文章主要讲对现有控件的扩展 1.圆形图片控件 自定义View,对ImageView的扩展 重写onD ...
随机推荐
- bzoj 2836 魔法树——树链剖分
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2836 树剖裸题.然而WA.RE了好久…… 原来是跳 top 的那个地方! top 不相等的时 ...
- eclipse的工程里的*.properties文件默认以unicode的编码形式显示
今天发现导入eclipse的工程里的*.properties文件无法显示中文,是unicode的编码形式显示的. 原因是Eclipse的.properties文件的默认编码为iso-8859-1. 选 ...
- C - Bear and Five Cards
Description A little bear Limak plays a game. He has five cards. There is one number written on each ...
- 我所理解的Restful API最佳实践
一直在公司负责API数据接口的开发,期间也遇到了不小的坑,本篇博客算是做一个小小的记录. 1. 不要纠结于无意义的规范 在开始本文之前,我想先说这么一句:RESTful 真的很好,但它只是一种软 ...
- 把myeclipse中html/jsp文件的视图调到只看代码
烦恼———————————————————— 解决方法: ok---------------------------- *.jsp 同理
- Win7 server2008 共享文件夹 不输入网络密码
如何设置共享文件夹访问不需要输入用户名和密码: 1.“开始”——“运行”——secpol.msc (1)使用空白密码的本地帐户只允许进行控制台登录 禁用它 (2)“本地策略”——“安全选项”——“网络 ...
- visualstudio Team Foundation Server 使用教程
一.前沿 Team Foundation Server 是我们开发者使用最多的源代码管理工具.由于自己服务器搭建拉取工作慢的缘故,我使用了微软的 TFS.使用非常方便.快捷.免费.且不公开私有的项目. ...
- 洛谷 - P1663 - 山 - 半平面交
https://www.luogu.org/problemnew/show/P1663 给定山的性状,求一个最低点可以看见所有的地方. 就是半平面交. 粘贴全家福: #include<bits/ ...
- Memcached 查看帮助
进入到memcached目录, 输入命令: memcached -h 即可查看帮助 -p<num>要侦听的TCP端口号(默认值:11211) -u<num>udp监听端口号(默 ...
- studio 配置 opencv3.1
环境 win10 android studio2.0 OpenCV-3.1.0-android-sdk android-ndk-r10e-windows-x86_64 jdk-8u102-window ...