AndroidはScaleGestureDetectorで画像のズームを実現

14409 ワード

               ,    OnTouch()      ,            ,
    ScaleGestureDetector        ,      !
/** *      ImageView * @description: * @author ldm * @date 2016-4-13   11:08:53 */
blic class ScaleImageView extends ImageView implements OnGlobalLayoutListener,
        OnTouchListener, OnScaleGestureListener {
    //        
    private boolean mIsFirst = false;
    //          
    private float mBaseScale;
    //         
    private float mMaxScale;
    //          Matrix
    private Matrix mImageMatrix;
    //                    
    private ScaleGestureDetector mScaleGestureDetector;

    /** *           View         * * @param context */
    public ScaleImageView(Context context) {
        this(context, null);
    }

    public ScaleImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ScaleImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    /** *       * * @description: * @author ldm * @date 2016-4-12   4:57:04 */
    private void init(Context context) {
        mImageMatrix = new Matrix();
        super.setScaleType(ScaleType.MATRIX);
        mScaleGestureDetector = new ScaleGestureDetector(context, this);
        setOnTouchListener(this);
    }

    //  View         OnGlobalLayoutListener  ,            view   。
    @Override
    public void onGlobalLayout() {
        if (!mIsFirst) {
            mIsFirst = true;
            //           
            int width = getWidth();
            int height = getHeight();
            //    ImageView          
            Drawable d = getDrawable();
            if (null == d) {
                return;
            }
            int dw = d.getIntrinsicWidth();//       
            int dh = d.getIntrinsicHeight();//       
            float scale = 1.0f;
            if (dw > width && dh < height) {//                    
                scale = width * 1.0f / dw;//      
            }
            //                    &              
            if ((dw < width && dh < height) || (dw > width && dh < height)) {
                scale = Math.min(width * 1.0f / dw, height * 1.0f / dh);//                
            }
            if (dw < width && dh > height) {//           ,           
                scale = height * 1.0f / dh;//        
            }
            mBaseScale = scale;
            mMaxScale = mBaseScale * 4;
            //                
            float dx = width / 2 - dw / 2;
            float dy = height / 2 - dh / 2;
            mImageMatrix.postTranslate(dx, dy);
            mImageMatrix.postScale(mBaseScale, mBaseScale, width / 2,
                    height / 2);
            setImageMatrix(mImageMatrix);
        }
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        mScaleGestureDetector.onTouchEvent(event);
        return true;//   return true  
    }

    //  view           
    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        //  OnGlobalLayoutListener
        getViewTreeObserver().addOnGlobalLayoutListener(this);
    }

    //  view          
    @SuppressWarnings("deprecation")
    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        //  OnGlobalLayoutListener
        getViewTreeObserver().removeGlobalOnLayoutListener(this);
    }

    //         
    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        //                    
        float scaleFactor = detector.getScaleFactor();
        float scale = getScale();
        if (null == getDrawable()) {
            return true;
        }
        //          
        if ((scale < mMaxScale && scaleFactor > 1.0f)
                || (scale > mBaseScale && scaleFactor < 1.0f)) {
            if (scale * scaleFactor < mBaseScale) {
                scaleFactor = mBaseScale / scale;
            }
            if (scale * scaleFactor > mMaxScale) {
                scaleFactor = mMaxScale / scale;
            }
            //            
            // mScaleMatrix.postScale(scaleFactor, scaleFactor, getWidth() / 2,
            // getHeight() / 2);
            //            
            mImageMatrix.postScale(scaleFactor, scaleFactor,
                    detector.getFocusX(), detector.getFocusY());
            borderAndCenterCheck();
            setImageMatrix(mImageMatrix);
        }

        return false;
    }

    //         

    @Override
    public boolean onScaleBegin(ScaleGestureDetector detector) {
        //      true    onScale()    
        return true;
    }

    //       
    @Override
    public void onScaleEnd(ScaleGestureDetector detector) {
        // TODO Auto-generated method stub

    }

    private float getScale() {
        float[] values = new float[9];
        mImageMatrix.getValues(values);
        return values[Matrix.MSCALE_X];
    }

    /** *              */
    private void borderAndCenterCheck() {
        RectF rect = getMatrixRectF();
        float deltaX = 0;
        float deltaY = 0;
        int width = getWidth();
        int height = getHeight();
        //          ,      
        if (rect.width() >= width) {
            if (rect.left > 0) {
                deltaX = -rect.left;
            }
            if (rect.right < width) {
                deltaX = width - rect.right;
            }
        }
        if (rect.height() >= height) {
            if (rect.top > 0) {
                deltaY = -rect.top;
            }
            if (rect.bottom < height) {
                deltaY = height - rect.bottom;
            }
        }
        //                  ;     
        if (rect.width() < width) {
            deltaX = width / 2f - rect.right + rect.width() / 2f;

        }
        if (rect.height() < height) {
            deltaY = height / 2f - rect.bottom + rect.height() / 2f;
        }
        mImageMatrix.postTranslate(deltaX, deltaY);
    }

    /** *                * * @return */
    private RectF getMatrixRectF() {
        Matrix matrix = mImageMatrix;
        RectF rectF = new RectF();
        Drawable d = getDrawable();
        if (d != null) {
            rectF.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
            matrix.mapRect(rectF);
        }
        return rectF;
    }
}

レイアウトxmlで直接使用
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" >

    <com.ldm.view.ScaleImageView  android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="matrix" android:src="@drawable/zoom_test" />

</LinearLayout>