自定义ImageView 实现双击放大缩小还原,无极缩小和旋转及拖动(多机型测试很稳定)
2015-11-09 17:19
441 查看
/** * 该模块主要实现了放大和原大两个级别的缩放。 功能有: 1.以触摸点为中心放大(这个是网上其他的代码没有的) 2.取消边界控制(这个是网上其他的代码没有的)也可以添加边界控制 3.双击放大或缩小(主要考虑到电阻屏) 4.多点触摸放大和缩小 这个模块已经通过了测试,并且用户也使用有一段时间了,是属于比较稳定的了。 5.新加了旋转和无极放大缩小及拖动(这是网上很难找到的) * @author qi * */ public class TouchImageView extends ImageView { float x_down = 0; float y_down = 0; PointF start = new PointF(); PointF mid = new PointF(); float oldDist = 1f; float oldRotation = 0; float nowRotation = 0; Matrix matrix; Matrix matrix1 = new Matrix(); Matrix savedMatrix = new Matrix(); private static final int NONE = 0; private static final int DRAG = 1; private static final int ZOOM = 2; int mode = NONE; boolean matrixCheck = false; float widthScreen; float heightScreen; float widthImg; float heightImg; Bitmap bitmap; public TouchImageView(Activity activity,String url) { super(activity); Drawable drawable = DrawableCache.getInstance().loadDrawable(activity, url, 400, null, new ImageCallback() { public void imageLoaded(Drawable imageDrawable, String url) { //此处不需要处理 } }); BitmapDrawable bd = (BitmapDrawable)drawable; if(bd!=null){ bitmap = bd.getBitmap(); } if (bitmap == null) { return; } DisplayMetrics dm = new DisplayMetrics(); activity.getWindowManager().getDefaultDisplay().getMetrics(dm); widthScreen = dm.widthPixels; heightScreen = dm.heightPixels; widthImg = bitmap.getWidth(); heightImg = bitmap.getHeight(); float scaleX = widthScreen/widthImg; float scaleY = heightScreen/heightImg; // float scale ; // if(scaleX>scaleY){ // scale = scaleY; // }else{ // scale = scaleX; // } scale = scaleX < scaleY ? scaleX : scaleY; if (scale < 1 && 1 / scale < bigScale) { bigScale = (float) (1 / scale + 0.5); } matrix = new Matrix(); subX=(widthScreen-widthImg*scale)/2; subY=(heightScreen-heightImg*scale)/2; matrix.postScale(scale, scale); matrix.postTranslate(subX, subY);// 平移 matrix.postRotate(360, widthScreen/2, heightScreen/2);// 旋轉 } protected void onDraw(Canvas canvas) { if (bitmap == null) { return; } // 去除锯齿毛边 canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG)); //matrix.reset(); canvas.save(); canvas.drawBitmap(bitmap, matrix, null); //Matrix{[1.0, 0.0, 0.0][0.0, 1.0, 0.0][0.0, 0.0, 1.0]} //Matrix{[1.0, 0.0, 210.33707][0.0, 1.0, 140.651][0.0, 0.0, 1.0]} //Matrix{[1.0, 0.0, 232.17975][0.0, 1.0, 135.71112][0.0, 0.0, 1.0]} //PointF(512.08984, 766.87354) canvas.restore(); } long lastClickTime = 0; // 单击时间 public boolean onTouchEvent(MotionEvent event) { switch (event.getAction() & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: mode = DRAG; x_down = event.getX(); y_down = event.getY(); if (event.getPointerCount() == 1) { // 如果两次点击时间间隔小于一定值,则默认为双击事件 if (event.getEventTime() - lastClickTime < 300) { changeSize(x_down, y_down); } else if (isBig) { mode = DRAG; } } savedMatrix.set(matrix); lastClickTime = event.getEventTime(); break; case MotionEvent.ACTION_POINTER_DOWN: mode = ZOOM; oldDist = spacing(event); oldRotation = rotation(event); savedMatrix.set(matrix); midPoint(mid, event); break; case MotionEvent.ACTION_MOVE: if (mode == ZOOM) { matrix1.set(savedMatrix); nowRotation = rotation(event); float rotation = nowRotation - oldRotation; float newDist = spacing(event); float scale = newDist / oldDist; matrix1.postScale(scale, scale, mid.x, mid.y);// 縮放 matrix1.postRotate(rotation, mid.x, mid.y);// 旋轉 matrixCheck = matrixCheck(); if (matrixCheck == false) { matrix.set(matrix1); invalidate(); } } else if ((mode == DRAG) && (isMoveX || isMoveY)) { matrix1.set(savedMatrix); matrix1.postTranslate(event.getX() - x_down, event.getY() - y_down);// 平移 matrixCheck = matrixCheck(); matrixCheck = matrixCheck(); if (matrixCheck == false) { matrix.set(matrix1); invalidate(); } } break; case MotionEvent.ACTION_UP: /*float rotation = Math.abs(nowRotation); if(rotation>=0&&rotation<=45){ //rotation =90; } matrix.postRotate(rotation, mid.x, mid.y);// 旋轉 invalidate();*/ case MotionEvent.ACTION_POINTER_UP: mode = NONE; break; } return true; } Boolean isBig = false; // 是否是放大状态 float scale; // 适合屏幕缩放倍数 float subX; float subY; float bigScale = 3f; // 默认放大倍数 float topHeight=0f; // 状态栏高度和标题栏高度 float limitX1; float limitX2; float limitY1; float limitY2; Boolean isMoveX = true; // 是否允许在X轴拖动 Boolean isMoveY = true; // 是否允许在Y轴拖动 private void changeSize(float x, float y) { if (isBig) { // 如果处于最大状态,则还原 matrix.reset(); matrix.postScale(scale, scale); matrix.postTranslate(subX, subY); isBig = false; } else { matrix.postScale(bigScale, bigScale); // 在原有矩阵后乘放大倍数 float transX = -((bigScale - 1) * x); float transY = -((bigScale - 1) * (y - topHeight)); // (bigScale-1)(y-statusBarHeight-subY)+2*subY; float currentWidth = widthImg * scale * bigScale; // 放大后图片大小 float currentHeight = heightImg * scale * bigScale; // 如果图片放大后超出屏幕范围处理 if (currentHeight > heightScreen) { limitY1 = -(currentHeight - heightScreen); // 平移限制 limitY2 = 0; isMoveY = true; // 允许在Y轴上拖动 float currentSubY = bigScale * subY; // 当前平移距离 // 平移后,内容区域上部有空白处理办法 if (-transY < currentSubY) { transY = -currentSubY; } // 平移后,内容区域下部有空白处理办法 if (currentSubY + transY < limitY1) { transY = -(currentHeight + currentSubY - heightScreen); } } else { // 如果图片放大后没有超出屏幕范围处理,则不允许拖动 isMoveY = false; } if (currentWidth > widthScreen) { limitX1 = -(currentWidth - widthScreen); limitX2 = 0; isMoveX = true; float currentSubX = bigScale * subX; if (-transX < currentSubX) { transX = -currentSubX; } if (currentSubX + transX < limitX1) { transX = -(currentWidth + currentSubX - widthScreen); } } else { isMoveX = false; } matrix.postTranslate(transX, transY); isBig = true; } this.setImageMatrix(matrix); // if (mCustomMethod != null) { // mCustomMethod.customMethod(isBig); // } } private boolean matrixCheck() { float[] f = new float[9]; matrix1.getValues(f); // 图片4个顶点的坐标 float x1 = f[0] * 0 + f[1] * 0 + f[2]; float y1 = f[3] * 0 + f[4] * 0 + f[5]; float x2 = f[0] * bitmap.getWidth() + f[1] * 0 + f[2]; float y2 = f[3] * bitmap.getWidth() + f[4] * 0 + f[5]; float x3 = f[0] * 0 + f[1] * bitmap.getHeight() + f[2]; float y3 = f[3] * 0 + f[4] * bitmap.getHeight() + f[5]; float x4 = f[0] * bitmap.getWidth() + f[1] * bitmap.getHeight() + f[2]; float y4 = f[3] * bitmap.getWidth() + f[4] * bitmap.getHeight() + f[5]; // 图片现宽度 double width = Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); // 缩放比率判断 if (width < widthScreen / 3 || width > widthScreen * 3) { return true; } // 出界判断 if ((x1 < widthScreen / 3 && x2 < widthScreen / 3 && x3 < widthScreen / 3 && x4 < widthScreen / 3) || (x1 > widthScreen * 2 / 3 && x2 > widthScreen * 2 / 3 && x3 > widthScreen * 2 / 3 && x4 > widthScreen * 2 / 3) || (y1 < heightScreen / 3 && y2 < heightScreen / 3 && y3 < heightScreen / 3 && y4 < heightScreen / 3) || (y1 > heightScreen * 2 / 3 && y2 > heightScreen * 2 / 3 && y3 > heightScreen * 2 / 3 && y4 > heightScreen * 2 / 3)) { return true; } return false; } // 触碰两点间距离 private float spacing(MotionEvent event) { float x = event.getX(0) - event.getX(1); float y = event.getY(0) - event.getY(1); return FloatMath.sqrt(x * x + y * y); } // 取手势中心点 private void midPoint(PointF point, MotionEvent event) { float x = event.getX(0) + event.getX(1); float y = event.getY(0) + event.getY(1); point.set(x / 2, y / 2); } // 取旋转角度 private float rotation(MotionEvent event) { double delta_x = (event.getX(0) - event.getX(1)); double delta_y = (event.getY(0) - event.getY(1)); double radians = Math.atan2(delta_y, delta_x); return (float) Math.toDegrees(radians); } // 将移动,缩放以及旋转后的图层保存为新图片 // 本例中沒有用到該方法,需要保存圖片的可以參考 /*public Bitmap CreatNewPhoto() { Bitmap bitmap = Bitmap.createBitmap(widthScreen, heightScreen, Config.ARGB_8888); // 背景图片 Canvas canvas = new Canvas(bitmap); // 新建画布 canvas.drawBitmap(bitmap, matrix, null); // 画图 canvas.save(Canvas.ALL_SAVE_FLAG); // 保存画布 canvas.restore(); return bitmap; }*/ }
<img alt="大笑" src="http://static.blog.csdn.net/xheditor/xheditor_emot/default/laugh.gif" />
使用方法:
可以完全当成ImageView 来使用。
一般使用时聊天内容或者图库点击查看图片时,
imageView = new TouchImageView(this,url); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); imageView.setLayoutParams(params); setContentView(imageView);大多这样用,点击全屏查看一个图片。。。
非常之简单。。。分享给大家使用。
漏掉了一个类,这里补上:
DrawableCache:
import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.lang.ref.ReferenceQueue; import java.lang.ref.SoftReference; import java.net.HttpURLConnection; import java.net.InetSocketAddress; import java.net.Proxy; import java.net.Proxy.Type; import java.net.SocketAddress; import java.net.URL; import java.net.URLEncoder; import java.util.ArrayList; import java.util.HashMap; import java.util.Hashtable; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import android.content.Context; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.Message; import com.inwhoop.xbzhjypt.main.app.MyApplication; import com.inwhoop.xbzhjypt.main.util.StringUtils; public class DrawableCache { static private DrawableCache cache; // 用于Chche内容的存储的map private Hashtable<String,MySoftRef> hashRefs; // 垃圾Reference的队列(所引用的对象已经被回收,则将该引用存入队列中) private ReferenceQueue<Drawable> q; private ExecutorService executor; private Map<String,List<Handler>> RequestList = new HashMap<String,List<Handler>>(); /** * 继承SoftReference,使得每一个实例都具有可识别的标识。 */ private class MySoftRef extends SoftReference<Drawable> { private String _key = ""; public MySoftRef(Drawable db, ReferenceQueue<Drawable> q, String key) { super(db, q); _key = key; } } /** * 初始化容器和线程池 */ private DrawableCache() { hashRefs = new Hashtable<String,MySoftRef>(); q = new ReferenceQueue<Drawable>(); executor = Executors.newFixedThreadPool(10); } /** * 取得缓存器实例 */ public static DrawableCache getInstance() { if (cache == null) { cache = new DrawableCache(); } return cache; } /** * 以软引用的方式对一个Bitmap对象的实例进行引用并保存该引用 */ private void addCacheBitmap(Drawable db, String key) { cleanCache();// 每次加入Bitmap时清除垃圾引用 MySoftRef ref = new MySoftRef(db, q, key); hashRefs.put(key, ref); } /** * 取得Bitmap,以《URL》名称为key */ private Drawable getDrawableFromCache(String key, Context context) { Drawable db = null; if (hashRefs.containsKey(key)) { MySoftRef ref = (MySoftRef) hashRefs.get(key); db = (Drawable) ref.get(); } return db; } private void cleanCache() { MySoftRef ref = null; while ((ref = (MySoftRef) q.poll()) != null) { hashRefs.remove(ref._key); } } /** * 清除Cache内的全部内容,可以随意调用 */ public void clearCache() { cleanCache(); hashRefs.clear(); System.gc(); System.runFinalization(); } /** * 请求图片的主方法!!! */ public Drawable loadDrawable(final Context context, final String imageUrl, final Integer toSize, final Integer threadPriority, final ImageCallback imageCallback) { if (imageUrl == null) return null; // 缓存中是否有该Bitmap实例的软引用,如果有,从软引用中取得 Drawable drawable = getDrawableFromCache(imageUrl, context); if (drawable != null && drawable instanceof BitmapDrawable && ((BitmapDrawable) drawable).getBitmap() != null) { return drawable; } // 从URL中截取文件名 String fileName = null; try { fileName = URLEncoder.encode(imageUrl, "UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } final File file; // 判断可用来存储的位置 if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)) { File imageDir = MyApplication.getInstance().getAppPathFile("img"); isExist(imageDir); file = new File(imageDir, fileName); } else file = new File(context.getCacheDir(), fileName); // 下载图片到手机上 if (!file.isDirectory() && file.exists()) { drawable = getDrawable(file.getAbsolutePath(), toSize); if (drawable != null && drawable instanceof BitmapDrawable && ((BitmapDrawable) drawable).getBitmap() != null) { addCacheBitmap(drawable, imageUrl); return drawable; } } final Handler handler = new Handler() { public void handleMessage(Message message) { imageCallback.imageLoaded((Drawable) message.obj, imageUrl); } }; if (RequestList.containsKey(imageUrl)) { RequestList.get(imageUrl).add(handler); } else { RequestList.put(imageUrl, new ArrayList<Handler>()); RequestList.get(imageUrl).add(handler); // 如果没有软引用,或者从软引用中得到的实例是null,则新起一个线程,并保存对这个图片的软引用 executor.execute(new Runnable() { @Override public void run() { if (threadPriority != null) { Thread.currentThread().setPriority(threadPriority.intValue()); } Drawable drawable = loadImageFromUrl(context, imageUrl, file, toSize); addCacheBitmap(drawable, imageUrl); List<Handler> listHandler = RequestList.remove(imageUrl); for (Handler callbackHandler : listHandler) { Message message = callbackHandler.obtainMessage(0, drawable); callbackHandler.sendMessage(message); } listHandler.clear(); } }); } return null; } /** * 下载并保存图片 * * @param toSize */ public Drawable loadImageFromUrl(Context context, String imageUrl, File file, Integer toSize) { Drawable drawable = null; try { FileOutputStream fos = new FileOutputStream(file); HttpURLConnection conn = null; URL url = new URL(imageUrl); if (!MyApplication.getInstance().isWifi() && StringUtils.isNotEmpty(android.net.Proxy.getDefaultHost())) { Type type = Type.HTTP; SocketAddress sa = new InetSocketAddress(android.net.Proxy.getDefaultHost(), android.net.Proxy.getDefaultPort()); Proxy proxy = new Proxy(type, sa); conn = (HttpURLConnection) url.openConnection(proxy); } else { conn = (HttpURLConnection) url.openConnection(); } conn.connect(); int responseCode = conn.getResponseCode(); InputStream is = conn.getInputStream(); int data = is.read(); while (data != -1) { fos.write(data); data = is.read(); } fos.flush(); fos.close(); is.close(); drawable = getDrawable(file.toString(), toSize); } catch (Exception e) { // 出现任何异常都会删除文件 file.delete(); } return drawable; } private Drawable getDrawable(String sourceFileName, Integer toSize) { try { if (toSize == null) { return Drawable.createFromPath(sourceFileName); } else { Bitmap bitmap = BitmapConvert.resizeBitmap(sourceFileName, toSize); return new BitmapDrawable(bitmap); } } catch (FileNotFoundException e) { e.printStackTrace(); } return null; } /** * 判断文件夹是否存在,如果不存在则创建文件夹 */ public static void isExist(File imageDir) { File file = imageDir; if (!file.exists()) file.mkdirs(); } public interface ImageCallback { public void imageLoaded(Drawable imageDrawable, String imageUrl); } /** * 从网络中获取图片,以流的形式返回 * @return */ public static InputStream getImageViewInputStream(String URL_PATH) throws IOException { InputStream inputStream = null; URL url = new URL(URL_PATH); //服务器地址 if (url != null) { //打开连接 HttpURLConnection httpURLConnection = (HttpURLConnection)url.openConnection(); httpURLConnection.setConnectTimeout(3000);//设置网络连接超时的时间为3秒 httpURLConnection.setRequestMethod("GET"); //设置请求方法为GET httpURLConnection.setDoInput(true); //打开输入流 int responseCode = httpURLConnection.getResponseCode(); // 获取服务器响应值 if (responseCode == HttpURLConnection.HTTP_OK) { //正常连接 inputStream = httpURLConnection.getInputStream(); //获取输入流 } } return inputStream; } }
StringUtils:
package com.inwhoop.xbzhjypt.main.util; public class StringUtils { public static boolean isEmpty(String str) { if ("".equals(str) || str == null) { return true; } else { return false; } } public static boolean isNotEmpty(String str) { return str != null && (!"".equals(str)); } public static String join(String[] arr, String sep) { if (arr == null) { return null; } if (arr.length == 0) { return ""; } if (sep == null) { sep = ""; } StringBuilder builder = new StringBuilder(); int i = 0; for (String string : arr) { if (i != 0) { builder.append(sep); } builder.append(string); i++; } return builder.toString(); } public static String join(String[] arr) { return join(arr, ","); } }
另外本人一篇简单的自定义view类可以参考
/article/10893045.html
相关文章推荐
- Linux(suse11) 安装 JDK (.gz版本)
- iOS 9 ContactsFramework
- 第九周实践项目~对称矩阵压缩存储的实现与应用(1)
- 第十一周 项目一 验证算法
- DOCTYPE声明作用及用法详解
- 黑马程序员——枚举和动态代理
- (2)二叉树构造算法的验证
- 项目应用中的卡尔曼滤波
- 基于Ubuntu+Apache+Passenger+MySQL的Redmine安装笔记
- 用UIButton实现页面跳转(AppDelegate +NavigationViewController)
- c++ 智能指针用法详解
- nrf51822之间通讯
- java运行时间小程序
- css/js(工作中遇到的问题)-2
- 第11周实践项目1验证算法-哈夫曼编码的算法验证
- 第六周项目4 后缀表达式
- 第11周—项目1(2).1由后序序列和中序序列构造二叉树
- phonegap创建项目错误
- 第十一周项目1(1) - 二叉树层次遍历算法的验证
- 第十一周 项目1-3 线索化二叉树