본문 바로가기

소프트웨어/안드로이드

Android Compass(나침반)

불현득 생각이 나서 코틀린 연습 겸 샘플용으로 만들었습니다~

 

 

센서처리는 다음 블로그를 참고했어요.

http://swlock.blogspot.com/2017/10/android-compass.html

 

Android Compass (나침반)

안드로이드에서 나침반 만들기 1. 준비 한때 AR(증강 현실) 어플들이 유행하던 시절이 있었는데, 막상 그때는 관심이 없다가 지금에서야 정리하게 되네요. 시작하기전에 알아 두어야 하는 기본 개념이 3가지가 있습니다. Azimuth, Pit...

swlock.blogspot.com

 

 

MainActivity는 다음과 같이 되어있어요.

Listener를 별도로 빼고 interface로 연결해주면 좀 더 깔끔하게 작성할 수 있습니다.

class MainActivity : AppCompatActivity(), SeekBarToView, SensorToView{

    private val sensorImpl = SensorEventListenerImpl(this)
    private lateinit var sm: SensorManager

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        sm = getSystemService(Context.SENSOR_SERVICE) as SensorManager
    }

    override fun onResume() {
        super.onResume()
        sm.registerListener(sensorImpl, sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_NORMAL)
        sm.registerListener(sensorImpl, sm.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD), SensorManager.SENSOR_DELAY_NORMAL)
    }

    override fun onPause() {
        super.onPause()
        sm.unregisterListener(sensorImpl)
    }

    // from Listener
    override fun onDegreeChanged(degree: Float) {
        mainCanvas.degree = degree
    }
}

 

 

나침반 역할을 하는 View는 다음과 같이 CustomView로 만들어줬습니다.

간단한 삼각함수로 절대좌표를 구했어요. Canvas의 상대 사이즈로 만들어졌기 때문에 여기에서 더 코드 보완이 필요하겠네요.

private const val FIRST_RING_WIDTH = 10f

class Compass(ctx: Context, attr: AttributeSet?) : View(ctx, attr) {

    var degree: Float = 0.0f
        set(value) {
            // 90˚ offset
            field = value + 90f
            invalidate()
        }

    constructor(ctx: Context) : this(ctx, null)

    private val blackCirclePaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG)
    private val whiteCirclePaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG)
    private val indicatorPaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG)


    init {
        // Paint for black outer circle
        blackCirclePaint.color = Color.BLACK

        // Paint for white inner circle
        whiteCirclePaint.color = Color.WHITE

        // line indicator
        indicatorPaint.strokeWidth = 5f
        indicatorPaint.strokeCap = Paint.Cap.ROUND
    }

    override fun onDraw(canvas: Canvas?) {
        canvas?.let {
            myDraw(it)
        }
    }

    private fun myDraw(canvas: Canvas) {

        val halfHeight = canvas.height / 2f
        val radius = canvas.width / 2f
        canvas.drawCircle(radius, halfHeight, radius, blackCirclePaint)
        canvas.drawCircle(radius, halfHeight, radius - FIRST_RING_WIDTH, whiteCirclePaint)

        canvas.drawLine(radius, halfHeight, radius + calculateXWithDegree(radius, degree), halfHeight + calculateYWithDegree(radius, degree), indicatorPaint)
    }

    private fun calculateXWithDegree(r: Float, degree: Float): Float {
        // a/r = cosθ
        return r * Math.cos(Math.toRadians(degree.toDouble())).toFloat()
    }

    private fun calculateYWithDegree(r: Float, degree: Float): Float {
        // b/r = sinθ
        return r * Math.sin(Math.toRadians(degree.toDouble())).toFloat()
    }
}

 

 

샘플실행예제

 

 

전체 소스는 아래를 참고해주세요!

https://gitlab.com/iroiroys/basiccompass