대략적인 개념이기 때문에 실제 사용할 수 있는 나침반을 만들려면 다른 변수들도 반영니 되야 겠지만 가속도 센서(TYPE_ACCELEROMETER)와 자기장 센서(TYPE_MAGNETIC_FIELD)를 이용하여 단순한compass를 만들어보겠습니다. 구현 방법은 구글에서 이미 함수를 만들어서 제공을 하기 때문에 그걸 그대로 사용합니다. 사용되는 함수는 getRotationMatrix()와 getOrientation() 입니다.
1. 함수
1.1 getRotationMatrix
public static boolean getRotationMatrix (float[] R, float[] I, float[] gravity, float[] geomagnetic) |
Type |
Parameter |
설명 |
float[9] |
R |
회전 매트릭스 (Rotation matrix) |
float[9] |
I |
경사도 매트릭스 (Inclination matrix) |
float[3] |
gravity |
장치 좌표계의 gravity vector(TYPE_ACCELEROMETER) |
float[3] |
geomagnetic |
장치 좌표계의 geomagnetic vector(TYPE_MAGNETIC_FIELD) |
getRotationMatrix()는 경사도와 회전 매트릭스를 구하는 함수로 지구에 대한 세계 좌표계(World Coordinate System)를 기준으로 핸드폰 장치의 좌표계의 변화하는 값을 구합니다. 세계 좌표계에서 서로 직교하는 3개의 축은
x : 지표면에 접하는 동쪽 방향의 벡터
y : 지표면에 접하는 북극 방향의 벡터
z : 지표면과 직각을 이루는 상공을 향하는 벡터
으로 설명 할 수 있습니다. 구글에서 설명하는 그림을 참고하면 아래와 같습니다.
나침반을 만들 때에는 경사도 매트릭스를 사용하지 않습니다.
2.2 getOrientation
getOrientation() 함수는 rotation matrix를 이용하여 장치의 방향을 구하는 함수입니다. getRotationMatrix() 함수를 통해 구한 첫 번째 값인 회전 매트릭스를 사용해서 방향을 구할 수 있습니다.
public static float[] getOrientation (float[] R, float[] values) |
Parameter |
설명 |
R |
otation matrix : getRotationMatrix(float[], float[], float[], float[]) |
values |
float[3] : values[0]: Azimuth. z축에 대한 회전 방위각으로 이게 필요하죠. values[1]: Pitch. x축에 대한 회전 방위각 values[2]: Roll. y축에 대한 회전 방위각 |
설명에도 기술 하였지만 getRotationMatrix()에서 회전 매트릭스 (Rotation matrix)를 구하여 getOrientation()에 입력하면 values[0] (Azimuth : z축에 대한 회전 방위각)을 구하게 됩니다. 이 값이 바로 정 북을 향하는 각도가 됩니다.
2. 프로젝트 생성
CompassTest 라는 이름으로 프로젝트를 생성 합니다.
Activity는 empty로 선택합니다.
나머지는 그냥 기본 설정 값으로 놓고 Next 버튼을 클릭 합니다.
3. 코딩
3.1 layout
구해진 회전각을 출력 할 수 있는 TextView를 하나 생성하고 나침반 이미지를 위해서 ImageView를 추가 합니다.
<TextView android:id="@+id/DegreeTV" android:layout_width="wrap_content" android:layout_height="18dp" android:layout_marginBottom="8dp" android:layout_marginTop="8dp" android:text="Heading : 0.0" app:layout_constraintBottom_toTopOf="@+id/pointer" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.027" tools:layout_editor_absoluteX="127dp" /> <ImageView android:id="@+id/pointer" android:layout_width="match_parent" android:layout_height="437dp" android:src="@drawable/compass_icon" tools:layout_editor_absoluteX="0dp" tools:layout_editor_absoluteY="56dp" />
이미지는 인터넷에서 무료 이미지를 하나 줏어왔습니다. 배경이 투명이 아니라 돌아갈때 커다란네모가 돌면서 글씨를 가리는 군요. 전에 작업한 투명이미지 만들기를 보면서 수정했습니다.
[Pixlr] Adobe Photoshop 없이 무료 투명 배경 이미지 만들기
3.2 방위각 구하기
getRotationMatrix() 함수를 이용하여 회전 매트릭스(mR)를 구합니다.
SensorManager.getRotationMatrix(mR, null, mLastAccelerometer, mLastMagnetometer);
mR을 이용하여 getOrientation()에서 방위각을 구합니다.
SensorManager.getOrientation(mR, mOrientation);
3.3 이미지 회전
z 축의 회전각으로 RotateAnimation()을 이용하여 이미지를 회전 시킵니다.
RotateAnimation ra = new RotateAnimation(
mCurrentDegree,
-azimuthinDegress,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f
);
이미지 회전 설명은 [Android] 안드로이드 RotateAnimation 함수 이용 이미지 회전 을 참조 하세요.
3. 결과
4. 전체 소스
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <TextView android:id="@+id/DegreeTV" android:layout_width="wrap_content" android:layout_height="18dp" android:layout_marginBottom="8dp" android:layout_marginTop="8dp" android:text="Heading : 0.0" app:layout_constraintBottom_toTopOf="@+id/pointer" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.027" tools:layout_editor_absoluteX="127dp" /> <ImageView android:id="@+id/pointer" android:layout_width="match_parent" android:layout_height="437dp" android:src="@drawable/compass_icon" tools:layout_editor_absoluteX="0dp" tools:layout_editor_absoluteY="56dp" /> </android.support.constraint.ConstraintLayout>
MainActivity.java
package copycoding.tistory.com.compasstest; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.animation.Animation; import android.view.animation.RotateAnimation; import android.widget.ImageView; import android.widget.TextView; public class MainActivity extends AppCompatActivity implements SensorEventListener { private ImageView mPointer; private SensorManager mSensorManager; private Sensor mAccelerometer; private Sensor mMagnetometer; private float[] mLastAccelerometer = new float[3]; private float[] mLastMagnetometer = new float[3]; private boolean mLastAccelerometerSet = false; private boolean mLastMagnetometerSet = false; private float[] mR = new float[9]; private float[] mOrientation = new float[3]; private float mCurrentDegree = 0f; TextView DegreeTV; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE); mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); mMagnetometer = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD); mPointer = (ImageView)findViewById(R.id.pointer); DegreeTV = (TextView)findViewById(R.id.DegreeTV); } @Override protected void onResume() { super.onResume(); mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME); mSensorManager.registerListener(this, mMagnetometer, SensorManager.SENSOR_DELAY_GAME); } @Override protected void onPause() { super.onPause(); mSensorManager.unregisterListener(this, mAccelerometer); mSensorManager.unregisterListener(this, mMagnetometer); } @Override public void onSensorChanged(SensorEvent event) { if(event.sensor == mAccelerometer) { System.arraycopy(event.values, 0, mLastAccelerometer, 0, event.values.length); mLastAccelerometerSet = true; } else if(event.sensor == mMagnetometer) { System.arraycopy(event.values, 0, mLastMagnetometer, 0, event.values.length); mLastMagnetometerSet = true; } if(mLastAccelerometerSet && mLastMagnetometerSet) { SensorManager.getRotationMatrix(mR, null, mLastAccelerometer, mLastMagnetometer); float azimuthinDegress = (int) ( Math.toDegrees( SensorManager.getOrientation( mR, mOrientation)[0] ) + 360 ) % 360; DegreeTV.setText("Heading : " + Float.toString(azimuthinDegress) + " degrees"); RotateAnimation ra = new RotateAnimation( mCurrentDegree, -azimuthinDegress, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f ); ra.setDuration(250); ra.setFillAfter(true); mPointer.startAnimation(ra); mCurrentDegree = -azimuthinDegress; } } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { } }
'Android' 카테고리의 다른 글
android client와 eclipse server socket 예제 (0) | 2019.10.06 |
---|---|
안드로이드 WebView 이용 웹 페이지 불러오기 (2) | 2019.04.17 |
[Android] 안드로이드 RotateAnimation 함수 이용 이미지 회전 (0) | 2019.02.21 |
[안드로이드] Google Map에 현재 위치 표시하기(Google 제공 소스) (11) | 2019.01.27 |
[안드로이드] 안드로이드 데이터 저장 SharedPreferences 사용방법 (0) | 2019.01.24 |