티스토리 뷰



이 자료들은 팁스소프트에서 제공하는 [ 알짜배기 ] 프로그램을 이용하면 더 편리하게 볼수 있습니다.
* 알짜배기 프로그램 받기 - http://www.tipssoft.com/bulletin/tb.php/QnA/8406
* 관리자의 Tipssoft 이야기를 들어보세요 ( 트위터 ID : tipssoft )
* 안드로이드 강좌 목록 - http://www.tipssoft.com/bulletin/tb.php/old_bbs/501

안드로이드용 어플리케이션 중에서는 단순히 터치만을 사용자 조작으로 사용하지 않고, 기기의 방향을
체크하여 해당 방향으로 어떤 조작이 발생하도록 제공하는 것들이 있습니다. 대표적인 예로 기기의 평면
기울기를 이용하여 벌레를 이동시키는 게임이나 자동차의 핸들을 움직이듯이 가로로 누운 기기를 잡고
돌리면서 자동차 경주를 하는 게임 등이 있습니다.
이번 강좌에서는 기기의 현재 방향 및 기울기가 어떻게 되는지 방향 센서를 이용하여 얻는 방법에 대해
알아보도록 하겠습니다. 센서를 사용할 때 기본적으로 사용되는 공통 루틴에 대하여 잘 모르시는 분들은
아래에 링크된 자료를 먼저 참고하여 주시기 바랍니다.
센서를 사용하기 위한 공통 루틴 : http://www.tipssoft.com/bulletin/tb.php/FAQ/1033
1. 센서 측정값에 대하여
방향 센서가 측정하는 데이터는 총 3가지로 Azimuth(방위), pitch(경사도), roll(좌우 회전) 입니다.
1.1 Azimuth ( 방위 )
방위는 기기를 수평으로 두었을 때 기기의 머리부분이 어느 방향을 가리키고 있는지 수치값으로
나타 냅니다. 데이터의 범위가 0 ~ 359 일 때 각 방향에 따른 데이터 값은 아래의 그림처럼 북쪽부터
시작하여 시계방향으로 90 씩 더한 값입니다.
1.2 pitch ( 경사도 )
경사도는 기기의 수직 기울기를 의미합니다. 경사도는 아래의 그림처럼 기기의 머리부분과
아래부분이 수평을 이룰 때 0 값을 가지며 머리부분의 높이가 높아지면 수치값이 점점 감소하며
머리부분의 높이가 낮아지면 수치값이 점점 증가합니다.
위 그림은 방향 센서를 측정하는 두가지 방법 중에서 getOrientation 메소드를 이용했을 때 얻어진
값의 경우에만 적용됩니다. 경사도 값은 자동차 경주게임에서 기기의 움직임을 핸들의 움직임처럼
적용해야 할 때 사용하면 좋을 것입니다.
1.3 roll ( 좌우 회전 )
좌우 회전은 기기의 수평 기울기를 의미합니다. 이 값은 기기의 화면이 하늘을 향하고 있을 때
기기의 좌, 우 부분이 수평을 이면 0 값을 가지며 기기의 좌측 위치가 높아지면 수치값이 점점
증가하며 우측 위치가 높아지면 수치값이 점점 감소합니다. 각 위치에 따른 값의 변화는 아래의
그림과 같습니다.
위 그림을 보면 알 수 있듯이 경사도와는 달리 화면이 하늘을 보고 있는지 땅을 보고 있는지에 따라
동일한 기울기에도 다른 값을 가집니다. 위 그림이 설명하는 위치값은 방향 센서를 측정하는
두가지 방법 중에서 getOrientation 메소드를 이용한 경우에만 적용됩니다.
2. 기능 구현하기
방향 센서는 다른 센서들을 사용할 때처럼 getDefaultSensor 메소드에 센서의 타입을 명시하여 얻을
수 있고, 해당 객체를 리스너에 등록하여 센서 값을 사용할 수 있습니다. 그러나 현재 안드로이드
시스템에서는 가속 센서와 자기장 센서의 데이터를 얻어서 SensorManager.getOrientation 메소드로
방향 센서값을 직접 계산하는 것을 권장하고 있기때문에 기존의 방법은 하위호환성을 유지하기 위해
서만 제공하고 있습니다.
이제부터 두가지 방법을 이용하여 방향 데이터를 얻는 방법에 대하여 알아보도록 하겠습니다.
2.1 방향 센서가 주는 데이터 직접 사용하기
센서에 관련된 이전 강좌들과 동일한 방식으로 데이터를 얻습니다. 이 방식으로 얻은 데이터는
완전한 결과값이기때문에 데이터의 좌표 기준을 변경하거나 가공할 수 없다는 단점이 있습니다.
public class OldVersionOtActivity extends Activity implements SensorEventListener
{
// 센서 관련 객체
SensorManager m_sensor_manager;
Sensor m_ot_sensor;

int m_check_count = 0;
// 출력용 텍스트뷰
TextView m_check_view;
TextView m_result_view[] = new TextView[3];

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

// 출력용 텍스트뷰를 얻는다.
m_check_view = (TextView) findViewById(R.id.check_tv);
m_result_view[0] = (TextView) findViewById(R.id.result_tv1);
m_result_view[1] = (TextView) findViewById(R.id.result_tv2);
m_result_view[2] = (TextView) findViewById(R.id.result_tv3);

// 시스템서비스로부터 SensorManager 객체를 얻는다.
m_sensor_manager = (SensorManager)getSystemService(SENSOR_SERVICE);
// SensorManager 를 이용해서 방향 센서 객체를 얻는다.
m_ot_sensor = m_sensor_manager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
}

// 해당 액티비티가 포커스를 얻으면 방향 데이터를 얻을 수 있도록 리스너를 등록한다.
protected void onResume() {
super.onResume();
m_check_count = 0;

// 센서 값을 이 컨텍스트에서 받아볼 수 있도록 리스너를 등록한다.
m_sensor_manager.registerListener(this, m_ot_sensor, SensorManager.SENSOR_DELAY_UI);
}

// 해당 액티비티가 포커스를 잃으면 방향 데이터를 얻어도 소용이 없으므로 리스너를 해제한다.
protected void onPause() {
super.onPause();
// 센서 값이 필요하지 않는 시점에 리스너를 해제해준다.
m_sensor_manager.unregisterListener(this);
}

// 정확도 변경시 호출되는 메소드. 센서의 경우 거의 호출되지 않는다.
public void onAccuracyChanged(Sensor sensor, int accuracy)
{
}

// 측정한 값을 전달해주는 메소드.
public void onSensorChanged(SensorEvent event)
{
// 방향 센서가 전달한 데이터인 경우
if(event.sensor.getType() == Sensor.TYPE_ORIENTATION) {
String str;
// 첫번째 데이터인 방위값으로 문자열을 구성하여 텍스트뷰에 출력한다.
str = "azimuth(z) : " + (int)event.values[0];
m_result_view[0].setText(str);

// 두번째 데이터인 경사도로 문자열을 구성하여 텍스트뷰에 출력한다.
str = "pitch(x) : " + (int)event.values[1];
m_result_view[1].setText(str);

// 세번째 데이터인 좌우 회전값으로 문자열을 구성하여 텍스트뷰에 출력한다.
str = "roll(y) : " + (int)event.values[2];
m_result_view[2].setText(str);

// 함수의 출력횟수를 텍스트뷰에 출력한다.
m_check_count++;
str = "호출 횟수 : " + m_check_count + " 회";
m_check_view.setText(str);
}
}
}
2.2 가속 센서와 자기장 센서의 데이터로 방향 데이터를 계산하여 사용하기
이 방법은 가속 센서와 자기장 센서를 등록하여 해당 데이터의 결과로 회전 매트릭스를 연산하고,
회전 매트릭스로 방향 데이터를 얻는 방법입니다. 방향 데이터를 직접 연산하여 사용하기 때문에
데이터에 대한 여러가지 처리를 직접 해주어야하는 불편함이 있지만 회전 매트릭스를 가공하여
원하는 결과로 도출할 수 있다는 장점도 있습니다.
getOrientation 메소드로 얻은 데이터는 Radian 기준의 값이므로 Degree(도) 기준의 값으로 변환
하여 사용해야합니다. 또한 이 예제에서는 방위 데이터에 대한 세밀한 값 보정을 하지 않았기
때문에 기기의 화면이 하늘방향인 경우에만 정확한 방위값을 얻을 수 있습니다.
public class TestOrientationActivity extends Activity implements SensorEventListener
{
// 센서 관련 객체
SensorManager m_sensor_manager;
Sensor m_acc_sensor, m_mag_sensor;

int m_check_count = 0;
// 출력용 텍스트뷰
TextView m_check_view;
TextView m_result_view[] = new TextView[4];

// 데이터를 저장할 변수들
float[] m_acc_data = null, m_mag_data = null;
float[] m_rotation = new float[9];
float[] m_result_data = new float[3];

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

// 출력용 텍스트뷰를 얻는다.
m_check_view = (TextView) findViewById(R.id.check_tv);
m_result_view[0] = (TextView) findViewById(R.id.result_tv1);
m_result_view[1] = (TextView) findViewById(R.id.result_tv2);
m_result_view[2] = (TextView) findViewById(R.id.result_tv3);
m_result_view[3] = (TextView) findViewById(R.id.result_tv4);

// 시스템서비스로부터 SensorManager 객체를 얻는다.
m_sensor_manager = (SensorManager)getSystemService(SENSOR_SERVICE);

// SensorManager 를 이용해서 가속센서와 자기장 센서 객체를 얻는다.
m_acc_sensor = m_sensor_manager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
m_mag_sensor = m_sensor_manager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
}

// 해당 액티비티가 포커스를 얻으면 가속 데이터와 자기장 데이터를 얻을 수 있도록
// 리스너를 등록한다.
protected void onResume() {
super.onResume();
m_check_count = 0;

// 센서 값을 이 컨텍스트에서 받아볼 수 있도록 리스너를 등록한다.
m_sensor_manager.registerListener(this, m_acc_sensor, SensorManager.SENSOR_DELAY_UI);
m_sensor_manager.registerListener(this, m_mag_sensor, SensorManager.SENSOR_DELAY_UI);
}

// 해당 액티비티가 포커스를 잃으면 가속 데이터와 자기장 데이터를 얻어도 소용이 없으므로
// 리스너를 해제한다.
protected void onPause() {
super.onPause();
// 센서 값이 필요하지 않는 시점에 리스너를 해제해준다.
m_sensor_manager.unregisterListener(this);
}

// 정확도 변경시 호출되는 메소드. 센서의 경우 거의 호출되지 않는다.
public void onAccuracyChanged(Sensor sensor, int accuracy)
{
}

// 측정한 값을 전달해주는 메소드.
public void onSensorChanged(SensorEvent event)
{
if(event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
// 가속 센서가 전달한 데이터인 경우
// 수치 데이터를 복사한다.
m_acc_data = event.values.clone();
} else if(event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
// 자기장 센서가 전달한 데이터인 경우
// 수치 데이터를 복사한다.
m_mag_data = event.values.clone();
}

// 데이터가 존재하는 경우
if(m_acc_data != null && m_mag_data != null) {
// 가속 데이터와 자기장 데이터로 회전 매트릭스를 얻는다.
SensorManager.getRotationMatrix(m_rotation, null, m_acc_data, m_mag_data);
// 회전 매트릭스로 방향 데이터를 얻는다.
SensorManager.getOrientation(m_rotation, m_result_data);

String str;
// Radian 값을 Degree 값으로 변환한다.
m_result_data[0] = (float)Math.toDegrees(m_result_data[0]);

// 0 이하의 값인 경우 360을 더한다.
if(m_result_data[0] < 0) m_result_data[0] += 360;

// 첫번째 데이터인 방위값으로 문자열을 구성하여 텍스트뷰에 출력한다.
str = "azimuth(z) : " + (int)m_result_data[0];
m_result_view[0].setText(str);

// 두번째 데이터인 경사도를 Degree 로 변환한 후 문자열을 구성하여 출력한다.
str = "pitch(x) : " + (int)Math.toDegrees(m_result_data[1]);
m_result_view[1].setText(str);

// 세번째 데이터인 좌우 회전 값을 Degree 로 변환한 후 문자열을 구성하여 출력한다.
str = "roll(y) : " + (int)Math.toDegrees(m_result_data[2]);
m_result_view[2].setText(str);

// 함수의 출력횟수를 텍스트뷰에 출력한다.
m_check_count++;
str = "호출 횟수 : " + m_check_count + " 회";
m_check_view.setText(str);

}
}
}
2. 실행 화면

 

댓글