自定义可伸缩的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 ...
随机推荐
- Linux终端程序用c语言实现改变输出的字的颜色
颜色代码: 格式: echo "\033[字背景颜色;字体颜色m字符串\033[0m" 例如: echo "\033[41;36m something here \033 ...
- Java并发之synchronized
Java多线程同步关键词是常用的多线程同步手段.它可以修饰静态类方法,实例方法,或代码块.修饰static静态方法时是对整个类加锁. 一.实现原理 在JVM中对象内存分三块区域,对象头.实例数据.对齐 ...
- 洛谷 1541 乌龟棋——dp
题目:https://www.luogu.org/problemnew/show/P1541 以用了几张牌为阶段.注意知道了用了4种牌各几张后,当前位置就是确定的,所以不用记录什么的. #includ ...
- Azure Key Vault (1) 入门
<Windows Azure Platform 系列文章目录> 为什么要使用Azure Key Vault? 我们假设在微软云Azure上有1个场景,在Windows VM里面有1个.NE ...
- AndroidStudio自动弹出Documentation
AndroidStudio自动弹出Documentation窗口 例如,在布局文件中添加 Button 标签 敲完 <Butotn 回车后就自动出现 Documentation窗口 那如何关闭自 ...
- docker 学习(四) springboot + docker
下面演示: 在Windows上新建一个简单的Springboot工程,生成docker iamge,然后在本地的docker上运行: (1):登录到 https://start.spring.io/, ...
- 运用Eclipse的Working Set,界面清爽多了
使用Eclipse的Working Set,界面清爽多了 想必大家的Eclipse里也会有这么多得工程...... 每次工作使用到的项目肯定不会太多...... 每次从这么大数量的工程当中找到自己要使 ...
- $.ajax数据传输成功却执行失败的回调函数
这个问题迷惑了我好几天,都快要放弃了,功夫不负有心人,最终成功解决,下面写一下我的解决方法. 我传的数据是json类型的,执行失败的回调函数是因为从后台传过来的数据不是严格的json类型,所以才会不执 ...
- 《剑指offer》面试题16—反转链表
Node* p1 p2 p3 思路:开始时,p1为NULL,p2=phead,p3=p2—>next.使p2—>next = p1,然后使p1=p2,p2=p3.如果只有1个结点则此时 ...
- 自定义Mybatis返回类型及注意事项
一.自定义返回拦截器package com.yaoex.crm.service.util; import org.apache.ibatis.session.ResultContext;import ...