一、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 开发 调用图库选择图片实现和参数详解
  2. android在EditText中插入表情图片
  3. 【Android】ViewPager实现图片左右滑动播放及添加点击事件
  4. Android Picasso 图片加载库基础使用详解
  5. android加载网络图片(逐行扫描格式png图片)的一个bug
  6. [转] Android TextView处理HTML标签、显示图片等
  7. 2014-11-8Android学习------Android 实现图片的旋转--------动画
  8. android中使用线程池和临时缓存优化网络图片加载
  9. Android Nine Patch图片及按钮背景

随机推荐

  1. Android 高级UI解密 (三) :Canvas裁剪 与
  2. Android中隐藏标题栏和状态栏(电源,信号,时
  3. android studio 使用NDK和swig编译c++示
  4. Android深度定制化TabLayout:圆角,渐变色,背
  5. Android: Android学习的几点建议
  6. Android的数据处理:使用annotation实现JSO
  7. Android Retrofit与Spring后台配合,实现单
  8. Android如何获取APP启动时间
  9. [置顶] Android防止内存溢出浅析
  10. Java OR Android