您的位置:首页 > 其它

自定义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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: