一、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

更多相关文章

  1. Android(安卓)Bitmap的加载和Cache
  2. Android(安卓)开发 调用图库选择图片实现和参数详解
  3. android在EditText中插入表情图片
  4. Android(安卓)图片缩放-Matrix
  5. Android自用-----AsyncTask实现异步处理任务
  6. Android(安卓)Button及TextView动态变换颜色
  7. 【Android】ViewPager实现图片左右滑动播放及添加点击事件
  8. webview 笔记二(android和js交互、包括链接跳转常见问题处理,加载
  9. Android异步处理常用方法

随机推荐

  1. 【国外转】Spring Android and Maven (Ma
  2. android kernel下載及編譯 (goldfish)
  3. android 检测应用异常 UncaughtException
  4. Android Activity去除标题栏和状态栏(z)
  5. android中drawable显示到view上的过程
  6. 控件:拖动条 --- SeekBar(改变屏幕亮度)
  7. android 代码设置apn
  8. Android 通过代码执行Linux 命令 echo 命
  9. android的单个图片 上传服务器
  10. 网址收藏