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();
// 获取图片的Matrix
mMatrix.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"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/black"
    android:orientation="vertical">

    <com.jyy.lzn.supportdesigntest.widget.JImageView
        android:id="@+id/news_image_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_centerVertical="true"
        android:layout_centerHorizontal="true"
        android:scaleType="center"/>

</RelativeLayout>

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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值