Android(安卓)imageview图片缩放实现
一、Matrix详解
在Android中,如果你用Matrix进行过图像处理,那么一定知道Matrix这个类。Android中的Matrix是一个3 x 3的矩阵,其内容如下:
Matrix的对图像的处理可分为四类基本变换:
Translate 平移变换
Rotate 旋转变换
Scale 缩放变换
Skew 错切变换
从字面上理解,矩阵中的MSCALE用于处理缩放变换,MSKEW用于处理错切变换,MTRANS用于处理平移变换,MPERSP用于处理透视变换。实际中当然不能完全按照字面上的说法去理解Matrix。同时,在Android的文档中,未见到用Matrix进行透视变换的相关说明,所以本文也不讨论这方面的问题。
针对每种变换,Android提供了pre、set和post三种操作方式。其中
set用于设置Matrix中的值。
pre是先乘,因为矩阵的乘法不满足交换律,因此先乘、后乘必须要严格区分。先乘相当于矩阵运算中的右乘。
post是后乘,因为矩阵的乘法不满足交换律,因此先乘、后乘必须要严格区分。后乘相当于矩阵运算中的左乘。
除平移变换(Translate)外,旋转变换(Rotate)、缩放变换(Scale)和错切变换(Skew)都可以围绕一个中心点来进行,如果不指定,在默认情况下是围绕(0, 0)来进行相应的变换的。
二、如何获取ImageView中图片的四个坐标
要获取图片的四角坐标,我们需要结合image的Matrix和边界Rect来处理
// 获取image边界矩形Rect rect = imageView.getDrawable().getBounds();// 获取图片的MatrixmMatrix.set(imageView.getImageMatrix());// 将Matrix中9个参数赋值到values float[] values = new float[9];mMatrix.getValues(values);
之后我们可以根据上述信息来获取四角坐标
//计算图片四角坐标float leftTopX = values[Matrix.MTRANS_X];float leftTopY = values[Matrix.MTRANS_Y];float leftBottomX = values[Matrix.MTRANS_X];float leftBottomY = values[Matrix.MTRANS_Y] + rect.height() * values[Matrix.MSCALE_Y];float rightTopX = values[Matrix.MTRANS_X] + rect.width() * values[Matrix.MSCALE_X];float rightTopY = values[Matrix.MTRANS_Y];float rightBottomX = values[Matrix.MTRANS_X] + rect.width() * values[Matrix.MSCALE_X];float rightBottomY = values[Matrix.MTRANS_Y] + rect.height() * values[Matrix.MSCALE_Y];
三、详细实现代码
1 布局文件
<?xml version="1.0" encoding="utf-8"?>
2 图片显示组件
import android.content.Context;import android.graphics.Bitmap;import android.graphics.Matrix;import android.graphics.PointF;import android.graphics.Rect;import android.os.Bundle;import android.support.annotation.Nullable;import android.support.v4.app.Fragment;import android.support.v4.app.NavUtils;import android.support.v4.view.ViewPager;import android.util.Log;import android.view.GestureDetector;import android.view.LayoutInflater;import android.view.MotionEvent;import android.view.View;import android.view.ViewGroup;import android.widget.ImageView;import com.jyy.lzn.supportdesigntest.R;import com.jyy.lzn.supportdesigntest.news.bean.Node;import com.jyy.lzn.supportdesigntest.util.DisplayUtil;import com.squareup.picasso.Picasso;/** * Created by HP on 2016/9/22. */public class NewsImageViewerFragment extends Fragment { public static final String TAG = "NewsImageViewerFragment"; private ViewPager mViewPager; private Node mNode; private ImageView imageView; // 两手指间距离 private float mFingerDistance = 0; // 当前触摸模式 private int mOperateMode = -1; private static final int MOVE = 0; private static final int ZOOM = 1; // 当前位置 private PointF mCurrentPointF = new PointF(); // 两手指间中间坐标点 private PointF mMidPointF = new PointF(); private Matrix mMatrix = new Matrix(); private Matrix mCurrentMatrix = new Matrix(); // 最大放大倍数 private static final float MAX_SCALE = 3f; private float MIN_SCALE = 1f; private boolean mIsReachMinScale = true; private boolean mIsReachMaxScale = false; // 手指间最小间隔距离 private static final float MIN_FINGER_DISTANCE = 10f; // 图片默认宽高 private int requestedWidth; private int requestedHeight; private GestureDetector mGestureDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener()); public static Fragment newInstance(Node node) { NewsImageViewerFragment newsImageViewerFragment = new NewsImageViewerFragment(); Bundle bundle = new Bundle(); bundle.putSerializable(TAG, node); newsImageViewerFragment.setArguments(bundle); return newsImageViewerFragment; } @Override public void onAttach(Context context) { super.onAttach(context); } @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); mNode = (Node) getArguments().getSerializable(TAG); } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_news_image_viewer, container, false); mViewPager = ((ViewPager) getActivity().findViewById(R.id.news_image_view_pager)); mGestureDetector.setOnDoubleTapListener(mOnDoubleTapListener); // 加载图片 imageView = (ImageView) view.findViewById(R.id.news_image_view); imageView.setOnTouchListener(onTouchListener); // 重置ImageView尺寸 requestedWidth = DisplayUtil.windowWidth(getContext()); requestedHeight = (int) (requestedWidth / mNode.getAspectRatio()); Picasso.with(getActivity()).load(mNode.getValue()).config(Bitmap.Config.ARGB_8888).resize(requestedWidth, requestedHeight).placeholder(R.drawable.imageview_default_bg).tag(TAG).into(imageView); return view; } private View.OnTouchListener onTouchListener = new View.OnTouchListener() { @Override public boolean onTouch(View view, MotionEvent event) { imageView.setScaleType(ImageView.ScaleType.MATRIX); if (mGestureDetector.onTouchEvent(event)) { return true; } switch (event.getAction() & MotionEvent.ACTION_MASK) { // 单点触摸动作 case MotionEvent.ACTION_DOWN: mOperateMode = MOVE; // 记录当前位置坐标 mCurrentPointF.set(event.getX(), event.getY()); mCurrentMatrix.set(imageView.getImageMatrix()); break; // 触摸点移动动作 case MotionEvent.ACTION_MOVE: mViewPager.requestDisallowInterceptTouchEvent(true); if (mOperateMode == MOVE) { drag(event); } else if (mOperateMode == ZOOM) { float endFingerDistance = fingerDistance(event); if (endFingerDistance > MIN_FINGER_DISTANCE) { float scale = endFingerDistance / mFingerDistance; zoom(scale); } } break; // 多点触摸动作 case MotionEvent.ACTION_POINTER_DOWN: mOperateMode = ZOOM; // 记录当前位置坐标 mFingerDistance = fingerDistance(event); if (mFingerDistance > MIN_FINGER_DISTANCE) { mCurrentPointF.set(event.getX(), event.getY()); mCurrentMatrix.set(imageView.getImageMatrix()); midFingerDistance(event); } else { mOperateMode = MOVE; } break; // 单点触摸离开动作 case MotionEvent.ACTION_UP: mOperateMode = -1; break; // 多点离开动作 case MotionEvent.ACTION_POINTER_UP: mOperateMode = -1; adjustPosition(); break; } return true; } }; /** * 调整图片位置 */ private void adjustPosition() { if (mIsReachMinScale) { imageView.setScaleType(ImageView.ScaleType.CENTER); } int screenWidth = DisplayUtil.windowWidth(getContext()); Rect rect = imageView.getDrawable().getBounds(); mMatrix.set(imageView.getImageMatrix()); float[] values = new float[9]; mMatrix.getValues(values); float leftTopX = values[Matrix.MTRANS_X]; float rightTopX = values[Matrix.MTRANS_X] + rect.width() * values[Matrix.MSCALE_X]; if (leftTopX > 0) { values[Matrix.MTRANS_X] = 0f; } else if (rightTopX < screenWidth) { values[Matrix.MTRANS_X] = screenWidth - (rect.width() * values[Matrix.MSCALE_X]); } // 调整上下边界 float dy = DisplayUtil.windowHeight(getContext()) - DisplayUtil.getSystemStatueBarHeight(getContext()) - rect.height() * values[Matrix.MSCALE_Y]; values[Matrix.MTRANS_Y] = dy / 2; // 更新图片矩阵 mMatrix.setValues(values); imageView.setImageMatrix(mMatrix); } /** * 处理图片拖动 * * @param event */ private void drag(MotionEvent event) { if (mIsReachMinScale) { mViewPager.requestDisallowInterceptTouchEvent(false); return; } boolean isFullScreen; int screenWidth = DisplayUtil.windowWidth(getContext()); int screenHeight = DisplayUtil.windowHeight(getContext()); float transX = event.getX() - mCurrentPointF.x; float transY = event.getY() - mCurrentPointF.y; Rect rect = imageView.getDrawable().getBounds(); float[] intiValues = new float[9]; mCurrentMatrix.getValues(intiValues); float height = rect.height() * intiValues[Matrix.MSCALE_Y]; // 图片没有充满屏幕 所以y轴禁止拖动 if (height < screenHeight) { isFullScreen = false; transY = 0; } else { isFullScreen = true; } mMatrix.set(mCurrentMatrix); mMatrix.postTranslate(transX, transY); float[] values = new float[9]; mMatrix.getValues(values); //计算图片四角坐标 float leftTopX = values[Matrix.MTRANS_X]; float leftTopY = values[Matrix.MTRANS_Y]; float leftBottomX = values[Matrix.MTRANS_X]; float leftBottomY = values[Matrix.MTRANS_Y] + rect.height() * values[Matrix.MSCALE_Y]; float rightTopX = values[Matrix.MTRANS_X] + rect.width() * values[Matrix.MSCALE_X]; float rightTopY = values[Matrix.MTRANS_Y]; float rightBottomX = values[Matrix.MTRANS_X] + rect.width() * values[Matrix.MSCALE_X]; float rightBottomY = values[Matrix.MTRANS_Y] + rect.height() * values[Matrix.MSCALE_Y]; if (isFullScreen) { // 图片充满屏幕处理 // 左上角越界处理 if (leftTopX > 0 && leftTopY > 0) { values[Matrix.MTRANS_X] = 0f; values[Matrix.MTRANS_Y] = 0f; mViewPager.requestDisallowInterceptTouchEvent(false); } else if (leftTopX > 0 && leftTopY < 0) { values[Matrix.MTRANS_X] = 0f; mViewPager.requestDisallowInterceptTouchEvent(false); } else if (leftTopX < 0 && leftTopY > 0) { values[Matrix.MTRANS_Y] = 0f; } // 左下角越界处理 if (leftBottomX > 0 && leftBottomY < screenHeight) { values[Matrix.MTRANS_X] = 0f; values[Matrix.MTRANS_Y] = screenHeight - (rect.height() * values[Matrix.MSCALE_Y]); mViewPager.requestDisallowInterceptTouchEvent(false); } else if (leftBottomX > 0 && leftBottomY > screenHeight) { values[Matrix.MTRANS_X] = 0f; mViewPager.requestDisallowInterceptTouchEvent(false); } else if (leftBottomX < 0 && leftBottomX > screenHeight) { values[Matrix.MTRANS_Y] = screenHeight - (rect.height() * values[Matrix.MSCALE_Y]); } // 右上角越界处理 if (rightTopX < screenWidth && rightTopY > 0) { values[Matrix.MTRANS_X] = screenWidth - (rect.width() * values[Matrix.MSCALE_X]); values[Matrix.MTRANS_Y] = 0; mViewPager.requestDisallowInterceptTouchEvent(false); } else if (rightTopX < screenWidth && rightTopY < 0) { values[Matrix.MTRANS_X] = screenWidth - (rect.width() * values[Matrix.MSCALE_X]); mViewPager.requestDisallowInterceptTouchEvent(false); } else if (rightTopX > screenWidth && rightTopY > 0) { values[Matrix.MTRANS_Y] = 0; } // 右下角越界处理 if (rightBottomX < screenWidth && rightBottomY < screenHeight) { values[Matrix.MTRANS_X] = screenWidth - (rect.width() * values[Matrix.MSCALE_X]); values[Matrix.MTRANS_Y] = screenHeight - (rect.height() * values[Matrix.MSCALE_Y]); mViewPager.requestDisallowInterceptTouchEvent(false); } else if (rightBottomX < screenWidth && rightBottomY > screenHeight) { values[Matrix.MTRANS_X] = screenWidth - (rect.width() * values[Matrix.MSCALE_X]); mViewPager.requestDisallowInterceptTouchEvent(false); } else if (rightBottomX > screenWidth && rightBottomY < screenHeight) { values[Matrix.MTRANS_Y] = screenHeight - (rect.height() * values[Matrix.MSCALE_Y]); } } else { // 图片没有充满屏幕处理 if (leftTopX > 0) { values[Matrix.MTRANS_X] = 0f; mViewPager.requestDisallowInterceptTouchEvent(false); } else if (rightBottomX < screenWidth) { values[Matrix.MTRANS_X] = screenWidth - (rect.width() * values[Matrix.MSCALE_X]); mViewPager.requestDisallowInterceptTouchEvent(false); } } mMatrix.setValues(values); imageView.setImageMatrix(mMatrix); } /** * 处理图片缩放 * * @param scale */ private void zoom(float scale) { if (mIsReachMaxScale && scale > 1 || mIsReachMinScale && scale < 1) { return; } mMatrix.set(mCurrentMatrix); mMatrix.postScale(scale, scale, DisplayUtil.windowWidth(getContext()) / 2, DisplayUtil.windowHeight(getContext()) / 2 - DisplayUtil.getSystemStatueBarHeight(getContext()) / 2); float[] values = new float[9]; mMatrix.getValues(values); if (values[Matrix.MSCALE_X] >= MAX_SCALE) { mIsReachMinScale = false; mIsReachMaxScale = true; values[Matrix.MSCALE_X] = MAX_SCALE; values[Matrix.MSCALE_Y] = MAX_SCALE; } else if (values[Matrix.MSCALE_X] <= MIN_SCALE) { mIsReachMinScale = true; mIsReachMaxScale = false; values[Matrix.MSCALE_X] = MIN_SCALE; values[Matrix.MSCALE_Y] = MIN_SCALE; } else { mIsReachMinScale = false; mIsReachMaxScale = false; } mMatrix.setValues(values); imageView.setImageMatrix(mMatrix); } /** * 计算两指之间距离 * * @param event * @return */ private float fingerDistance(MotionEvent event) { try { float dx = event.getX(1) - event.getX(0); float dy = event.getY(1) - event.getX(0); return (float) Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2)); } catch (IllegalArgumentException e) { e.printStackTrace(); } return 0; } /** * 计算两指间中间点 * * @param event */ private void midFingerDistance(MotionEvent event) { try { float dx = event.getX(1) + event.getX(0); float dy = event.getY(1) + event.getX(0); mMidPointF.set(dx / 2, dy / 2); } catch (IllegalArgumentException e) { e.printStackTrace(); } } private GestureDetector.OnDoubleTapListener mOnDoubleTapListener = new GestureDetector.OnDoubleTapListener() { @Override public boolean onSingleTapConfirmed(MotionEvent motionEvent) { if (NavUtils.getParentActivityName(getActivity()) != null) { NavUtils.navigateUpFromSameTask(getActivity()); } return true; } @Override public boolean onDoubleTap(MotionEvent motionEvent) { mMatrix.set(imageView.getImageMatrix()); if (mIsReachMinScale) { mMatrix.postScale(MAX_SCALE, MAX_SCALE, DisplayUtil.windowWidth(getContext()) / 2, DisplayUtil.windowHeight(getContext()) / 2 - DisplayUtil.getSystemStatueBarHeight(getContext()) / 2); imageView.setImageMatrix(mMatrix); mIsReachMinScale = false; mIsReachMaxScale = true; float[] values = new float[9]; mMatrix.getValues(values); Log.d(TAG, "y: " + values[Matrix.MTRANS_Y]); } else { mMatrix.postScale(MIN_SCALE, MIN_SCALE, DisplayUtil.windowWidth(getContext()) / 2, DisplayUtil.windowHeight(getContext()) / 2); imageView.setImageMatrix(mMatrix); imageView.setScaleType(ImageView.ScaleType.CENTER); mIsReachMinScale = true; mIsReachMaxScale = false; } return true; } @Override public boolean onDoubleTapEvent(MotionEvent motionEvent) { return true; } };}
注意点:由于ImageView设置的宽高是match_parent,所以在处理出示图片居中问题时,可以先将scaleType设置为center,之后再进行图片处理的时候将scaleType动态的设置为matrix
四、参考资料
1 http://blog.csdn.net/flash129/article/details/8234599
2 http://www.cnblogs.com/linjzong/p/4211661.html
更多相关文章
- Android(安卓)Bitmap的加载和Cache
- Android(安卓)开发 调用图库选择图片实现和参数详解
- android在EditText中插入表情图片
- Android(安卓)图片缩放-Matrix
- Android自用-----AsyncTask实现异步处理任务
- Android(安卓)Button及TextView动态变换颜色
- 【Android】ViewPager实现图片左右滑动播放及添加点击事件
- webview 笔记二(android和js交互、包括链接跳转常见问题处理,加载
- Android异步处理常用方法