본문 바로가기

안드로이드/레이아웃 구현과 활용

[Android] 룰렛 구현하기

반응형

최근에 [점메추] 앱에서 메뉴를 추천하기 위한 방식중 하나로 개발을 했는데 아쉽게 실 버전엔 포함되지 못했습니다.

 

결과 화면 

 

개발 방식

라이브러리도 있지만 커스텀하기 용이한 점과 개발 해보고 싶었던 마음이 있어 캔버스에 직접 그리는 방식으로 했습니다.

Roulette.kt

룰렛을 한 화면에서만 사용하지만 관리가 용이해 커스텀 뷰로 만들었습니다.

    @SuppressLint("DrawAllocation")
    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        if (canvas == null) return

        val rectLeft = left + paddingLeft + Constant.DEFAULT_PADDING
        val rectRight = right - paddingRight - Constant.DEFAULT_PADDING
        val rectTop = height / 2f - rectRight / 2f + paddingTop + Constant.DEFAULT_PADDING
        val rectBottom = height / 2f + rectRight / 2f - paddingRight - Constant.DEFAULT_PADDING

        val rectF = RectF(rectLeft, rectTop, rectRight, rectBottom)

        centerX = (rectF.left + rectF.right) / 2f
        centerY = (rectF.top + rectF.bottom) / 2f

        drawRoulette(canvas, rectF)

        if (centerPointVisibility == VISIBLE) {
            canvas.drawCircle(centerX, centerY, centerPointRadius, centerPointPaint)
        }
    }

 

rectF : 룰렛을 그려줄 공간 

centerX, centerY : 룰렛 가운데 지점 좌표

 

fun drawRouleet

    private fun drawRoulette(canvas: Canvas, rectF: RectF) {

        if (rouletteDataList.size in 2..8) {
            val sweepAngle = 360f / rouletteDataList.size.toFloat()
            val radius = (rectF.right - rectF.left) / 2 * 0.5

            for (i in rouletteDataList.indices) {
                fillPaint.color = ContextCompat.getColor(context,shapeColors[i])

                // draw roulette arc
                val startAngle = if (i == 0) 0f else sweepAngle * i
                canvas.drawArc(rectF, startAngle, sweepAngle, true, fillPaint)

                // draw roulette text
                val medianAngle = ((startAngle + sweepAngle / 2f) * Math.PI / 180f )
                val x = (centerX + (radius * cos(medianAngle))).toFloat()
                val y = (centerY + (radius * sin(medianAngle))).toFloat() + Constant.DEFAULT_PADDING
                val text = rouletteDataList[i]
                canvas.drawText(text, x, y, textPaint)
            }
        } else {
            throw IndexOutOfBoundsException("size out of roulette")
        }
    }
  • rouletteDataList : 룰렛에 표현된 메뉴 리스트
  • sweepAngle : 원에서 한 메뉴들이 차지 하는 각도  

 

위의 코드로 앞서 보여드린 룰렛 사진처럼 각 영역이 나뉘어지게 됩니다.  영역마다 글씨가 써집니다.

 

회전 함수 

애니메이션을 활용해 구현하는게 제일 자연스러웠습니다. 

    /**
     * 룰렛 회전 함수
     * @param toDegrees : 종료 각도(시작 각도 : 0)
     * */
    private fun rotateRoulette(toDegrees: Float) {
        val animListener = object : Animation.AnimationListener {
            override fun onAnimationRepeat(animation: Animation?) {}

            override fun onAnimationStart(animation: Animation?) {
                rotateListener?.onRotateStart()
            }

            override fun onAnimationEnd(animation: Animation?) {
                rotateListener?.onRotateEnd(getRouletteRotateResult(toDegrees))
            }
        }

        val rotateAnim = RotateAnimation(
            0f, toDegrees,
            Animation.RELATIVE_TO_SELF, 0.5f,
            Animation.RELATIVE_TO_SELF, 0.5f
        )
        rotateAnim.duration = rotateDuration
        rotateAnim.fillAfter = true
        rotateAnim.setAnimationListener(animListener)

        startAnimation(rotateAnim)
    }

결과 영상 

실제 앱엔 없지만 메뉴 추천 앱이 궁금하다면 링크를 클릭해주세요!

 

점메추 - Google Play 앱

뭐 먹을지 고민될땐 점메추!

play.google.com

 

 

 

반응형