[ZoomInOutImageView.java]

public class ZoomInOutImageView extends androidx.appcompat.widget.AppCompatImageView {
    private final Matrix mMatrix = new Matrix();
    
    private final int WIDTH = 0;
    private final int HEIGHT = 1;

    private static final float MIN_ZOOM = 0.33333F;
    private static final float MAX_ZOOM = 5F;

    private PointF mLastTouch = new PointF(0, 0);
    private PointF mLastFocus = new PointF(0, 0);

    public float mScaleFactor = 1F;
    private int mActivePointerId = INVALID_POINTER_ID;

    private Paint mPaint;
    private Bitmap mBitmapLayout;

    private float mMaxScale = 2.0f;	//2배 까지 확대

    private boolean mIsInit = false;

    //    private OnFactorChangedListener mListener;
    private ScaleGestureDetector mScaleDetector;

    public ZoomInOutImageView(Context context) {
        super(context);
        initializeInConstructor(context);
    }

    public ZoomInOutImageView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        initializeInConstructor(context);
    }

    public ZoomInOutImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initializeInConstructor(context);
    }

    public void initializeInConstructor(Context context) {
        mPaint = new Paint();

        mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
        mScaleDetector.setQuickScaleEnabled(false);

        setScaleType(ScaleType.MATRIX);

    }

    public void showImage(Uri imageUri, float maxScale) {
        this.setImageURI(imageUri);
        BitmapDrawable drawble = (BitmapDrawable) getDrawable();
        mBitmapLayout = drawble.getBitmap();
        this.mMaxScale = maxScale;
    }

    public void showImage(Bitmap bitmap, float maxScale) {
        this.setImageBitmap(bitmap);
        BitmapDrawable drawble = (BitmapDrawable) getDrawable();
        mBitmapLayout = drawble.getBitmap();
        this.mMaxScale = maxScale;
    }

    private float mLastScaleFactor = 1.0f;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        mScaleDetector.onTouchEvent(event);

        int action = event.getActionMasked();

        switch (action) {
            case MotionEvent.ACTION_DOWN: {

                int pointerIndex = event.getActionIndex();
                float x = event.getX(pointerIndex);
                float y = event.getY(pointerIndex);

                // Remember where we started (for dragging)
                mLastTouch = new PointF(x, y);

                // Save the ID of this pointer (for dragging)
                mActivePointerId = event.getPointerId(0);

            }

            case MotionEvent.ACTION_POINTER_DOWN: {
                if (event.getPointerCount() == 2) {
                    mLastFocus = new PointF(mScaleDetector.getFocusX(), mScaleDetector.getFocusY());
                }
            }

            case MotionEvent.ACTION_MOVE: {
                // Find the index of the active pointer and fetch its position
                int pointerIndex = event.findPointerIndex(mActivePointerId);

                float x = event.getX(pointerIndex);
                float y = event.getY(pointerIndex);

                // Calculate the distance moved
                float dx = 0;
                float dy = 0;

                if (event.getPointerCount() == 1) {


                    // Calculate the distance moved
                    dx = x - mLastTouch.x;
                    dy = y - mLastTouch.y;

                    Log.d("onTouchEvent - 1",  String.valueOf(x) + " / " + String.valueOf(y) + " / " +String.valueOf(dx) + " / " + String.valueOf(dy) );

                    // Remember this touch position for the next move event
                    mLastTouch = new PointF(x, y);

                    // 1:1 스케일의 경우 이미지 이동 되지 않게 고정
                    if (mScaleFactor <= 1.0f) {
                        imageScaleSetup();
                    }

                } else if (event.getPointerCount() == 2) {
                    // Calculate the distance moved
                    float focusX = mScaleDetector.getFocusX();
                    float focusY = mScaleDetector.getFocusY();
                    dx = focusX - mLastFocus.x;
                    dy = focusY - mLastFocus.y;

                    Log.d("onTouchEvent - 2", String.valueOf(focusX) + " / " + String.valueOf(focusY) + " / " + String.valueOf(dx) + " / " + String.valueOf(dy) );

                    // Since we are accumating translation/scaling, we are just adding to
                    // the previous scale.
                    mMatrix.postScale(mScaleFactor / mLastScaleFactor, mScaleFactor / mLastScaleFactor, focusX, focusY);
                    mLastScaleFactor = mScaleFactor;

                    mLastFocus = new PointF(focusX, focusY);

                }

                // Translation is cumulative.
                mMatrix.postTranslate(dx, dy);
                break;
            }

            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL: {
                mActivePointerId = INVALID_POINTER_ID;
                break;
            }

            case MotionEvent.ACTION_POINTER_UP: {

                final int pointerIndex = event.getActionIndex();
                final int pointerId = event.getPointerId(pointerIndex);

                if (pointerId == mActivePointerId) {
                    // This was our active pointer going up. Choose a new
                    // active pointer and adjust accordingly.
                    final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
                    mLastTouch = new PointF(event.getX(newPointerIndex), event.getY(newPointerIndex));
                    mActivePointerId = event.getPointerId(newPointerIndex);
                } else {
                    final int tempPointerIndex = event.findPointerIndex(mActivePointerId);
                    mLastTouch = new PointF(event.getX(tempPointerIndex), event.getY(tempPointerIndex));
                }

                break;
            }
        }
        invalidate();
        return true;
    }

    @Override
    protected void onDraw(Canvas canvas) {

        if (mBitmapLayout == null) { return; }

        if (mIsInit == false) {
            mIsInit = true;
            imageScaleSetup();

        }

        canvas.save();
        canvas.setMatrix(mMatrix);
        canvas.drawColor(Color.BLACK);
        canvas.drawBitmap(mBitmapLayout,0,0, mPaint);
        canvas.restore();
    }

    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
        @Override
        public boolean onScale(ScaleGestureDetector detector) {

            mScaleFactor *= detector.getScaleFactor();

            // 줌 In Out 범위 제한
            if (mScaleFactor < 1.0f) {
                mScaleFactor = 1.0f;
                imageScaleSetup();
                return true;
            } else if (mScaleFactor > mMaxScale) {
                mScaleFactor = mMaxScale;
                return true;
            }

            mScaleFactor = Math.max(MIN_ZOOM, Math.min(mScaleFactor, MAX_ZOOM));
            Log.d("onScale", String.valueOf(mScaleFactor));
            return true;
        }
    }

    /**
     * 이미지를 스크린 사이즈에 맞게 맞추고 중앙 정렬
     */
    private void imageScaleSetup() {
        float[] value = new float[9];
        this.mMatrix.getValues(value);

        // 뷰 사이즈
        int screenWidth = this.getWidth();
        int screenHeight = this.getHeight();

        // 이미지 사이즈
        int imageWidth = mBitmapLayout.getWidth();
        int imageHeight = mBitmapLayout.getHeight();
        int scaleWidth = (int) (imageWidth * value[0]);
        int scaleHeight = (int) (imageHeight * value[4]);

        // 이미지가 밖으로 벗어나지 못하게 함.
        value[2] = 0;
        value[5] = 0;

        // 이미지를 스크린 사이즈에 맞게 사이즈 조절
        int target = WIDTH;
        if (imageWidth < imageHeight) {
            target = HEIGHT;
        }

        if (target == WIDTH) {
            value[0] = value[4] = (float) screenWidth / imageWidth;
        }

        if (target == HEIGHT) {
            value[0] = value[4] = (float) screenHeight / imageHeight;
        }

        scaleWidth = (int) (imageWidth * value[0]);
        scaleHeight = (int) (imageHeight * value[4]);

        if (scaleWidth > screenWidth) {
            value[0] = value[4] = (float) screenWidth / imageWidth;
        }

        if (scaleHeight > screenHeight) {
            value[0] = value[4] = (float) screenHeight / imageHeight;
        }

        // 가운데 위치하도록
        scaleWidth = (int) (imageWidth * value[0]);
        scaleHeight = (int) (imageHeight * value[4]);

        if (scaleWidth < screenWidth) {
            value[2] = (float) screenWidth / 2 - (float) scaleWidth / 2;
        }

        if (scaleHeight < screenHeight) {
            value[5] = (float) screenHeight / 2 - (float) scaleHeight / 2;
        }

        mMatrix.setValues(value);
    }
}

 

 

[activity_preview.xml]

<!--    이미지 레이어-->
<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"

    android:layout_below="@+id/top_layer"
    android:layout_marginBottom="92dp">

    <kr.kfcc.insurance.CustomUI.ZoomInOutImageView
        android:id="@+id/iv_canvas"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="fitCenter"
        android:layout_gravity="center" />
</FrameLayout>

 

[PreviewActivity.java]

public class PreviewActivity extends AppCompatActivity implements View.OnClickListener {
	private ZoomInOutImageView mImageView;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_preview);
        
        mImageView = (ZoomInOutImageView) findViewById(R.id.iv_canvas);
        
        Bitmap curImageBitmap = ImageBucket.getInstance().getImage(imageSize - 1);

        // 이미지 표시
        if (curImageBitmap != null) {
            try {
                mImageView.post(new Runnable() {
                    @Override
                    public void run() {
                        mImageView.showImage(curImageBitmap, 3.0f);
                    }
                });

            } catch (IllegalArgumentException e) {
                Log.e("Exception", e.getMessage());
            }
        }
    }
}

+ Recent posts