본문 바로가기

책/오준석의 안드로이드 생존코딩 코틀린편

8. 수평 측정기

프로젝트명 TiltSensor
기능
  • 기기를 기울이면 수평을 측정할 수 있다.
  • 화면이 표시되는 원이 가운데로 이동하면 수평이다.
핵심 구성요소
  • SensorManager : 센서 관리자
  • SensorEventListener : 센서 이벤트를 수신하는 리스너
  • 커스텀 뷰 : 나만의 새로운 뷰를 만드는 방법
  1. 준비하기 : 프로젝트 생성 및 안드로이드 설정
  2. 스텝1 : 액티비티의 생명주기 알아보기
  3. 스텝2 : 센서 사용하기
  4. 스텝3 : 커스텀 뷰 작성하기

스텝1 액티비티의 생명주기 알아보기

액티비티 생명주기 출처 : https://developer.android.com/guide/components/activities/activity-lifecycle

 

Activity 수명 주기에 대한 이해  |  Android 개발자  |  Android Developers

An Activity is an application component that provides a screen with which users can interact in order to do something, such as dial the phone, take a photo, send an email, or view a map. Each activity is given a window in which to draw its user interface��

developer.android.com

스텝2 센서 사용하기

센서 목록

Sensor 클래스에 정의된 센서 목록

센서 설명 용도
TYPE_ACCELEROMETER 가속도 센서 동작 감지(흔들림, 기울임 등)
TYPE_AMBIENT_TEMPERATURE 주변 온도 센서 대기 온도 모니터링
TYPE_GRAVITY 중력 센서 동작 감지( 흔들림, 기울임 등)
TYPE_GYROSCOPE 자이로 센서 회전 감지
TYPE_LIGHT 조도 센서 화면 밝기 제어
TYPE_LINEAR_ACCELERATION 선형 가속도 센서 단일 축을 따라 가속 모니터링
TYPE_MAGNETIC_FIELD 자기장 센서 나침반
TYPE_ORIENTATION 방향 센서 장치 위치 결정
TYPE_PRESSURE 기압 센서 공기압 모니터링 변화
TYPE_PROXIMITY 근접 센서 통화 중인지 검사
TYPE_RELATIVE_HUMIDITY 상대 습도 센서 이슬점, 상대 습도를 모니터링
TYPE_ROTATION_VECTOR 회전 센서 모션 감지, 회전 감지
TYPE_TEMPERATURE 온도 센서 장치의 온도 감지

센서 사용 준비

  1. SensorManager 인스턴스를 얻는다.
  2. 위 표에 있는 센서중 하나를 getDefaultSensor() 메서드에 지정하여 Sensor 객체를 얻는다.
  3. onResume() 메서드에 registerListener() 메서드로 센서의 감지를 등록한다.
  4. onPause() 메서드에서 unregisterListener() 메서드로 센서의 감지를 해제한다.
// 자신을 SensorEventListener 인터페이스의 구현체로 등록
class MainActivity : AppCompatActivity(), SensorEventListener {

    // 1. 지연된 초기화를 사용하여 sensorManager 객체를 얻는다.
    private val sensorManager by lazy {
        getSystemService(Context.SENSOR_SERVICE) as SensorManager
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }

    /* 2. registerListener로 사용할 센서를 등록한다.

       첫 번째 인자 : 센서값을 받을 SensorEventListener
       두 번째 인자 : 센서 종류 지정 getDefaultSensor
       세 번째 인자 : 센서 값을 얼마나 자주 받을지 지정
         - SENSOR_DELAY_FASTAST : 가능한 가장 자주 센서값을 얻습니다.
         - SENSOR_DELAY_GAME : 게임에 적합한 정도로 센서값을 얻습니다.
         - SENSOR_DELAY_NORMAL : 화면방향이 전환될 때 적합한 정도로 센서값을 얻습니다.
         - SENSOR_DELAY_UI : 사용자 인터페이스를 표시하기에 적합한 정도로 센서값을 얻습니다.
    */
    override fun onResume() {
        super.onResume()
        sensorManager.registerListener(
                this,
                sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
                SensorManager.SENSOR_DELAY_NORMAL
        )
    }

    // 3. unregisterListener로 센서 사용을 해제한다.
    override fun onPause() {
        super.onPause()
        sensorManager.unregisterListener(this)
    }

    // SensorEventListener 인터페이스에서 구현해야 할 메서드
    // 센서 정밀도가 변경되면 호출
    override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
        TODO("Not yet implemented")
    }

    // 센서 값이 변경 되면 호출
    override fun onSensorChanged(event: SensorEvent?) {
        TODO("Not yet implemented")
    }
}

좌표 시스템

  • 센서 프레임워크에서는 센서값을 나타내는데 x, y, z 표준 3축 좌표계를 사용한다.
    • x : 수평
    • y : 수직
    • z : 화면 바깥쪽
  • 3축 좌표계는 다음 센서에서 사용
    • 가속도 센서
    • 중력 센서
    • 자이로 스코프
    • 선형 가속도 센서
    • 자기장 센서

센서 API에서 사용하는 좌표 시스템 출처 : https://developer.android.com/guide/topics/sensors/sensors_overview

 

센서 개요  |  Android 개발자  |  Android Developers

대부분의 Android 지원 기기에는 움직임, 방향 및 다양한 환경 조건을 측정하는 센서가 내장되어 있습니다. 이러한 센서는 높은 정밀도와 정확도로 원시 데이터를 제공하며 3차원으로 모니터링하�

developer.android.com

가속도 센서값 사용하기 

// 자신을 SensorEventListener 인터페이스의 구현체로 등록
class MainActivity : AppCompatActivity(), SensorEventListener {

    // 1. 지연된 초기화를 사용하여 sensorManager 객체를 얻는다.
....
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
....
    // 2. registerListener로 사용할 센서를 등록한다.
....

    // 3. unregisterListener로 센서 사용을 해제한다.
....

    // SensorEventListener 인터페이스에서 구현해야 할 메서드
    // 센서 값이 변경 되면 호출
    override fun onSensorChanged(event: SensorEvent?) {
        /*
            SensorEvent.values[0] : x 축 값 - 위로 기울이면 -10 ~ 0, 아래로 기울이면 0 ~ 10
            SensorEvent.values[1] : y 축 값 - 왼쪽으로 기울이면 -10 ~ 0, 오른쪽으로 기울이면 0 ~ 10
            SensorEvent.values[2] : z 축 값 - 미사용
         */
        event?.let {
            Log.d("MainActivity;"
                  ,"onChanged: x: ${event.values[0]}, y: ${event.values[1]}, z: ${event.values[2]}" )
        }
    }
    
    // 센서 정밀도가 변경되면 호출
....
}

디버그용 메서드

  • Log.d( [태그] [, 메시지] )
    • 태그 : 로그캣에는 많은 내용이 표시되므로 필터링 할 때 사용
    • 메시지 : 출력할 메시지를 작성한다.
  • Log 메서드의 종류
    • Log.e : 에러를 표시할 때 사용
    • Log.w : 경고를 표시할 때 사용
    • Log.i : 정보성 로그를 표시할 때 사용
    • Log.v : 모든 로그를 표시할 때 사용

가로 고정 및 화면 꺼지지 않게 하기

// 자신을 SensorEventListener 인터페이스의 구현체로 등록
class MainActivity : AppCompatActivity(), SensorEventListener {

    // 1. 지연된 초기화를 사용하여 sensorManager 객체를 얻는다.
....
    override fun onCreate(savedInstanceState: Bundle?) {
        // 화면이 꺼지지 않게 하기
        window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
    
        // 화면이 가로모드로 고정되게 하기
        requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
        
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
....
    // 2. registerListener로 사용할 센서를 등록한다.
....

    // 3. unregisterListener로 센서 사용을 해제한다.
....

    // SensorEventListener 인터페이스에서 구현해야 할 메서드
    // 센서 값이 변경 되면 호출
....
    // 센서 정밀도가 변경되면 호출
....
}

에뮬레이터에서 센서 테스트 하기

다음 순서대로 선택하여 ④에서 마우스로 클릭한 상태에서 움직여 보며 테스트 할 수 있다.

스텝3 커스텀 뷰 작성하기

다음과 같이 TiltView를 생성하고 onDraw 메서드를 오버라이드 한다.

package abstractask.example.tiltsensor

import android.content.Context
import android.view.View

class TiltView(context: Context?) : View(context) {
    override fun onDraw(canvas: Canvas?) {
   
    }   
}

MainActivity 에서는 TiltView 를 사용할 수 있도록 다음과 같이 작성한다.

// 자신을 SensorEventListener 인터페이스의 구현체로 등록
class MainActivity : AppCompatActivity(), SensorEventListener {

    // 1. 지연된 초기화를 사용하여 sensorManager 객체를 얻는다.
....

    private lateinit var tiltView: TiltView
    
    override fun onCreate(savedInstanceState: Bundle?) {
        // 화면이 꺼지지 않게 하기
        ....
        // 화면이 가로모드로 고정되게 하기       
        ....
        
        // 커스텀 뷰 사용하기
        tiltView = TiltView(this)
        setContentView(tiltView)
    }
....
    // 2. registerListener로 사용할 센서를 등록한다.
....

    // 3. unregisterListener로 센서 사용을 해제한다.
....

    // SensorEventListener 인터페이스에서 구현해야 할 메서드
    // 센서 값이 변경 되면 호출
....
    // 센서 정밀도가 변경되면 호출
....
}

그래픽 API를 다루는 기초

  • Canvas : 도화지 (뷰의 표면)
    • drawCircle(cx : Float, cy: Float, radius: Float, paint: Paint! )
      • cx : X좌표
      • cy : Y좌표
      • radius : 반지름
      • paint : Paint객체
    • drawLine(startX: Float, startY: Float, stopX: Float, stopY: Float, paint: Paint!)
      • startX : 한 점의 X좌표
      • startY : 한 점의 Y좌표
      • stopX : 다른 점의 X좌표
      • stopY : 다른 점의 Y좌표
      • paint : Paint 객체
  • Paint : 붓 (색, 굵기, 스타일 정의)
class TiltView(context: Context?) : View(context) {
    
    private val greenPaint: Paint = Paint()
    private val blackPaint: Paint = Paint()
    
    init {
        // 녹색 페인트
        greenPaint.color = Color.GREEN
        
        // 검은색 테두리 페인트
        /*
            - FILL : 색을 채움. 획 관련된 설정을 무시
            - FILL_AND_STROKE : 획과 관련된 설정을 유지하면서 책을 채움
            - STROKE : 화 관련 설정을 유지하여 외관선만 그림
         */
        blackPaint.style = Paint.Style.STROKE
    }
    ....
}

수평계 그래픽 구상, 센서 값을 뷰에 반영하기

package abstractask.example.tiltsensor

import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.view.View

class TiltView(context: Context?) : View(context) {

    private val greenPaint: Paint = Paint()
    private val blackPaint: Paint = Paint()

    private var cX: Float = 0f
    private var cY: Float = 0f
    
    //
    private var xCoord: Float = 0f
    private var yCoord: Float = 0f

    init {
        // 녹색 페인트
        greenPaint.color = Color.GREEN

        // 검은색 테두리 페인트
        /*
            - FILL : 색을 채움. 획 관련된 설정을 무시
            - FILL_AND_STROKE : 획과 관련된 설정을 유지하면서 책을 채움
            - STROKE : 화 관련 설정을 유지하여 외관선만 그림
         */
        blackPaint.style = Paint.Style.STROKE

    }

    // 뷰의 크기가 변경될 때마다 호출
    /*
        - w : 변경된 가로 길이
        - h : 변경된 세로 길이
        - oldw : 변경 전 가로 길이
        - oldh : 변경 전 세로 길이
     */
    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        cX = w / 2f
        cY = h / 2f
    }

    override fun onDraw(canvas: Canvas?) {
        // 바깥 원
        canvas?.drawCircle(cX, cY, 100f, blackPaint)

        // 녹색 원
        canvas?.drawCircle(xCoord + cX, yCoord + cY, 100f, greenPaint)

        // 가운데 십자가
        canvas?.drawLine(cX - 20, cY, cX + 20, cY, blackPaint)
        canvas?.drawLine( cX, cY - 20 , cX, cY + 20, blackPaint)
    }
    
    // MainActivity 에서 호출하여 View에 반영되도록 함수 작성
    // onSensorChanged 함수에서 호출됨
    fun onSensorEvent( event: SensorEvent) {
        // 화면을 가로로 돌렸으므로 X축과 Y축을 서로 바꿈
        xCoord = event.values[0] * 20
        yCoord = event.values[1] * 20
        invalidate()
    }
}

MainActivity.onSensorChanged 에서 TiltView.onSensorEvent 호출

// 자신을 SensorEventListener 인터페이스의 구현체로 등록
class MainActivity : AppCompatActivity(), SensorEventListener {

    // 1. 지연된 초기화를 사용하여 sensorManager 객체를 얻는다.
....
    override fun onCreate(savedInstanceState: Bundle?) {
        // 화면이 꺼지지 않게 하기
        ....
        // 화면이 가로모드로 고정되게 하기       
        ....
        // 커스텀 뷰 사용하기
        ....
    }
....
    // 2. registerListener로 사용할 센서를 등록한다.
....

    // 3. unregisterListener로 센서 사용을 해제한다.
....

    // SensorEventListener 인터페이스에서 구현해야 할 메서드
    // 센서 값이 변경 되면 호출
    override fun onSensorChanged(event: SensorEvent?) {
        /*
            SensorEvent.values[0] : x 축 값 - 위로 기울이면 -10 ~ 0, 아래로 기울이면 0 ~ 10
            SensorEvent.values[1] : y 축 값 - 왼쪽으로 기울이면 -10 ~ 0, 오른쪽으로 기울이면 0 ~ 10
            SensorEvent.values[2] : z 축 값 - 미사용
         */
        event?.let {
            ....          
            tiltView.onSensorEvent(event)
        }
    }
    
    // 센서 정밀도가 변경되면 호출
....
}

결과

' > 오준석의 안드로이드 생존코딩 코틀린편' 카테고리의 다른 글

11. 손전등  (0) 2020.06.23
10. 지도와 GPS  (0) 2020.06.21
7. 나만의 웹 브라우저  (0) 2020.06.15
6. 스톱워치  (0) 2020.06.11
5. 비만도 계산기  (0) 2020.06.09