본문 바로가기

Study/Android

[Android] Zoom Layout

A 폰에서 특정 영역에 대해 zoom in/out 을 한 부분을 B 폰에서 특정 영역 zoom in/out 한 부분을 보여주고자 할 때 넘길 좌표값 구하기

이것때문에 개고생을 했지만..
그래도 할 만 했다.


기존 소스 편집한 거라 안 맞는 부분 있을 수 있음.

 

 

ZoomLayout.java

public class ZoomLayout extends View {

    private static final String TAG = "zoom";

    private Context mContext;
    private OnTouchResultListener mListener;
    private ZoomLayoutTouchListener mZoomLayoutTouchListener;
    private ScaleGestureDetector mScaleDetector;
    private ScaleListener mScaleListener;
    private FrameLayout mParent;

    // 사각형 영역의 스케일 값을 보관한다
    private float mZoomScale = 1.0f;
    private static final float MIN_ZOOM = 1.0f;
    private static final float MAX_ZOOM = 5.0f;


    private boolean mIsScaling = false;
    private boolean mIsUp = false;

    // 라인 속성
    private Paint mPaint;

    // FRAME LAYOUT 의 사이즈를 저장
    private RectF mFrameRect = null;
    // 화면에 그려지는 사각형
    private RectF mDrawRect = new RectF();

    // 사각형의 중심 좌표값을 갖는다(0~1)
    private PointF mZoomCenter = new PointF(0.5f, 0.5f);


    public ZoomLayout(Context context, FrameLayout parent, OnTouchResultListener listener) {
        super(context);

        this.mContext = context;
        this.mParent = parent;
        this.mListener = listener;

        // 프레임 RECT
        mFrameRect = new RectF(0, 0, parent.getWidth(), parent.getHeight());

        mPaint = new Paint();
        mPaint.setColor(Color.WHITE);
        mPaint.setStrokeWidth(10);
        mPaint.setStyle(Paint.Style.STROKE);

        mZoomLayoutTouchListener = new ZoomLayoutTouchListener();
        mParent.setOnTouchListener(mZoomLayoutTouchListener);
        mScaleListener = new ScaleListener();
        mScaleDetector = new ScaleGestureDetector(mContext, mScaleListener);

        // 최초 사각형 영역 계산한기
        RecalculateLocScl();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        // 변경된 프레임의 사이즈를 저장
        mFrameRect = new RectF(0, 0, w, h);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawRect(mDrawRect, mPaint);
    }


    // X, Y, Scale 값을 다시 계산함
    private void RecalculateLocScl()
    {
        // 사각형의 가로, 세로 사이즈
        float xWidth = mFrameRect.width() * 1.0f / mZoomScale;
        float xHeight = mFrameRect.height() * 1.0f / mZoomScale;

        float LTx = (mZoomCenter.x * mFrameRect.width()) - xWidth/2;
        float LTy = (mZoomCenter.y * mFrameRect.height()) - xHeight/2;

        float RBx = LTx + xWidth;
        float RBy = LTy + xHeight;

        mDrawRect.set(LTx, LTy, RBx, RBy);

        mListener.onResult(mZoomCenter.x, mZoomCenter.y, mZoomScale);

        // 화면 다시 그리기
        invalidate();
    }

    private void setLocation(float x, float y)
    {
        float tmpX = x;
        float tmpY = y;

        // 터치 영역이 FrameLayout 밖을 넘지 않도록 함.
        if(tmpX < 0)
            tmpX = 0;
        if(tmpX > (mFrameRect.width() - 1))
            tmpX = mFrameRect.width() -1;

        if(tmpY < 0)
            tmpY = 0;
        if(tmpY > (mFrameRect.height() - 1))
            tmpY = mFrameRect.height() -1;


        float xWidth = (mFrameRect.width() * 1.0f / mZoomScale);
        float xHeight = (mFrameRect.height() * 1.0f / mZoomScale);


        // X, Y 최소/최대 범위 지정
        if((tmpX - (xWidth/2)) < 0)
            tmpX = (xWidth/2);
        else if((tmpX + (xWidth/2)) >= mFrameRect.width())
            tmpX = mFrameRect.width()-(xWidth/2);

        if((tmpY - (xHeight/2)) < 0)
            tmpY = (xHeight/2);
        else if((tmpY + (xHeight/2)) >= mFrameRect.height())
            tmpY = mFrameRect.height()-(xHeight/2);


        mZoomCenter.x = tmpX / mFrameRect.width();
        mZoomCenter.y = tmpY / mFrameRect.height();


        RecalculateLocScl();
    }

    private void setScale(float scaleFactor)
    {
        mZoomScale /= scaleFactor;
//      mZoomScale *= scaleFactor; // 반대로 그리기
        if(mZoomScale <= MIN_ZOOM) mZoomScale = MIN_ZOOM;
        if(mZoomScale >= MAX_ZOOM) mZoomScale = MAX_ZOOM;

        RecalculateLocScl();
    }

    public interface OnTouchResultListener {
        void onResult(float zoomX, float zoomY, float scale);
    }
}

 

 

ScaleListener.java

    public class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {

        @Override
        public boolean onScaleBegin(ScaleGestureDetector detector) {
            mIsScaling = true;
            return true;
        }

        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            float scaleFactor = detector.getScaleFactor();
            setScale(scaleFactor);
            return true;
        }

        @Override
        public void onScaleEnd(ScaleGestureDetector detector) {
            mIsScaling = false;
        }
    }

 

 

ZoomLayoutTouchListener.java

    public class ZoomLayoutTouchListener implements OnTouchListener {
        float _begin_x, _begin_y;
        float _move_x, _move_y;

        public boolean isDrag(float x, float y)
        {
            float xmove = 0.0f;
            if(x > _begin_x)
                xmove = x - _begin_x;
            else
                xmove = _begin_x - x;

            float ymove = 0.0f;
            if(y > _begin_y)
                ymove = y - _begin_y;
            else
                ymove = _begin_y - y;

            double movexy = Math.sqrt(Math.pow(xmove, 2) + Math.pow(ymove, 2));

            if(movexy > 10)
                return true;

            return false;
        }

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            switch (event.getAction() & MotionEvent.ACTION_MASK) {
                case MotionEvent.ACTION_DOWN:
                    mIsUp = false;
                    if(mIsScaling == false) {
                        _begin_x = event.getX();
                        _begin_y = event.getY();
                    }
                    break;

                case MotionEvent.ACTION_POINTER_DOWN:
                    mIsUp = false;
                    break;

                case MotionEvent.ACTION_MOVE:
                    if(mIsScaling == false && isDrag(event.getX(), event.getY()) 
                    	== true && mIsUp == false) {
                        setLocation(event.getX(), event.getY());
                        _move_x = event.getX();
                        _move_y = event.getY();
                    }

                    break;

                case MotionEvent.ACTION_UP:
                    break;

                case MotionEvent.ACTION_POINTER_UP:
                    mIsUp = true;
                    break;
            }

            mScaleDetector.onTouchEvent(event);

            return true;
        }
    }