본문 바로가기
안드로이드 코틀린

[Kotlin][Android] 수평 측정기 앱을 만들어 보자

by teamnova 2021. 7. 16.

안녕하세요 '~' /

 

오늘은 스틱 코드와 안드로이드 센서를 이용해서 아래와 같은 순서로 수평 측정기를 만들어 보겠습니다.

 

 

1. 수평을 측정하는 화면 구현 

2. 수평을 측정하는 기능 구현

3. 테스트

 

 

스틱코드?

https://stickode.com/mainlogin.html

 

STICKODE

 

stickode.com


 

1. 수평 측정 화면 구현 

▶ 수평 측정을 시각적으로 보여주는 뷰를 만들기 위해 TiltView.kt라는 코틀린 클래스 파일을 만들어줍니다.

TiltView.kt 생성

 

 

 

# TiltView.kt 전체 코드

package com.example.accelerometersensor

import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.hardware.SensorEvent
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()
    }

}

 


 

2. 수평 측정 기능 구현

▶ 위에서 만들어준 TiltView와 SensorManager를 선언해줍니다.

MainActivity.kt

 

 

 

 

▶ SensorEventListener 인터페이스를 추가해줍니다.

SensorEventListener 추가

 

 

 

▶ 센서의 변화량을 화면에 표시해주는 기능을 추가해줍니다.

센서 변화량 화면에 표시

 

 

 

 

onCreate에서 수평 측정 화면을 세팅해주고 측정 중 화면이 꺼지지 않도록 설정해 줍니다.

onCreate

 

 

 

onResume에서 스틱 코드를 사용해, 사용할 센서를 등록해 줍니다.

onResume 

 

 

 

 

onPause 에서는 센서 사용을 해제해 줍니다.

onPause  

 

 

 

# MainActivity.kt 전체 코드

package com.example.accelerometersensor

import android.content.Context
import android.content.pm.ActivityInfo
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.view.WindowManager


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

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

    // 수평을 시각적으로 보여주는 뷰
    private lateinit var tiltView: TiltView

    override fun onCreate(savedInstanceState: Bundle?) {

        // 화면이 꺼지지 않게 하기
        window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)

        // 화면이 가로모드로 고정되게 하기
        requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE

        super.onCreate(savedInstanceState)

        // 커스텀 뷰 사용하기
        tiltView = TiltView(this)
        setContentView(tiltView)

    }


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


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


    // 센서 값이 변경 되면 호출
    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]}" )

            tiltView.onSensorEvent(event)
        }
    }

    // 센서 정밀도가 변경되면 호출
    override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {

    }
}

 


 

3. 테스트

 

▶ 수평 상태

수평

 

 

▶ 수평이 틀어진 상태

수평이 틀어진 상태