반응형

안드로이드를 이용하여 Application을 개발하다보면 좌표에 관한 문제를 많이 접하게 될 것이다.

- View의 상대좌표에 대한 생각을 못하는 경우

- 상대좌표임을 알고있음에도 불구하고 계산하는데 있어 뭔가 잘 되지 않는 경우

 

 

View의 절대좌표, 상대좌표

절대좌표는 것은 디바이스의 좌측 상단이 0,0으로 시작하는 좌표값이다.

상대좌표는 디바이스 화면이 기준이 아닌 현재 View의 가장 좌측 상단이 0,0으로 시작하는 좌표값이다.

 

절대 좌표 : 디바이스 화면의 최상단, 최좌측 모서리 부분이 (0,0)

상대 좌표 : 자식 View의 최상단, 최좌측 모서리 부분이 (0,0)

 

 

 

위의 내용을 통해 각 뷰는 서로 자신의 상대 좌표를 가지고 있음 알 수 있다.

 

 

 

View를 움직일 때의 좌표 계산

 

View를 움직일 때 상대좌표를 제대로 고려하지 않으면 정상적인 좌표 이동이 되지 않는다.

 

공통적으로 사용되는 xml은 다음과 같다.

 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/image_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/rect" />

</LinearLayout>

 

위의 코드에서 src에 해당하는 png파일은 다음과 같다.

 

 

이제 상대좌표를 이해하기 위해 아래의 코드를 보자

package com.example.myhandler;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;

public class MainActivity extends AppCompatActivity {
    final static String TAG = "CROCUS";

    ImageView imageView;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        imageView = findViewById(R.id.image_view);

        imageView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch(event.getAction()){
                    case MotionEvent.ACTION_DOWN:
                        break;

                    case MotionEvent.ACTION_MOVE:
                        v.setX(event.getX());
                        v.setY(event.getY());
                        break;

                    case MotionEvent.ACTION_CANCEL:
                    case MotionEvent.ACTION_UP:
                        break;
                }
                return true;
            }
        });
    }
}

 

ACTION_MOVE를 보면 움직일 때 event.getX(), event.getY() 즉, 현재 move했는 좌표 위치를 그대로 v.setX, v.setY하고자 한다.

 

하지만 위의 코드를 이용하면 상대좌표의 개념을 놓치고 있는 순간이 된다.

 

event.getX와 event.getY는 현재 View(여기서는 사각형, ImageView)의 좌표를 나타내고있기에 움직이는 순간순간마다 절대 좌표가 아닌 상대 좌표로 움직이게 된다.

 

따라서 정말 미세하게 움직여보면 알 수 있듯이 setX, setY에 의해 내가 터치한 좌표의 상대좌표로 이동하는 것이 아닌 절대좌표로 이동하게 된다.

 

즉, 10,10을 움직이고자 하면 현재 좌표에서 + 10, 10을 해주어야하지만 이대로 10,10을 보내주면 절대 좌표의 10,10으로 이동하게 된다.

 

따라서 아래 코드를 통해 상대 좌표의 개념을 추가하여야 한다.

 

 

package com.example.myhandler;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;

public class MainActivity extends AppCompatActivity {
    final static String TAG = "CROCUS";

    ImageView imageView;

    float prevX, prevY;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        imageView = findViewById(R.id.image_view);

        imageView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch(event.getAction()){
                    case MotionEvent.ACTION_DOWN:
                        prevX = event.getX();
                        prevY = event.getY();
                        break;

                    case MotionEvent.ACTION_MOVE:
                        float dx = event.getX() - prevX;
                        float dy = event.getY() - prevY;
                        Log.v(TAG, "dx : " + dx + " dy :: " + dy);
                        v.setX(v.getX() + dx);
                        v.setY(v.getY() + dy);
                        break;

                    case MotionEvent.ACTION_CANCEL:
                    case MotionEvent.ACTION_UP:
                        break;
                }
                return true;
            }
        });
    }
}

ACTION_DOWN에서 현재 내가 터치한 좌표를 prevX, prevY에서 가지게 된다.

 

그 후, 움직이면 dx, dy에 의해 얼마만큼 이동했는지에 대한 값이 갱신되고

 

setX, setY는 기존의 getX, getY에서 dx, dy만큼 더 이동한 상대좌표 값으로 이동하게 된다.

 

즉, 내가 가지고 있던 원래의 view의 위치 + 이동한 거리(d)를 이용하여야만 정확한 계산이 가능해진다.

반응형