[Android/Flutter 교육] 39일차

MSU·2024년 2월 22일

Android-Flutter

목록 보기
41/85
post-thumbnail

이미지뷰의 속성 중
adjustViewBounds의 값을 true로 해주면 화면 꽉차게 채울 수 있다.

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:adjustViewBounds="true"
        app:srcCompat="@mipmap/ic_launcher" />

단말기 정보

  • 안드로이드 애플리케이션에서 단말기의 정보가 필요할 경우 정보를 가져다 사용할 수 있다.
  • 안드로이드 8.0부터 단말기 일련번호, 유심 일련번호 등 단말기의 고유한 정보는 사용할 수 없다.

AndroidManifest.xml에 권한 추가

    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
    <uses-permission android:name="android.permission.READ_SMS"/>
    <uses-permission android:name="android.permission.READ_PHONE_NUMBERS"/>
    <uses-feature
        android:name="android.hardware.telephony"
        android:required="false" />
class MainActivity : AppCompatActivity() {

    lateinit var activityMainBinding: ActivityMainBinding

    val permissionList = arrayOf(
        Manifest.permission.READ_PHONE_STATE,
        Manifest.permission.READ_SMS,
        Manifest.permission.READ_PHONE_NUMBERS,
    )

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(activityMainBinding.root)


        requestPermissions(permissionList,0)

        // 전화 관련된 정보를 관리하는 객체를 추출한다.
        val telephonyManager = getSystemService(TELEPHONY_SERVICE) as TelephonyManager

        // 권한 확인이 필요한 일부 코드들은 권한 허용 여부를 검사해줘야 하는 것들이 있다.
        // checkSelfPermission : 두 번째 매개변수에 넣어준 권한이 허용되어 있는지
        // PERMISSION_GRANTED : 허용된 권한
        // PERMISSION_DENIED : 거부된 권한
        val a1 = ActivityCompat.checkSelfPermission(this@MainActivity, Manifest.permission.READ_SMS) == PackageManager.PERMISSION_GRANTED
        val a2 = ActivityCompat.checkSelfPermission(this@MainActivity, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED
        val a3 = ActivityCompat.checkSelfPermission(this@MainActivity, Manifest.permission.READ_PHONE_NUMBERS) == PackageManager.PERMISSION_GRANTED

        // 권한이 모두 허용되어 있다면
        if(a1 && a2 && a3){
            activityMainBinding.apply {
                textView.apply {
                    text = "전화번호 : ${telephonyManager.line1Number}\n"
                    append("SIM 국가 코드 : ${telephonyManager.simCountryIso}\n")
                    append("모바일 국가 코드 + 모바일 네트워크 코드 : ${telephonyManager.simOperator}\n") // 통신사에 따라 가능한 기능을 구현할때 체크
                    append("서비스 이름 : ${telephonyManager.simOperatorName}\n")
                    append("SIM 상태 (통신 가능 여부, Pin Lock 여부 등) : ${telephonyManager.simState}\n")
                    append("음성 메일 번호 : ${telephonyManager.voiceMailNumber}\n")
                }
            }
        }



    }
}

음성 메일 번호는 우리나라에는 없다

핸드폰 추가 정보

        // 권한이 모두 허용되어 있다면
        if(a1 && a2 && a3){
            activityMainBinding.apply {
                textView.apply {
                    text = "전화번호 : ${telephonyManager.line1Number}\n"
                    append("SIM 국가 코드 : ${telephonyManager.simCountryIso}\n")
                    append("모바일 국가 코드 + 모바일 네트워크 코드 : ${telephonyManager.simOperator}\n") // 통신사에 따라 가능한 기능을 구현할때 체크
                    append("서비스 이름 : ${telephonyManager.simOperatorName}\n")
                    append("SIM 상태 (통신 가능 여부, Pin Lock 여부 등) : ${telephonyManager.simState}\n")
                    append("음성 메일 번호 : ${telephonyManager.voiceMailNumber}\n")

                    append("보드 이름 : ${Build.BOARD}\n")
                    append("소프트웨어를 커스터마이징한 회사 : ${Build.BRAND}\n")
                    append("제조사 디자인 명 : ${Build.DEVICE}\n")
                    append("사용자에게 표시되는 빌드 ID : ${Build.DISPLAY}\n")
                    append("빌드 고유 ID : ${Build.FINGERPRINT}\n")
                    append("ChangeList 번호 : ${Build.ID}\n")
                    append("제품/하드웨어 제조업체 : ${Build.MANUFACTURER}\n")
                    append("제품 모델명 : ${Build.MODEL}\n")
                    append("제품명 : ${Build.PRODUCT}\n")
                    append("빌드 구분 : ${Build.TAGS}\n")
                    append("빌드 타입 : ${Build.TYPE}\n")
                    append("안드로이드 버전 : ${Build.VERSION.RELEASE}\n")
                    append("안드로이드 버전 코드네임 : ${Build.VERSION.CODENAME}\n")
                    append("안드로이드 API 레벨 : ${Build.VERSION.SDK_INT}\n")
                }
            }
        }

화면 관련 정보

                    // 화면에 관련된 정보를 가지고 있는 객체를 추출한다.
                    val windowManager = getSystemService(WINDOW_SERVICE) as WindowManager

                    // 안드로이드 11 이상 여부로 분기한다.
                    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.R){
                        // 단말기 해상도의 가로길이를 가져온다.
                        val w = windowManager.currentWindowMetrics.bounds.width()
                        // 단말기 해상도의 세로길이를 가져온다.
                        val h = windowManager.currentWindowMetrics.bounds.height()

                        textView.append("가로길이 : ${w}\n")
                        textView.append("세로길이 : ${w}\n")
                    } else {
                        // 해상도 정보를 담을 객체를 추출한다.
                        val point = Point()
                        // 해상도 정보를 담는다.
                        windowManager.defaultDisplay.getSize(point)
                        
                        textView.append("가로길이 : ${point.x}\n")
                        textView.append("세로길이 : ${point.y}\n")
                    }

안드로이드 센서

  • 스마트폰이 부팅되면 단말기에 장착되어 있는 모든 센서들이 동작하기 시작하고 애플리케이션에서는 이 값을 받아와 사용할 수 있다.

센서 종류 확인

class MainActivity : AppCompatActivity() {

    lateinit var activityMainBinding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(activityMainBinding.root)

        activityMainBinding.apply {
            textView.apply {
                // 센서를 관리하는 객체를 추출한다.
                val sensorManager = getSystemService(SENSOR_SERVICE) as SensorManager
                // 단말기에 있는 센서 목록을 가져온다.
                val sensorList = sensorManager.getSensorList(Sensor.TYPE_ALL)

                text = ""

                sensorList.forEach {
                    append("센서 이름 : ${it.name}\n")
                    append("센서 제조사 : ${it.vendor}\n")
                    append("센서 종류 : ${it.type}\n\n")
                }
            }
        }
    }
}

센서 당 받아올 수 있는 최대 값이 3개임(자이로 스코프 센서는 x,y,z축 값)

에뮬레이터 창의 메뉴 중 ... 버튼 선택하면 디바이스의 센서를 확인할 수 있다.

조도 센서

  • 주변 밝기를 측정하는 센서
  • 주변 밝기를 측정하여 화면 밝기를 조절하는 등의 용도로 사용한다.
class MainActivity : AppCompatActivity() {

    lateinit var activityMainBinding: ActivityMainBinding

    // 조도 센서의 리스너를 담을 프로퍼티
    var lightSensorListener:LightSensorListener? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(activityMainBinding.root)

        activityMainBinding.apply {
            button.setOnClickListener {
                // 리스너 연결이 되어 있지 않을 경우
                if(lightSensorListener == null){
                    button.text = "조도 센서 해제"
                    // 리스너 객체를 생성
                    lightSensorListener = LightSensorListener()
                    // 센서들을 관리하는 객체를 추출한다.
                    val sensorManager = getSystemService(SENSOR_SERVICE) as SensorManager
                    // 조도 센서 객체를 가져온다.
                    val sensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT)
                    // 조도 센서와 리스너를 연결한다.
                    // 첫 번째 : 리스너
                    // 두 번째 : 연결할 센서 객체
                    // 세 번째 : 데이터를 받아올 주기
                    // 반환값 : 센서 연결에 성공했는지 여부
                    val chk = sensorManager.registerListener(lightSensorListener, sensor, SensorManager.SENSOR_DELAY_UI)
                    // 센서 연결에 실패했다면
                    if(chk==false){
                        lightSensorListener = null
                        textView.text = "조도 센서를 지원하지 않습니다."
                    }
                }
                // 리스너가 연결되어 있을 경우
                else {
                    button.text = "조도 센서 연결"
                    // 센서들을 관리하는 객체를 추출한다.
                    val sensorManager = getSystemService(SENSOR_SERVICE) as SensorManager
                    // 조도 센서의 리스너를 해제한다.
                    sensorManager.unregisterListener(lightSensorListener)

                    lightSensorListener = null
                }
            }
        }
    }

    // 조도 센서의 리스너
    inner class LightSensorListener : SensorEventListener{
        // 센서에 변화가 일어날 때 호출되는 메서드
        // 이 메서드에서 측정된 값을 가져올 수 있다.
        override fun onSensorChanged(event: SensorEvent?) {
            if(event != null) {
                // 조도 값을 가지고 온다.
                val a1 = event.values[0]
                activityMainBinding.textView.text = "주변 밝기 : $a1 lux"
            }
        }

        // 센서의 감도가 변경되었을 때 호출되는 메서드
        override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {

        }
    }

}

조도 센서를 조작하면 텍스트뷰값도 같이 바뀌는걸 확인할 수 있다.

실제 단말기에서 테스트를 할때에는 단말기 상단을 가려주면 된다.

기압 센서

  • 공기압을 측정하는 센서
  • 사용자가 움직일 때 공기압의 변화 값을 계산하여 경사도 계산이나 건물의 층 수를 계산하는 용도로 사용한다.
  • 실내, 실외, 지상, 지하를 구분할 때 사용(지도의 지하철 지하상가에 있을 때)
class MainActivity : AppCompatActivity() {

    lateinit var activityMainBinding: ActivityMainBinding

    // 기압 센서의 리스너를 담을 프로퍼티
    var pressureSensorListener:PressureSensorListener? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(activityMainBinding.root)

        activityMainBinding.apply {

            button2.setOnClickListener {
                // 리스너 연결이 되어 있지 않을 경우
                if(pressureSensorListener == null){
                    button2.text = "기압 센서 해제"
                    // 리스너 객체를 생성
                    pressureSensorListener = PressureSensorListener()
                    // 센서들을 관리하는 객체를 추출한다.
                    val sensorManager = getSystemService(SENSOR_SERVICE) as SensorManager
                    // 기압 센서 객체를 가져온다.
                    val sensor = sensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE)
                    // 기압 센서와 리스너를 연결한다.
                    // 첫 번째 : 리스너
                    // 두 번째 : 연결할 센서 객체
                    // 세 번째 : 데이터를 받아올 주기
                    // 반환값 : 센서 연결에 성공했는지 여부
                    val chk = sensorManager.registerListener(pressureSensorListener, sensor, SensorManager.SENSOR_DELAY_UI)
                    // 센서 연결에 실패했다면
                    if(chk==false){
                        pressureSensorListener = null
                        textView.text = "기압 센서를 지원하지 않습니다."
                    }
                }
                // 리스너가 연결되어 있을 경우
                else {
                    button2.text = "기압 센서 연결"
                    // 센서들을 관리하는 객체를 추출한다.
                    val sensorManager = getSystemService(SENSOR_SERVICE) as SensorManager
                    // 기압 센서의 리스너를 해제한다.
                    sensorManager.unregisterListener(pressureSensorListener)

                    pressureSensorListener = null
                }
            }
        }
    }


    // 공기압 센서의 리스너
    inner class PressureSensorListener : SensorEventListener {
        // 센서에 변화가 일어날 때 호출되는 메서드
        // 이 메서드에서 측정된 값을 가져올 수 있다.
        override fun onSensorChanged(event: SensorEvent?) {
            if(event != null) {
                // 공기압 값을 가지고 온다.
                val a1 = event.values[0]
                activityMainBinding.textView.text = "현재 기압 : $a1 millibar"
            }
        }

        // 센서의 감도가 변경되었을 때 호출되는 메서드
        override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {

        }
    }

}

근접 센서

  • 단말기와 물체 간의 거리를 계산할 수 있는 센서이다.
  • 보통 단말기 상단 전면(전면카메라쪽)에 장착되어 있어 cm단위로 측정할 수 있다.
  • 실제로는 cm 단위의 거리 측정은 안되고 물체가 근접했는가 근접하지 않았는가만 측정할 수 있다.
    핸드폰 뒤집을 때 측정하는 센서는 근접 센서가 아니라 가속도 센서임
class MainActivity : AppCompatActivity() {

    lateinit var activityMainBinding: ActivityMainBinding

    // 근접 센서의 리스너를 담을 프로퍼티
    var proximitySensorListener:ProximitySensorListener? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(activityMainBinding.root)

        activityMainBinding.apply {

            button3.setOnClickListener {
                // 리스너 연결이 되어 있지 않을 경우
                if(proximitySensorListener == null){
                    button3.text = "근접 센서 해제"
                    // 리스너 객체를 생성
                    proximitySensorListener = ProximitySensorListener()
                    // 센서들을 관리하는 객체를 추출한다.
                    val sensorManager = getSystemService(SENSOR_SERVICE) as SensorManager
                    // 근접 센서 객체를 가져온다.
                    val sensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY)
                    // 근접 센서와 리스너를 연결한다.
                    // 첫 번째 : 리스너
                    // 두 번째 : 연결할 센서 객체
                    // 세 번째 : 데이터를 받아올 주기
                    // 반환값 : 센서 연결에 성공했는지 여부
                    val chk = sensorManager.registerListener(proximitySensorListener, sensor, SensorManager.SENSOR_DELAY_UI)
                    // 센서 연결에 실패했다면
                    if(chk==false){
                        proximitySensorListener = null
                        textView.text = "근접 센서를 지원하지 않습니다."
                    }
                }
                // 리스너가 연결되어 있을 경우
                else {
                    button3.text = "근접 센서 연결"
                    // 센서들을 관리하는 객체를 추출한다.
                    val sensorManager = getSystemService(SENSOR_SERVICE) as SensorManager
                    // 근접 센서의 리스너를 해제한다.
                    sensorManager.unregisterListener(proximitySensorListener)

                    proximitySensorListener = null
                }
            }

        }
    }


    // 근접 센서의 리스너
    inner class ProximitySensorListener : SensorEventListener {
        // 센서에 변화가 일어날 때 호출되는 메서드
        // 이 메서드에서 측정된 값을 가져올 수 있다.
        override fun onSensorChanged(event: SensorEvent?) {
            if(event != null) {
                // 물체와의 거리를 가지고 온다.
                val a1 = event.values[0]
                activityMainBinding.textView.text = "물체와의 거리 : $a1 cm"
            }
        }

        // 센서의 감도가 변경되었을 때 호출되는 메서드
        override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {

        }
    }

}

자이로 스코프 센서

  • 단위 시간당 회전한 각도 값을 측정하는 센서
    대륙을 탐사하던 시절 배가 기울어짐에 따라 선반 등의 물체를 반대 방향으로 기울이게 하여 수평을 유지하도록 고안된 장치
    현대에는 이 장치의 원리를 이용해 항공기, 선박 등에서 계기판이나 수평 유지 장치에 널리 사용되고 있다.
class MainActivity : AppCompatActivity() {

    lateinit var activityMainBinding: ActivityMainBinding

    // 자이로 스코프 센서의 리스너를 담을 프로퍼티
    var gyroscopeSensorListener:GyroscopeSensorListener? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(activityMainBinding.root)

        activityMainBinding.apply {
        
            button4.setOnClickListener {
                // 리스너 연결이 되어 있지 않을 경우
                if(gyroscopeSensorListener == null){
                    button4.text = "자이로 스코프 센서 해제"
                    // 리스너 객체를 생성
                    gyroscopeSensorListener = GyroscopeSensorListener()
                    // 센서들을 관리하는 객체를 추출한다.
                    val sensorManager = getSystemService(SENSOR_SERVICE) as SensorManager
                    // 자이로 스코프 센서 객체를 가져온다.
                    val sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE)
                    // 자이로 스코프 센서와 리스너를 연결한다.
                    // 첫 번째 : 리스너
                    // 두 번째 : 연결할 센서 객체
                    // 세 번째 : 데이터를 받아올 주기
                    // 반환값 : 센서 연결에 성공했는지 여부
                    val chk = sensorManager.registerListener(gyroscopeSensorListener, sensor, SensorManager.SENSOR_DELAY_UI)
                    // 센서 연결에 실패했다면
                    if(chk==false){
                        gyroscopeSensorListener = null
                        textView.text = "자이로 스코프 센서를 지원하지 않습니다."
                    }
                }
                // 리스너가 연결되어 있을 경우
                else {
                    button4.text = "자이로 스코프 센서 연결"
                    // 센서들을 관리하는 객체를 추출한다.
                    val sensorManager = getSystemService(SENSOR_SERVICE) as SensorManager
                    // 자이로 스코프 센서의 리스너를 해제한다.
                    sensorManager.unregisterListener(gyroscopeSensorListener)

                    gyroscopeSensorListener = null
                }
            }
            
        }
    }
    
    // 자이로 스코프 센서의 리스너
    inner class GyroscopeSensorListener : SensorEventListener{
        // 센서에 변화가 일어날 때..
        // 이 메서드에서 측정된 값을 가져올 수 있다
        override fun onSensorChanged(event: SensorEvent?) {
            if(event != null) {
                // X 축의 각속도
                val a1 = event.values[0]
                // Y 축의 각속도
                val a2 = event.values[1]
                // Z 축의 각속도
                val a3 = event.values[2]

                activityMainBinding.textView.text = "X 축의 각속도 : $a1"
                activityMainBinding.textView2.text = "Y 축의 각속도 : $a2"
                activityMainBinding.textView3.text = "Z 축의 각속도 : $a3"
            }
        }
        // 센서의 감도가 변경되었을 때...
        override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {

        }
    }

}

Z-Rot, X-Rot, Y-Rot 값을 조정할 수 있다.

자이로 스코프는 얼마나 기울여졌냐가 아니라 얼마나 빠르게 움직여졌는지를 측정한다.
천천히 움직일때와 빠르게 움직일때의 측정값이 다르다.
움직인 후에는 값이 0으로 돌아온다.

가속도 센서

class MainActivity : AppCompatActivity() {

    lateinit var activityMainBinding: ActivityMainBinding

    // 가속도 센서의 리스너를 담을 프로퍼티
    var accelerometerSensorListener:AccelerometerSensorListener? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(activityMainBinding.root)

        activityMainBinding.apply {
        
            button5.setOnClickListener {
                // 리스너 연결이 되어 있지 않을 경우
                if(accelerometerSensorListener == null){
                    button5.text = "가속도 센서 해제"
                    // 리스너 객체를 생성
                    accelerometerSensorListener = AccelerometerSensorListener()
                    // 센서들을 관리하는 객체를 추출한다.
                    val sensorManager = getSystemService(SENSOR_SERVICE) as SensorManager
                    // 가속도 센서 객체를 가져온다.
                    val sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
                    // 가속도 센서와 리스너를 연결한다.
                    // 첫 번째 : 리스너
                    // 두 번째 : 연결할 센서 객체
                    // 세 번째 : 데이터를 받아올 주기
                    // 반환값 : 센서 연결에 성공했는지 여부
                    val chk = sensorManager.registerListener(accelerometerSensorListener, sensor, SensorManager.SENSOR_DELAY_UI)
                    // 센서 연결에 실패했다면
                    if(chk==false){
                        accelerometerSensorListener = null
                        textView.text = "가속도 센서를 지원하지 않습니다."
                    }
                }
                // 리스너가 연결되어 있을 경우
                else {
                    button5.text = "가속도 센서 연결"
                    // 센서들을 관리하는 객체를 추출한다.
                    val sensorManager = getSystemService(SENSOR_SERVICE) as SensorManager
                    // 가속도 센서의 리스너를 해제한다.
                    sensorManager.unregisterListener(accelerometerSensorListener)

                    accelerometerSensorListener = null
                }
            }
            
        }
    }
    
    // 가속도 센서의 리스너
    inner class AccelerometerSensorListener : SensorEventListener{
        // 센서에 변화가 일어날 때..
        // 이 메서드에서 측정된 값을 가져올 수 있다
        override fun onSensorChanged(event: SensorEvent?) {
            if(event != null) {
                // X 축의 기울기
                val a1 = event.values[0]
                // Y 축의 기울기
                val a2 = event.values[1]
                // Z 축의 기울기
                val a3 = event.values[2]

                activityMainBinding.textView.text = "X 축의 기울기 : $a1"
                activityMainBinding.textView2.text = "Y 축의 기울기 : $a2"
                activityMainBinding.textView3.text = "Z 축의 기울기 : $a3"
            }
        }
        // 센서의 감도가 변경되었을 때...
        override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {

        }
    }

}

빠르기(자이로 스코프)와 상관없이 기울기에따라 값이 변한다.

마그네틱 필드

  • 진북(지구의 자전축이 지나는 진짜 북극)이 아닌 자북(자기장, 나침반 기준 북극)을 측정함
  • 단말기 주변의 자기장 값을 읽어온다
  • 지구에 흐르는 자기장 값을 읽어 방향을 측정하는 용도로 사용한다.
  • 단말기 기울어짐에 따라 값의 오차가 발생할 수 있어 실제 방위 측정에는 가속도 센서와 같이 사용함
  • 단말기에 맥세이프같은 자성을 띈 물체가 같이 있으면 간섭을 받게됨
class MainActivity : AppCompatActivity() {

    lateinit var activityMainBinding: ActivityMainBinding

    // 마그네틱 센서의 리스너를 담을 프로퍼티
    var magneticSensorListener:MagneticSensorListener? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(activityMainBinding.root)

        activityMainBinding.apply {
        
            button6.setOnClickListener {
                // 리스너 연결이 되어 있지 않을 경우
                if(magneticSensorListener == null){
                    button6.text = "마그네틱 센서 해제"
                    // 리스너 객체를 생성
                    magneticSensorListener = MagneticSensorListener()
                    // 센서들을 관리하는 객체를 추출한다.
                    val sensorManager = getSystemService(SENSOR_SERVICE) as SensorManager
                    // 마그네틱 센서 객체를 가져온다.
                    val sensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD)
                    // 마그네틱 센서와 리스너를 연결한다.
                    // 첫 번째 : 리스너
                    // 두 번째 : 연결할 센서 객체
                    // 세 번째 : 데이터를 받아올 주기
                    // 반환값 : 센서 연결에 성공했는지 여부
                    val chk = sensorManager.registerListener(magneticSensorListener, sensor, SensorManager.SENSOR_DELAY_UI)
                    // 센서 연결에 실패했다면
                    if(chk==false){
                        magneticSensorListener = null
                        textView.text = "마그네틱 센서를 지원하지 않습니다."
                    }
                }
                // 리스너가 연결되어 있을 경우
                else {
                    button6.text = "마그네틱 센서 연결"
                    // 센서들을 관리하는 객체를 추출한다.
                    val sensorManager = getSystemService(SENSOR_SERVICE) as SensorManager
                    // 마그네틱 센서의 리스너를 해제한다.
                    sensorManager.unregisterListener(magneticSensorListener)

                    magneticSensorListener = null
                }
            }
            
        }
    }
    
    // 마그네틱 센서의 리스너
    inner class MagneticSensorListener : SensorEventListener{
        // 센서에 변화가 일어날 때..
        // 이 메서드에서 측정된 값을 가져올 수 있다
        override fun onSensorChanged(event: SensorEvent?) {
            if(event != null) {
                // X 축 주변 자기장
                val a1 = event.values[0]
                // Y 축 주변 자기장
                val a2 = event.values[1]
                // Z 축 주변 자기장
                val a3 = event.values[2]

                activityMainBinding.textView.text = "X 축의 주변 자기장 : $a1"
                activityMainBinding.textView2.text = "Y 축의 주변 자기장 : $a2"
                activityMainBinding.textView3.text = "Z 축의 주변 자기장 : $a3"
            }
        }
        // 센서의 감도가 변경되었을 때...
        override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {

        }
    }

}

방위값 측정하기

  • 마그네틱 필드의 첫 번째 값으로 방위 측정이 가능하나 단말기의 기울어짐에 따라 오차가 발생한다.
  • 마그네틱 필드와 가속도 센서를 이용하여 방위를 측정하면 오차를 수정할 수 있다.
  • 방위는 자북을 0으로 하여 0~359.99999까지 값이 측정된다.

가속도, 자기장 센서로 측정할 값을 담을 배열을 프로퍼티로 선언

    // 가속도 센서로 측정한 값을 담을 배열을 담을 프로퍼티
    val accValues = floatArrayOf(0.0f, 0.0f, 0.0f)
    // 자기장 센서로 측정한 값을 담을 배열을 담을 프로퍼티
    val magValues = floatArrayOf(0.0f, 0.0f, 0.0f)

센서 측정 여부를 체크할 변수

    // 센서로부터 값이 측정된 적이 있는지
    var isGetAcc = false
    var isGetMag = false

각 센서의 리스너 클래스 작성

    // 가속도 센서의 리스너
    inner class AccelerometerSensorListener : SensorEventListener{
        override fun onSensorChanged(event: SensorEvent?) {
            if(event != null){
                // 측정된 값이 담긴 배열을 담아준다.
                accValues[0] = event.values[0]
                accValues[1] = event.values[1]
                accValues[2] = event.values[2]

                isGetAcc = true
            }
        }

        override fun onAccuracyChanged(p0: Sensor?, p1: Int) {
            TODO("Not yet implemented")
        }

    }

    // 자기장 센서의 리스너
    inner class MagneticSensorListener : SensorEventListener{
        override fun onSensorChanged(event: SensorEvent?) {
            if(event != null){
                // 측정된 값이 담긴 배열을 담아준다.
                magValues[0] = event.values[0]
                magValues[1] = event.values[1]
                magValues[2] = event.values[2]

                isGetMag = true
            }
        }

        override fun onAccuracyChanged(p0: Sensor?, p1: Int) {
            TODO("Not yet implemented")
        }

    }

방위값을 계산하는 메서드

이전에는 방위값을 계산하는 가이드만 알려주었지만 지금은 메서드도 제공하고 있다.

    // 방위값을 계산하는 메서드
    fun getAzimuth(){
        // 두 센서 모두 값이 측정된 적이 있을 경우에만
        if(isGetAcc && isGetMag){
            // 방위값 등을 계싼하기 위한 계산 행렬
            val R = FloatArray(9)
            val I = FloatArray(9)
            // 계산 행렬을 구한다.
            SensorManager.getRotationMatrix(R, I, accValues, magValues)
            // 방위값을 추출한다.
            val values = FloatArray(3)
            SensorManager.getOrientation(R, values)

            // 결과가 라디언 값으로 나오기 때문에 각도 값으로 변환한다.
            var azimuth = Math.toDegrees(values[0].toDouble()) // 방위
            var pitch = Math.toDegrees(values[1].toDouble()) // 좌우 기울기 값
            var roll = Math.toDegrees(values[2].toDouble()) // 앞뒤 기울기 값

            // 만약 방위값이 음수가 나온다면 360을 더해준다.
            if(azimuth<0){
                azimuth += 360
            }

            activityMainBinding.apply {
                textView.text = "방위값 : $azimuth"
                textView2.text = "좌우 기울기 값 : $pitch"
                textView3.text = "앞뒤 기울기 값 : $roll"

                // 이미지 뷰 회전
                imageView.rotation = (360 - azimuth).toFloat()
            }
        }
    }

두개의 센서 리스너에 작성한 방위값 계산 메서드를 호출

    // 가속도 센서의 리스너
    inner class AccelerometerSensorListener : SensorEventListener{
        override fun onSensorChanged(event: SensorEvent?) {
            if(event != null){
                // 측정된 값이 담긴 배열을 담아준다.
                accValues[0] = event.values[0]
                accValues[1] = event.values[1]
                accValues[2] = event.values[2]

                isGetAcc = true

                getAzimuth()
            }
        }

        override fun onAccuracyChanged(p0: Sensor?, p1: Int) {
        }

    }

    // 자기장 센서의 리스너
    inner class MagneticSensorListener : SensorEventListener{
        override fun onSensorChanged(event: SensorEvent?) {
            if(event != null){
                // 측정된 값이 담긴 배열을 담아준다.
                magValues[0] = event.values[0]
                magValues[1] = event.values[1]
                magValues[2] = event.values[2]

                isGetMag = true

                getAzimuth()
            }
        }

        override fun onAccuracyChanged(p0: Sensor?, p1: Int) {
        }

    }

각 센서들의 리스너를 셋팅

    // 각 센서들의 리스너
    var accelerometerSensorListener:AccelerometerSensorListener? = null
    var magneticSensorListener:MagneticSensorListener? = null
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(activityMainBinding.root)
        
        activityMainBinding.apply {
            button.setOnClickListener {
                if(accelerometerSensorListener == null && magneticSensorListener == null){
                    // 센서 관리 객체를 추출한다.
                    val sensorManager = getSystemService(SENSOR_SERVICE) as SensorManager
                    // 센서 객체를 가져온다.
                    val accSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
                    val magSensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD)
                    // 리스너 생성
                    accelerometerSensorListener = AccelerometerSensorListener()
                    magneticSensorListener = MagneticSensorListener()
                    // 센서에 리스너를 연결한다.
                    sensorManager.registerListener(accelerometerSensorListener, accSensor, SensorManager.SENSOR_DELAY_UI)
                    sensorManager.registerListener(magneticSensorListener, magSensor, SensorManager.SENSOR_DELAY_UI)
                }
            }

            button2.setOnClickListener {
                if(accelerometerSensorListener != null && magneticSensorListener != null){
                    val sensorManager = getSystemService(SENSOR_SERVICE) as SensorManager

                    // 센서 리스너를 해제한다.
                    sensorManager.unregisterListener(accelerometerSensorListener)
                    sensorManager.unregisterListener(magneticSensorListener)

                    accelerometerSensorListener = null
                    magneticSensorListener = null
                }
            }
        }
        
    }




※ 출처 : 멋쟁이사자 앱스쿨 2기, 소프트캠퍼스 
profile
안드로이드공부

0개의 댓글