之前见到好多人是用替换ImageView或者用Bitmap的属性设置来实现图片的缩放和移动,这样做会很容易引起虚拟机的OOM错误和不易控制,最重要的是看起来很复杂。
如果直接用ImageView和Matrix呢?
废话不多说,直接上代码,大家看运行效果吧。
package sunbo.app.preview; import android.app.Activity; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Matrix; import android.graphics.PointF; import android.graphics.RectF; import android.os.Bundle; import android.os.Handler; import android.util.DisplayMetrics; import android.util.FloatMath; import android.view.MotionEvent; import android.view.View; import android.view.View.OnTouchListener; import android.widget.ImageView; public class ImagePreview extends Activity implements OnTouchListener { private ImageView mImageView; private final Matrix matrix = new Matrix(); private final Matrix savedMatrix = new Matrix(); private DisplayMetrics mDisplyMetrcs; private Bitmap mBitmap; private float minScaleR;// 最小缩放比例 private static final float MAX_SCALE = 4f;// 最大缩放比例 private static final int NONE = 0;// 初始状态 private static final int DRAG = 1;// 拖动 private static final int ZOOM = 2;// 缩放 private int mode = NONE; private final PointF prev = new PointF(); private final PointF mid = new PointF(); private float dist = 1f; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.setContentView(R.layout.main); this.setupViews(); } private void setupViews() { // 实例化图片控件 mImageView = (ImageView) this.findViewById(R.id.mImageView); // 引用本地资源文件创建位图对象 mBitmap = BitmapFactory.decodeResource(this.getResources(), R.drawable.image); // 将位图对象设置到图片控件中 mImageView.setImageBitmap(mBitmap); // 为图片控件添加触控事件 mImageView.setOnTouchListener(this); // 获取当前屏幕分辨率对象 mDisplyMetrcs = new DisplayMetrics(); this.getWindowManager().getDefaultDisplay().getMetrics(mDisplyMetrcs); this.setMinZoom(); this.setCenter(); new Handler().postDelayed(new Runnable() { public void run() { // 程序启动0.5秒以后设置图片控件的缩放属性 // 如果在描述文件或一开始就设置,那么,图片就会出现在屏幕的左上角,而我们希望图片出现在屏幕的中间位置 mImageView.setScaleType(ImageView.ScaleType.MATRIX); } }, 500); mImageView.setImageMatrix(matrix); } @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction() & MotionEvent.ACTION_MASK) { // 主点按下 case MotionEvent.ACTION_DOWN: savedMatrix.set(matrix); prev.set(event.getX(), event.getY()); mode = DRAG; break; // 副点按下 case MotionEvent.ACTION_POINTER_DOWN: dist = spacing(event); // 如果连续两点距离大于10,则判定为多点模式 if (spacing(event) > 10f) { savedMatrix.set(matrix); midPoint(mid, event); mode = ZOOM; } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_POINTER_UP: mode = NONE; break; case MotionEvent.ACTION_MOVE: if (mode == DRAG) { matrix.set(savedMatrix); matrix.postTranslate(event.getX() - prev.x, event.getY() - prev.y); } else if (mode == ZOOM) { float newDist = spacing(event); if (newDist > 10f) { matrix.set(savedMatrix); float tScale = newDist / dist; matrix.postScale(tScale, tScale, mid.x, mid.y); } } break; } mImageView.setImageMatrix(matrix); this.checkView(); return true; } private void checkView() { float p[] = new float[9]; matrix.getValues(p); if (mode == ZOOM) { if (p[0] < minScaleR) matrix.setScale(minScaleR, minScaleR); if (p[0] > MAX_SCALE) matrix.set(savedMatrix); } this.setCenter(); } /** * 设置最小缩放比列,最大值和图片大小相等 */ private void setMinZoom() { minScaleR = Math.min( (float) mDisplyMetrcs.widthPixels / (float) mBitmap.getWidth(), (float) mDisplyMetrcs.heightPixels / (float) mBitmap.getHeight()); if (minScaleR < 1.0) matrix.postScale(minScaleR, minScaleR); } private void setCenter() { this.setCenter(true, true); } private void setCenter(boolean horizontal, boolean vertical) { Matrix mMatrix = new Matrix(); mMatrix.set(matrix); RectF mRectF = new RectF(0, 0, mBitmap.getWidth(), mBitmap.getHeight()); mMatrix.mapRect(mRectF); float height = mRectF.height(); float width = mRectF.width(); float deltaX = 0, deltaY = 0; if (vertical) { // 图片小于屏幕大小,则居中显示。大于屏幕,上方留空则往上移,下方留空则往下移 int screenHeight = mDisplyMetrcs.heightPixels; if (height < screenHeight) deltaY = (screenHeight - height) / 2 - mRectF.top; else if (mRectF.top > 0) deltaY = -mRectF.top; else if (mRectF.bottom < screenHeight) deltaY = mImageView.getHeight() - mRectF.bottom; } if (horizontal) { int screenWidth = mDisplyMetrcs.widthPixels; if (width < screenWidth) deltaX = (screenWidth - width) / 2 - mRectF.left; else if (mRectF.left > 0) deltaX = -mRectF.left; else if (mRectF.right < screenWidth) deltaX = screenWidth - mRectF.right; } matrix.postTranslate(deltaX, deltaY); } /** * 两点的距离 * * @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); } /** * 两点的中点 * * @param point * @param event */ 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); } }
布局文件:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent"> <ImageView android:id="@+id/mImageView" android:layout_width="fill_parent" android:layout_height="fill_parent" /> </LinearLayout>
好了,到此结束。
本程序是一个简单的Demo,所以只支持多点触摸进行缩放,测试需在真机中运行,所以,在这里向用模拟器测试的同学表示遗憾。
好了,至此,由于我也是在模拟器中运行,所以,缩放效果无法展示,请大家自测: