记得以前写过一篇双指拉伸图片的博客,那里基本是可以的,文章最后推出了一个问题关于Bitmap的优化。那篇博客用的是拉伸过程当中不断地创建Bitmap的老套方法,不能说是老套方法,因为那个方法放在这个应用场景下是不能用的,会照成内存泄露最终OOM。其实imageview里面有一个setImageMatrix函数,直接调用矩阵变换图片会非常容易解决这个问题。
下面这个程序会从文件中读取几十张图片(默认为23张并且有固定的名称,不过那都是小问题可以根据自己来改),然后用了缓存技术改进下性能(缓存在这里不明显,如果用在网络图片的加载而不是本地图片的加载会比较明显,这种缓存是用LRU而没有用软引用队列,因为听说现在软引用和弱引用在系统GC的时候都会被强制回收掉所以没有必要,但是究竟是不是还要查一下)。支持双指拉伸图片和单指移动图片,会记录上次的页码。
最后提一下,如果我还忘记把onTouch的返回值改为true我就剁手。。
下面是代码,写的有点乱:
package com.example.mytest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapFactory.Options;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.RectF;
import android.os.Bundle;
import android.os.Environment;
import android.support.v4.util.LruCache;
import android.util.FloatMath;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.ImageView;
public class MainActivity extends Activity {
// 三个控件
private ImageView iv;
private Button pre;
private Button next;
// 当前页码和最大页码
private int curPage = 1;
final private int maxPage = 23;
// 屏幕的窗口大小
private int windowsWidth;
private int windowsHeight;
// 当前图片的大小
private int picWidth = 0;
private int picHeight = 0;
// 图片路径
private String parentPath = Environment.getExternalStorageDirectory()
+ "/finger/";
// 当前图片的路径
private String curPath;
// 缓存所需容器
private LruCache<String, Bitmap> mMemoryCache;
// 旧的两指距离
private float oldDis = 0.0f;
// 新的两指距离
private float newDis = 0.0f;
// 单指点击时起始位置
private PointF startPoint;
// 判断是否是双指
private boolean touchAgain = false;
// 存储图片缩放比
private float scarePre = 1.0f;
// 判断当前图片是否已经被移动过
private boolean isTouchedAgain = false;
// 辅助判断的条件,判断在同一个页面中是否是第一次移动
private boolean isFirstMoved = true;
// 双指触控所需组件
private Paint paint;
private Matrix matrix;
// 最少缩放比例
float minScaleR = 0.1f;
// 最大缩放比例
static final float MAX_SCALE = 4f;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 获得起始页面
SharedPreferences sp = getSharedPreferences("config",
Context.MODE_PRIVATE);
int t = sp.getInt("page", 0);
if (t != 0) {
curPage = t;
}
// 生成当前图片的路径
curPath = parentPath + curPage + ".png";
pre = (Button) findViewById(R.id.pre);
next = (Button) findViewById(R.id.next);
iv = (ImageView) findViewById(R.id.iv);
// 初始化画笔和矩阵
paint = new Paint();
paint.setColor(Color.BLACK);
matrix = new Matrix();
startPoint = new PointF();
// 计算缓存最大内存
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
final int cacheSize = maxMemory / 8;
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
windowsWidth = wm.getDefaultDisplay().getWidth();
windowsHeight = wm.getDefaultDisplay().getHeight();
mMemoryCache = new LruCache<String, Bitmap>((cacheSize)) {
@SuppressLint("NewApi")
@Override
protected int sizeOf(String key, Bitmap value) {
// TODO Auto-generated method stub
return value.getByteCount() / 1024;
}
};
// 加载第一张图片
Bitmap firstPic = creatBm(curPage);
iv.setImageBitmap(firstPic);
// 缓存当前和下一张图片
savePage(curPage);
if (curPage < maxPage) {
savePage(curPage + 1);
}
/**
* 上一张按钮的监听器
*/
pre.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
if (curPage <= 1) {
return;
}
curPage--;
Bitmap bm = mMemoryCache.get(curPage + "");
if (bm == null) {
curPath = parentPath + curPage + ".png";
bm = creatBm(curPage);
}
iv.setImageBitmap(bm);
isTouchedAgain = false;
// 缓存前一张图片
if (curPage > 1) {
savePage(curPage - 1);
}
}
});
/**
* 下一张按钮的监听器
*/
next.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
if (curPage >= maxPage) {
return;
}
curPage++;
Bitmap bm = mMemoryCache.get(curPage + "");
if (bm == null) {
curPath = parentPath + curPage + ".png";
bm = creatBm(curPage);
}
iv.setImageBitmap(bm);
isTouchedAgain = false;
// 缓存下一张图片
if (curPage < maxPage) {
savePage(curPage + 1);
}
}
});
/**
* 为imageview设置点击事件。作用:进行图片的放大和缩小。
*/
iv.setOnTouchListener(new OnTouchListener() {
// 当前的缩放比
private float scale = 0.0f;
@Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
switch (event.getAction() & MotionEvent.ACTION_MASK) {
// 第一个手指按下
case MotionEvent.ACTION_DOWN:
// 记录按下位置,方便后面单指拖拽操作
startPoint.set(event.getX(), event.getY());
break;
// 第二个手指按下
case MotionEvent.ACTION_POINTER_DOWN:
touchAgain = true;
oldDis = spacing(event);
break;
// 手指移动
case MotionEvent.ACTION_MOVE:
if (touchAgain == false) {
// 仅有一个手指下做拖拽操作
matrix.postTranslate(
(event.getX() - startPoint.x) / 16.0f,
(event.getY() - startPoint.y) / 16.0f);
iv.setImageMatrix(matrix);
break;
}
newDis = spacing(event);
// 微小变化不作处理
if (newDis > (oldDis - 10.0f) && newDis < (oldDis + 10.0f)) {
break;
}
// 计算缩放比
scale = newDis / oldDis;
if (scale > 1) {
float t = scale - 1.0f;
t = t / 2.0f;
scale = scale - t;
}
if (scale < 1) {
float t = 1.0f - scale;
t = t / 2.0f;
scale = scale + t;
}
// 如果是原来的页面
if (isTouchedAgain == true) {
// 如果在该页面是第一次移动
if (isFirstMoved == true) {
scale = scarePre;
isFirstMoved = false;
} else {
// 在原来的页面不是第一次移动
scale = scale * scarePre;
}
}
// 计算出新的宽高
matrix.setScale(scale, scale);
// 图片居中
center();
iv.setImageMatrix(matrix);
newDis = oldDis;
break;
case MotionEvent.ACTION_POINTER_UP:
touchAgain = false;
// 保存这次结束后的缩放比
scarePre = scale;
isTouchedAgain = true;
isFirstMoved = true;
break;
default:
break;
}
return true;
}
});
}
/**
*
* 缓存图片函数
*
* @param page
* :要缓存图片的页码
*
*
*/
private void savePage(int page) {
Bitmap curPic = mMemoryCache.get(page + "");
if (curPic == null) {
curPic = creatBm(page);
}
mMemoryCache.put(page + "", curPic);
}
/**
* 根据屏幕大小进行创建并缩放图片
*
* @param page
* :要创建的图片的标示
* @return 创建的Bitmap
*/
private Bitmap creatBm(int page) {
String path = parentPath + page + ".png";
Options opts = new Options();
opts.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, opts);
picWidth = opts.outWidth;
picHeight = opts.outHeight;
float scareWidth = (float) picWidth / windowsWidth;
float scareHeight = (float) picHeight / windowsHeight;
float scare = (scareWidth > scareHeight) ? scareWidth : scareHeight;
if (scare < 1) {
scare = 1;
}
opts.inJustDecodeBounds = false;
opts.inSampleSize = (int) scare;
Bitmap bm = BitmapFactory.decodeFile(path, opts);
return bm;
}
/**
* 求出两点之间的距离
*
* @param event
* 触摸事件
* @return 两点之间的距离
*/
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);
}
/**
* 居中图片
*/
protected void center() {
center(true, true);
}
/**
*
* @param horizontal
* 水平方向是否居中
* @param vertical
* 竖直方向是否居中
*/
private void center(boolean horizontal, boolean vertical) {
RectF rect = new RectF(0, 0, picWidth, picHeight);
matrix.mapRect(rect);
float height = rect.height();
float width = rect.width();
float deltaX = 0, deltaY = 0;
if (vertical) {
if (height < windowsHeight) {
deltaY = (windowsHeight - height) / 2 - rect.top;
} else if (rect.top > 0) {
deltaY = -rect.top;
} else if (rect.bottom < windowsHeight) {
deltaY = windowsHeight - rect.bottom;
} else {
deltaY = -(height - windowsHeight) / 2;
}
}
if (horizontal) {
if (width < windowsWidth) {
deltaX = (windowsWidth - width) / 2 - rect.left;
System.out.println(1);
} else if (rect.left > 0) {
deltaX = -rect.left;
System.out.println(2);
} else if (rect.right < windowsWidth) {
deltaX = windowsWidth - rect.right;
System.out.println(3);
} else {
deltaX = -(width - windowsWidth) / 2;
}
}
matrix.postTranslate(deltaX, deltaY);
}
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
SharedPreferences sp = getSharedPreferences("config",
Context.MODE_PRIVATE);
Editor ed = sp.edit();
ed.putInt("page", curPage);
ed.commit();
}
}