Android imageview画像のスケーリングを実現

18153 ワード

一、Matrix詳細
Androidでは、Matrixで画像処理をしたことがあれば、Matrixというクラスを知っているに違いありません.AndroidのMatrixは3 x 3の行列で、その内容は以下の通りです.
 
Matrixの画像に対する処理は4種類の基本変換に分けられる.
Translateパン変換
Rotate回転変換
スケール変換
Skew正接変換
マトリクス内のMSCALEはスケーリング変換の処理に用いられ、MSKEWはタンジェント変換の処理に用いられ、MTRANSはパン変換の処理に用いられ、MPERSPはピボット変換の処理に用いられる.実际には、Matrixを完全に文字通りに理解することはできません.また,Androidの文書では,Matrixによる透視変換に関する説明は見られないため,本稿ではこの点についても議論しない.
 
Androidは、変換ごとにpre、set、postの3つの操作方法を提供しています.内
setは、Matrixの値を設定します.
preは先乗であり,行列の乗算は交換則を満たさないため,先乗,後乗は厳密に区別しなければならない.先乗はマトリクス演算の右乗に相当します.
postは後乗であり,行列の乗算は交換則を満たさないため,先乗,後乗は厳密に区別しなければならない.後乗はマトリクス演算の左乗に相当します.
 
トランスレーション変換(Translate)に加えて、回転変換(Rotate)、スケール変換(Scale)、およびスキュー変換(Skew)は、1つの中心点の周りで行うことができ、指定しない場合、デフォルトでは(0,0)の周りで対応する変換を行うことができます.
二、ImageViewの画像の4つの座標を取得する方法
画像の四角座標を取得するには、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レイアウトファイル



    


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